blob: 482599478b80038a22b6dbbc9845fdbfd4408a98 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass11cd6312016-03-11 22:07:13 -07002/*
3 * Copyright (C) 2016 Google, Inc
Simon Glass11cd6312016-03-11 22:07:13 -07004 */
5
6#include <common.h>
7#include <dm.h>
8#include <errno.h>
9#include <fdtdec.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Simon Glass11cd6312016-03-11 22:07:13 -070011#include <pch.h>
12#include <pci.h>
13#include <asm/cpu.h>
14#include <asm/gpio.h>
15#include <asm/io.h>
16#include <asm/pci.h>
17#include <dm/pinctrl.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
21#define GPIO_USESEL_OFFSET(x) (x)
22#define GPIO_IOSEL_OFFSET(x) (x + 4)
23#define GPIO_LVL_OFFSET(x) ((x) ? (x) + 8 : 0xc)
24#define GPI_INV 0x2c
25
26#define IOPAD_MODE_MASK 0x7
27#define IOPAD_PULL_ASSIGN_SHIFT 7
28#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT)
29#define IOPAD_PULL_STRENGTH_SHIFT 9
30#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT)
31
32static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value)
33{
34 if (value)
35 setio_32(base, 1UL << offset);
36 else
37 clrio_32(base, 1UL << offset);
38
39 return 0;
40}
41
42static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func)
43{
44 if (func)
45 setio_32(base, 1UL << offset);
46 else
47 clrio_32(base, 1UL << offset);
48
49 return 0;
50}
51
52static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir)
53{
54 if (!dir)
55 setio_32(base, 1UL << offset);
56 else
57 clrio_32(base, 1UL << offset);
58
59 return 0;
60}
61
62static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
63{
64 bool is_gpio, invert;
65 u32 gpio_offset[2];
66 int pad_offset;
67 int dir, val;
68 int ret;
69
70 /*
71 * GPIO node is not mandatory, so we only do the pinmuxing if the
72 * node exists.
73 */
74 ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset",
75 gpio_offset, 2);
76 if (!ret) {
77 /* Do we want to force the GPIO mode? */
78 is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio");
79 if (is_gpio)
80 ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) +
81 gpio_offset[0], gpio_offset[1],
82 1);
83
84 dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
85 if (dir != -1)
86 ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) +
87 gpio_offset[0], gpio_offset[1],
88 dir);
89
90 val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value",
91 -1);
92 if (val != -1)
93 ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) +
94 gpio_offset[0], gpio_offset[1],
95 val);
96
97 invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert");
98 if (invert)
99 setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]);
100 debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n",
101 gpio_offset[0], gpio_offset[1], is_gpio, dir, val,
102 invert);
103 }
104
105 /* if iobase is present, let's configure the pad */
106 if (iobase != -1) {
Simon Glassaa0ed9d2017-01-16 07:03:42 -0700107 ulong iobase_addr;
Simon Glass11cd6312016-03-11 22:07:13 -0700108
109 /*
110 * The offset for the same pin for the IOBASE and GPIOBASE are
111 * different, so instead of maintaining a lookup table,
112 * the device tree should provide directly the correct
113 * value for both mapping.
114 */
115 pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node,
116 "pad-offset", -1);
117 if (pad_offset == -1)
118 return 0;
119
120 /* compute the absolute pad address */
121 iobase_addr = iobase + pad_offset;
122
123 /*
124 * Do we need to set a specific function mode?
125 * If someone put also 'mode-gpio', this option will
126 * be just ignored by the controller
127 */
128 val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1);
129 if (val != -1)
130 clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val);
131
132 /* Configure the pull-up/down if needed */
133 val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1);
134 if (val != -1)
135 clrsetbits_le32(iobase_addr,
136 IOPAD_PULL_ASSIGN_MASK,
137 val << IOPAD_PULL_ASSIGN_SHIFT);
138
139 val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength",
140 -1);
141 if (val != -1)
142 clrsetbits_le32(iobase_addr,
143 IOPAD_PULL_STRENGTH_MASK,
144 val << IOPAD_PULL_STRENGTH_SHIFT);
145
146 debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset,
147 readl(iobase_addr));
148 }
149
150 return 0;
151}
152
153static int ich6_pinctrl_probe(struct udevice *dev)
154{
155 struct udevice *pch;
156 int pin_node;
157 int ret;
158 u32 gpiobase;
159 u32 iobase = -1;
160
161 debug("%s: start\n", __func__);
162 ret = uclass_first_device(UCLASS_PCH, &pch);
163 if (ret)
164 return ret;
165 if (!pch)
166 return -ENODEV;
167
168 /*
169 * Get the memory/io base address to configure every pins.
170 * IOBASE is used to configure the mode/pads
171 * GPIOBASE is used to configure the direction and default value
172 */
173 ret = pch_get_gpio_base(pch, &gpiobase);
174 if (ret) {
175 debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
176 gpiobase);
177 return -EINVAL;
178 }
179
180 /*
181 * Get the IOBASE, this is not mandatory as this is not
182 * supported by all the CPU
183 */
184 ret = pch_get_io_base(pch, &iobase);
185 if (ret && ret != -ENOSYS) {
186 debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase);
187 return -EINVAL;
188 }
189
Simon Glassdd79d6e2017-01-17 16:52:55 -0700190 for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
Simon Glass11cd6312016-03-11 22:07:13 -0700191 pin_node > 0;
192 pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
193 /* Configure the pin */
194 ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
195 if (ret != 0) {
196 debug("%s: invalid configuration for the pin %d\n",
197 __func__, pin_node);
198 return ret;
199 }
200 }
201 debug("%s: done\n", __func__);
202
203 return 0;
204}
205
206static const struct udevice_id ich6_pinctrl_match[] = {
207 { .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF },
208 { /* sentinel */ }
209};
210
211U_BOOT_DRIVER(ich6_pinctrl) = {
212 .name = "ich6_pinctrl",
213 .id = UCLASS_SYSCON,
214 .of_match = ich6_pinctrl_match,
215 .probe = ich6_pinctrl_probe,
216};