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