]>
Commit | Line | Data |
---|---|---|
b24f5c4f SG |
1 | /* |
2 | * Copyright (C) 2016 Google, Inc | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <errno.h> | |
10 | #include <fdtdec.h> | |
11 | #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 <asm/arch/gpio.h> | |
18 | #include <dt-bindings/gpio/x86-gpio.h> | |
19 | #include <dm/pinctrl.h> | |
20 | ||
21 | DECLARE_GLOBAL_DATA_PTR; | |
22 | ||
23 | enum { | |
24 | MAX_GPIOS = 95, | |
25 | }; | |
26 | ||
27 | #define PIRQ_SHIFT 16 | |
28 | #define CONF_MASK 0xffff | |
29 | ||
30 | struct pin_info { | |
31 | int node; | |
32 | int phandle; | |
33 | bool mode_gpio; | |
34 | bool dir_input; | |
35 | bool invert; | |
36 | bool trigger_level; | |
37 | bool output_high; | |
38 | bool sense_disable; | |
39 | bool owner_gpio; | |
40 | bool route_smi; | |
41 | bool irq_enable; | |
42 | bool reset_rsmrst; | |
43 | bool pirq_apic_route; | |
44 | }; | |
45 | ||
46 | static int broadwell_pinctrl_read_configs(struct udevice *dev, | |
47 | struct pin_info *conf, int max_pins) | |
48 | { | |
49 | const void *blob = gd->fdt_blob; | |
50 | int count = 0; | |
51 | int node; | |
52 | ||
53 | debug("%s: starting\n", __func__); | |
e160f7d4 | 54 | for (node = fdt_first_subnode(blob, dev_of_offset(dev)); |
b24f5c4f SG |
55 | node > 0; |
56 | node = fdt_next_subnode(blob, node)) { | |
57 | int phandle = fdt_get_phandle(blob, node); | |
58 | ||
59 | if (!phandle) | |
60 | continue; | |
61 | if (count == max_pins) | |
62 | return -ENOSPC; | |
63 | ||
64 | /* We've found a new configuration */ | |
65 | memset(conf, '\0', sizeof(*conf)); | |
66 | conf->node = node; | |
67 | conf->phandle = phandle; | |
68 | conf->mode_gpio = fdtdec_get_bool(blob, node, "mode-gpio"); | |
69 | if (fdtdec_get_int(blob, node, "direction", -1) == PIN_INPUT) | |
70 | conf->dir_input = true; | |
71 | conf->invert = fdtdec_get_bool(blob, node, "invert"); | |
72 | if (fdtdec_get_int(blob, node, "trigger", -1) == TRIGGER_LEVEL) | |
73 | conf->trigger_level = true; | |
74 | if (fdtdec_get_int(blob, node, "output-value", -1) == 1) | |
75 | conf->output_high = true; | |
76 | conf->sense_disable = fdtdec_get_bool(blob, node, | |
77 | "sense-disable"); | |
78 | if (fdtdec_get_int(blob, node, "owner", -1) == OWNER_GPIO) | |
79 | conf->owner_gpio = true; | |
80 | if (fdtdec_get_int(blob, node, "route", -1) == ROUTE_SMI) | |
81 | conf->route_smi = true; | |
82 | conf->irq_enable = fdtdec_get_bool(blob, node, "irq-enable"); | |
83 | conf->reset_rsmrst = fdtdec_get_bool(blob, node, | |
84 | "reset-rsmrst"); | |
85 | if (fdtdec_get_int(blob, node, "pirq-apic", -1) == | |
86 | PIRQ_APIC_ROUTE) | |
87 | conf->pirq_apic_route = true; | |
88 | debug("config: phandle=%d\n", phandle); | |
89 | count++; | |
90 | conf++; | |
91 | } | |
92 | debug("%s: Found %d configurations\n", __func__, count); | |
93 | ||
94 | return count; | |
95 | } | |
96 | ||
97 | static int broadwell_pinctrl_lookup_phandle(struct pin_info *conf, | |
98 | int conf_count, int phandle) | |
99 | { | |
100 | int i; | |
101 | ||
102 | for (i = 0; i < conf_count; i++) { | |
103 | if (conf[i].phandle == phandle) | |
104 | return i; | |
105 | } | |
106 | ||
107 | return -ENOENT; | |
108 | } | |
109 | ||
110 | static int broadwell_pinctrl_read_pins(struct udevice *dev, | |
111 | struct pin_info *conf, int conf_count, int gpio_conf[], | |
112 | int num_gpios) | |
113 | { | |
114 | const void *blob = gd->fdt_blob; | |
115 | int count = 0; | |
116 | int node; | |
117 | ||
e160f7d4 | 118 | for (node = fdt_first_subnode(blob, dev_of_offset(dev)); |
b24f5c4f SG |
119 | node > 0; |
120 | node = fdt_next_subnode(blob, node)) { | |
121 | int len, i; | |
122 | const u32 *prop = fdt_getprop(blob, node, "config", &len); | |
123 | ||
124 | if (!prop) | |
125 | continue; | |
126 | ||
127 | /* There are three cells per pin */ | |
128 | count = len / (sizeof(u32) * 3); | |
129 | debug("Found %d GPIOs to configure\n", count); | |
130 | for (i = 0; i < count; i++) { | |
131 | uint gpio = fdt32_to_cpu(prop[i * 3]); | |
132 | uint phandle = fdt32_to_cpu(prop[i * 3 + 1]); | |
133 | int val; | |
134 | ||
135 | if (gpio >= num_gpios) { | |
136 | debug("%s: GPIO %d out of range\n", __func__, | |
137 | gpio); | |
138 | return -EDOM; | |
139 | } | |
140 | val = broadwell_pinctrl_lookup_phandle(conf, conf_count, | |
141 | phandle); | |
142 | if (val < 0) { | |
143 | debug("%s: Cannot find phandle %d\n", __func__, | |
144 | phandle); | |
145 | return -EINVAL; | |
146 | } | |
147 | gpio_conf[gpio] = val | | |
148 | fdt32_to_cpu(prop[i * 3 + 2]) << PIRQ_SHIFT; | |
149 | } | |
150 | } | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static void broadwell_pinctrl_commit(struct pch_lp_gpio_regs *regs, | |
156 | struct pin_info *pin_info, | |
157 | int gpio_conf[], int count) | |
158 | { | |
159 | u32 owner_gpio[GPIO_BANKS] = {0}; | |
160 | u32 route_smi[GPIO_BANKS] = {0}; | |
161 | u32 irq_enable[GPIO_BANKS] = {0}; | |
162 | u32 reset_rsmrst[GPIO_BANKS] = {0}; | |
163 | u32 pirq2apic = 0; | |
164 | int set, bit, gpio = 0; | |
165 | ||
166 | for (gpio = 0; gpio < MAX_GPIOS; gpio++) { | |
167 | int confnum = gpio_conf[gpio] & CONF_MASK; | |
168 | struct pin_info *pin = &pin_info[confnum]; | |
169 | u32 val; | |
170 | ||
171 | val = pin->mode_gpio << CONFA_MODE_SHIFT | | |
172 | pin->dir_input << CONFA_DIR_SHIFT | | |
173 | pin->invert << CONFA_INVERT_SHIFT | | |
174 | pin->trigger_level << CONFA_TRIGGER_SHIFT | | |
175 | pin->output_high << CONFA_OUTPUT_SHIFT; | |
176 | outl(val, ®s->config[gpio].conf_a); | |
177 | outl(pin->sense_disable << CONFB_SENSE_SHIFT, | |
178 | ®s->config[gpio].conf_b); | |
179 | ||
180 | /* Determine set and bit based on GPIO number */ | |
181 | set = gpio / GPIO_PER_BANK; | |
182 | bit = gpio % GPIO_PER_BANK; | |
183 | ||
184 | /* Apply settings to set specific bits */ | |
185 | owner_gpio[set] |= pin->owner_gpio << bit; | |
186 | route_smi[set] |= pin->route_smi << bit; | |
187 | irq_enable[set] |= pin->irq_enable << bit; | |
188 | reset_rsmrst[set] |= pin->reset_rsmrst << bit; | |
189 | ||
190 | /* PIRQ to IO-APIC map */ | |
191 | if (pin->pirq_apic_route) | |
192 | pirq2apic |= gpio_conf[gpio] >> PIRQ_SHIFT; | |
193 | debug("gpio %d: conf %d, mode_gpio %d, dir_input %d, output_high %d\n", | |
194 | gpio, confnum, pin->mode_gpio, pin->dir_input, | |
195 | pin->output_high); | |
196 | } | |
197 | ||
198 | for (set = 0; set < GPIO_BANKS; set++) { | |
199 | outl(owner_gpio[set], ®s->own[set]); | |
200 | outl(route_smi[set], ®s->gpi_route[set]); | |
201 | outl(irq_enable[set], ®s->gpi_ie[set]); | |
202 | outl(reset_rsmrst[set], ®s->rst_sel[set]); | |
203 | } | |
204 | ||
205 | outl(pirq2apic, ®s->pirq_to_ioxapic); | |
206 | } | |
207 | ||
208 | static int broadwell_pinctrl_probe(struct udevice *dev) | |
209 | { | |
210 | struct pch_lp_gpio_regs *regs; | |
211 | struct pin_info conf[12]; | |
212 | int gpio_conf[MAX_GPIOS]; | |
213 | struct udevice *pch; | |
214 | int conf_count; | |
215 | u32 gpiobase; | |
216 | int ret; | |
217 | ||
218 | ret = uclass_first_device(UCLASS_PCH, &pch); | |
219 | if (ret) | |
220 | return ret; | |
221 | if (!pch) | |
222 | return -ENODEV; | |
223 | debug("%s: start\n", __func__); | |
224 | ||
225 | /* Only init once, before relocation */ | |
226 | if (gd->flags & GD_FLG_RELOC) | |
227 | return 0; | |
228 | ||
229 | /* | |
230 | * Get the memory/io base address to configure every pins. | |
231 | * IOBASE is used to configure the mode/pads | |
232 | * GPIOBASE is used to configure the direction and default value | |
233 | */ | |
234 | ret = pch_get_gpio_base(pch, &gpiobase); | |
235 | if (ret) { | |
236 | debug("%s: invalid GPIOBASE address (%08x)\n", __func__, | |
237 | gpiobase); | |
238 | return -EINVAL; | |
239 | } | |
240 | ||
241 | conf_count = broadwell_pinctrl_read_configs(dev, conf, | |
242 | ARRAY_SIZE(conf)); | |
243 | if (conf_count < 0) { | |
244 | debug("%s: Cannot read configs: err=%d\n", __func__, ret); | |
245 | return conf_count; | |
246 | } | |
247 | ||
248 | /* | |
249 | * Assume that pin settings are provided for every pin. Pins not | |
250 | * mentioned will get the first config mentioned in the list. | |
251 | */ | |
252 | ret = broadwell_pinctrl_read_pins(dev, conf, conf_count, gpio_conf, | |
253 | MAX_GPIOS); | |
254 | if (ret) { | |
255 | debug("%s: Cannot read pin settings: err=%d\n", __func__, ret); | |
256 | return ret; | |
257 | } | |
258 | ||
259 | regs = (struct pch_lp_gpio_regs *)gpiobase; | |
260 | broadwell_pinctrl_commit(regs, conf, gpio_conf, ARRAY_SIZE(conf)); | |
261 | ||
262 | debug("%s: done\n", __func__); | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static const struct udevice_id broadwell_pinctrl_match[] = { | |
268 | { .compatible = "intel,x86-broadwell-pinctrl", | |
269 | .data = X86_SYSCON_PINCONF }, | |
270 | { /* sentinel */ } | |
271 | }; | |
272 | ||
273 | U_BOOT_DRIVER(broadwell_pinctrl) = { | |
274 | .name = "broadwell_pinctrl", | |
275 | .id = UCLASS_SYSCON, | |
276 | .of_match = broadwell_pinctrl_match, | |
277 | .probe = broadwell_pinctrl_probe, | |
278 | }; |