Commit | Line | Data |
---|---|---|
813e7d36 LW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // IXP4 GPIO driver | |
4 | // Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> | |
5 | // | |
6 | // based on previous work and know-how from: | |
7 | // Deepak Saxena <dsaxena@plexity.net> | |
8 | ||
9 | #include <linux/gpio/driver.h> | |
10 | #include <linux/io.h> | |
11 | #include <linux/irq.h> | |
12 | #include <linux/irqdomain.h> | |
13 | #include <linux/irqchip.h> | |
e4bfb0ff | 14 | #include <linux/of_irq.h> |
813e7d36 LW |
15 | #include <linux/platform_device.h> |
16 | #include <linux/bitops.h> | |
17 | /* Include that go away with DT transition */ | |
18 | #include <linux/irqchip/irq-ixp4xx.h> | |
19 | ||
20 | #include <asm/mach-types.h> | |
21 | ||
22 | #define IXP4XX_REG_GPOUT 0x00 | |
23 | #define IXP4XX_REG_GPOE 0x04 | |
24 | #define IXP4XX_REG_GPIN 0x08 | |
25 | #define IXP4XX_REG_GPIS 0x0C | |
26 | #define IXP4XX_REG_GPIT1 0x10 | |
27 | #define IXP4XX_REG_GPIT2 0x14 | |
28 | #define IXP4XX_REG_GPCLK 0x18 | |
29 | #define IXP4XX_REG_GPDBSEL 0x1C | |
30 | ||
31 | /* | |
32 | * The hardware uses 3 bits to indicate interrupt "style". | |
33 | * we clear and set these three bits accordingly. The lower 24 | |
34 | * bits in two registers (GPIT1 and GPIT2) are used to set up | |
35 | * the style for 8 lines each for a total of 16 GPIO lines. | |
36 | */ | |
37 | #define IXP4XX_GPIO_STYLE_ACTIVE_HIGH 0x0 | |
38 | #define IXP4XX_GPIO_STYLE_ACTIVE_LOW 0x1 | |
39 | #define IXP4XX_GPIO_STYLE_RISING_EDGE 0x2 | |
40 | #define IXP4XX_GPIO_STYLE_FALLING_EDGE 0x3 | |
41 | #define IXP4XX_GPIO_STYLE_TRANSITIONAL 0x4 | |
42 | #define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0) | |
43 | #define IXP4XX_GPIO_STYLE_SIZE 3 | |
44 | ||
45 | /** | |
46 | * struct ixp4xx_gpio - IXP4 GPIO state container | |
47 | * @dev: containing device for this instance | |
48 | * @fwnode: the fwnode for this GPIO chip | |
49 | * @gc: gpiochip for this instance | |
50 | * @domain: irqdomain for this chip instance | |
51 | * @base: remapped I/O-memory base | |
52 | * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, | |
53 | * 0: level triggered | |
54 | */ | |
55 | struct ixp4xx_gpio { | |
56 | struct device *dev; | |
57 | struct fwnode_handle *fwnode; | |
58 | struct gpio_chip gc; | |
59 | struct irq_domain *domain; | |
60 | void __iomem *base; | |
61 | unsigned long long irq_edge; | |
62 | }; | |
63 | ||
64 | /** | |
65 | * struct ixp4xx_gpio_map - IXP4 GPIO to parent IRQ map | |
66 | * @gpio_offset: offset of the IXP4 GPIO line | |
67 | * @parent_hwirq: hwirq on the parent IRQ controller | |
68 | */ | |
69 | struct ixp4xx_gpio_map { | |
70 | int gpio_offset; | |
71 | int parent_hwirq; | |
72 | }; | |
73 | ||
74 | /* GPIO lines 0..12 have corresponding IRQs, GPIOs 13..15 have no IRQs */ | |
75 | const struct ixp4xx_gpio_map ixp4xx_gpiomap[] = { | |
76 | { .gpio_offset = 0, .parent_hwirq = 6 }, | |
77 | { .gpio_offset = 1, .parent_hwirq = 7 }, | |
78 | { .gpio_offset = 2, .parent_hwirq = 19 }, | |
79 | { .gpio_offset = 3, .parent_hwirq = 20 }, | |
80 | { .gpio_offset = 4, .parent_hwirq = 21 }, | |
81 | { .gpio_offset = 5, .parent_hwirq = 22 }, | |
82 | { .gpio_offset = 6, .parent_hwirq = 23 }, | |
83 | { .gpio_offset = 7, .parent_hwirq = 24 }, | |
84 | { .gpio_offset = 8, .parent_hwirq = 25 }, | |
85 | { .gpio_offset = 9, .parent_hwirq = 26 }, | |
86 | { .gpio_offset = 10, .parent_hwirq = 27 }, | |
87 | { .gpio_offset = 11, .parent_hwirq = 28 }, | |
88 | { .gpio_offset = 12, .parent_hwirq = 29 }, | |
89 | }; | |
90 | ||
91 | static void ixp4xx_gpio_irq_ack(struct irq_data *d) | |
92 | { | |
93 | struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); | |
94 | ||
95 | __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); | |
96 | } | |
97 | ||
98 | static void ixp4xx_gpio_irq_unmask(struct irq_data *d) | |
99 | { | |
100 | struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); | |
101 | ||
102 | /* ACK when unmasking if not edge-triggered */ | |
103 | if (!(g->irq_edge & BIT(d->hwirq))) | |
104 | ixp4xx_gpio_irq_ack(d); | |
105 | ||
106 | irq_chip_unmask_parent(d); | |
107 | } | |
108 | ||
109 | static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) | |
110 | { | |
111 | struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); | |
112 | int line = d->hwirq; | |
113 | unsigned long flags; | |
114 | u32 int_style; | |
115 | u32 int_reg; | |
116 | u32 val; | |
117 | ||
118 | switch (type) { | |
119 | case IRQ_TYPE_EDGE_BOTH: | |
120 | irq_set_handler_locked(d, handle_edge_irq); | |
121 | int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL; | |
122 | g->irq_edge |= BIT(d->hwirq); | |
123 | break; | |
124 | case IRQ_TYPE_EDGE_RISING: | |
125 | irq_set_handler_locked(d, handle_edge_irq); | |
126 | int_style = IXP4XX_GPIO_STYLE_RISING_EDGE; | |
127 | g->irq_edge |= BIT(d->hwirq); | |
128 | break; | |
129 | case IRQ_TYPE_EDGE_FALLING: | |
130 | irq_set_handler_locked(d, handle_edge_irq); | |
131 | int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE; | |
132 | g->irq_edge |= BIT(d->hwirq); | |
133 | break; | |
134 | case IRQ_TYPE_LEVEL_HIGH: | |
135 | irq_set_handler_locked(d, handle_level_irq); | |
136 | int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; | |
137 | g->irq_edge &= ~BIT(d->hwirq); | |
138 | break; | |
139 | case IRQ_TYPE_LEVEL_LOW: | |
140 | irq_set_handler_locked(d, handle_level_irq); | |
141 | int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW; | |
142 | g->irq_edge &= ~BIT(d->hwirq); | |
143 | break; | |
144 | default: | |
145 | return -EINVAL; | |
146 | } | |
147 | ||
148 | if (line >= 8) { | |
149 | /* pins 8-15 */ | |
150 | line -= 8; | |
151 | int_reg = IXP4XX_REG_GPIT2; | |
152 | } else { | |
153 | /* pins 0-7 */ | |
154 | int_reg = IXP4XX_REG_GPIT1; | |
155 | } | |
156 | ||
157 | spin_lock_irqsave(&g->gc.bgpio_lock, flags); | |
158 | ||
159 | /* Clear the style for the appropriate pin */ | |
160 | val = __raw_readl(g->base + int_reg); | |
161 | val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE)); | |
162 | __raw_writel(val, g->base + int_reg); | |
163 | ||
164 | __raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS); | |
165 | ||
166 | /* Set the new style */ | |
167 | val = __raw_readl(g->base + int_reg); | |
168 | val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); | |
169 | __raw_writel(val, g->base + int_reg); | |
170 | ||
171 | /* Force-configure this line as an input */ | |
172 | val = __raw_readl(g->base + IXP4XX_REG_GPOE); | |
173 | val |= BIT(d->hwirq); | |
174 | __raw_writel(val, g->base + IXP4XX_REG_GPOE); | |
175 | ||
176 | spin_unlock_irqrestore(&g->gc.bgpio_lock, flags); | |
177 | ||
178 | /* This parent only accept level high (asserted) */ | |
179 | return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); | |
180 | } | |
181 | ||
182 | static struct irq_chip ixp4xx_gpio_irqchip = { | |
183 | .name = "IXP4GPIO", | |
184 | .irq_ack = ixp4xx_gpio_irq_ack, | |
185 | .irq_mask = irq_chip_mask_parent, | |
186 | .irq_unmask = ixp4xx_gpio_irq_unmask, | |
187 | .irq_set_type = ixp4xx_gpio_irq_set_type, | |
188 | }; | |
189 | ||
190 | static int ixp4xx_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) | |
191 | { | |
192 | struct ixp4xx_gpio *g = gpiochip_get_data(gc); | |
193 | struct irq_fwspec fwspec; | |
194 | ||
195 | fwspec.fwnode = g->fwnode; | |
196 | fwspec.param_count = 2; | |
197 | fwspec.param[0] = offset; | |
198 | fwspec.param[1] = IRQ_TYPE_NONE; | |
199 | ||
200 | return irq_create_fwspec_mapping(&fwspec); | |
201 | } | |
202 | ||
203 | static int ixp4xx_gpio_irq_domain_translate(struct irq_domain *domain, | |
204 | struct irq_fwspec *fwspec, | |
205 | unsigned long *hwirq, | |
206 | unsigned int *type) | |
207 | { | |
315c1a8e | 208 | int ret; |
813e7d36 LW |
209 | |
210 | /* We support standard DT translation */ | |
211 | if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { | |
315c1a8e LW |
212 | return irq_domain_translate_twocell(domain, fwspec, |
213 | hwirq, type); | |
813e7d36 LW |
214 | } |
215 | ||
216 | /* This goes away when we transition to DT */ | |
217 | if (is_fwnode_irqchip(fwspec->fwnode)) { | |
315c1a8e LW |
218 | ret = irq_domain_translate_twocell(domain, fwspec, |
219 | hwirq, type); | |
220 | if (ret) | |
221 | return ret; | |
813e7d36 LW |
222 | WARN_ON(*type == IRQ_TYPE_NONE); |
223 | return 0; | |
224 | } | |
225 | return -EINVAL; | |
226 | } | |
227 | ||
228 | static int ixp4xx_gpio_irq_domain_alloc(struct irq_domain *d, | |
229 | unsigned int irq, unsigned int nr_irqs, | |
230 | void *data) | |
231 | { | |
232 | struct ixp4xx_gpio *g = d->host_data; | |
233 | irq_hw_number_t hwirq; | |
234 | unsigned int type = IRQ_TYPE_NONE; | |
235 | struct irq_fwspec *fwspec = data; | |
236 | int ret; | |
237 | int i; | |
238 | ||
239 | ret = ixp4xx_gpio_irq_domain_translate(d, fwspec, &hwirq, &type); | |
240 | if (ret) | |
241 | return ret; | |
242 | ||
243 | dev_dbg(g->dev, "allocate IRQ %d..%d, hwirq %lu..%lu\n", | |
244 | irq, irq + nr_irqs - 1, | |
245 | hwirq, hwirq + nr_irqs - 1); | |
246 | ||
247 | for (i = 0; i < nr_irqs; i++) { | |
248 | struct irq_fwspec parent_fwspec; | |
249 | const struct ixp4xx_gpio_map *map; | |
250 | int j; | |
251 | ||
252 | /* Not all lines support IRQs */ | |
253 | for (j = 0; j < ARRAY_SIZE(ixp4xx_gpiomap); j++) { | |
254 | map = &ixp4xx_gpiomap[j]; | |
255 | if (map->gpio_offset == hwirq) | |
256 | break; | |
257 | } | |
258 | if (j == ARRAY_SIZE(ixp4xx_gpiomap)) { | |
259 | dev_err(g->dev, "can't look up hwirq %lu\n", hwirq); | |
260 | return -EINVAL; | |
261 | } | |
262 | dev_dbg(g->dev, "found parent hwirq %u\n", map->parent_hwirq); | |
263 | ||
264 | /* | |
265 | * We set handle_bad_irq because the .set_type() should | |
266 | * always be invoked and set the right type of handler. | |
267 | */ | |
268 | irq_domain_set_info(d, | |
269 | irq + i, | |
270 | hwirq + i, | |
271 | &ixp4xx_gpio_irqchip, | |
272 | g, | |
273 | handle_bad_irq, | |
274 | NULL, NULL); | |
275 | irq_set_probe(irq + i); | |
276 | ||
277 | /* | |
278 | * Create a IRQ fwspec to send up to the parent irqdomain: | |
279 | * specify the hwirq we address on the parent and tie it | |
280 | * all together up the chain. | |
281 | */ | |
282 | parent_fwspec.fwnode = d->parent->fwnode; | |
283 | parent_fwspec.param_count = 2; | |
284 | parent_fwspec.param[0] = map->parent_hwirq; | |
285 | /* This parent only handles asserted level IRQs */ | |
286 | parent_fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; | |
287 | dev_dbg(g->dev, "alloc_irqs_parent for %d parent hwirq %d\n", | |
288 | irq + i, map->parent_hwirq); | |
289 | ret = irq_domain_alloc_irqs_parent(d, irq + i, 1, | |
290 | &parent_fwspec); | |
291 | if (ret) | |
292 | dev_err(g->dev, | |
293 | "failed to allocate parent hwirq %d for hwirq %lu\n", | |
294 | map->parent_hwirq, hwirq); | |
295 | } | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | static const struct irq_domain_ops ixp4xx_gpio_irqdomain_ops = { | |
301 | .translate = ixp4xx_gpio_irq_domain_translate, | |
302 | .alloc = ixp4xx_gpio_irq_domain_alloc, | |
303 | .free = irq_domain_free_irqs_common, | |
304 | }; | |
305 | ||
306 | static int ixp4xx_gpio_probe(struct platform_device *pdev) | |
307 | { | |
308 | unsigned long flags; | |
309 | struct device *dev = &pdev->dev; | |
e4bfb0ff | 310 | struct device_node *np = dev->of_node; |
813e7d36 LW |
311 | struct irq_domain *parent; |
312 | struct resource *res; | |
313 | struct ixp4xx_gpio *g; | |
314 | int ret; | |
315 | int i; | |
316 | ||
317 | g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); | |
318 | if (!g) | |
319 | return -ENOMEM; | |
320 | g->dev = dev; | |
321 | ||
322 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
323 | g->base = devm_ioremap_resource(dev, res); | |
324 | if (IS_ERR(g->base)) { | |
325 | dev_err(dev, "ioremap error\n"); | |
326 | return PTR_ERR(g->base); | |
327 | } | |
328 | ||
329 | /* | |
330 | * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on | |
331 | * specific machines. | |
332 | */ | |
333 | if (machine_is_dsmg600() || machine_is_nas100d()) | |
334 | __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK); | |
335 | ||
336 | /* | |
337 | * This is a very special big-endian ARM issue: when the IXP4xx is | |
338 | * run in big endian mode, all registers in the machine are switched | |
339 | * around to the CPU-native endianness. As you see mostly in the | |
340 | * driver we use __raw_readl()/__raw_writel() to access the registers | |
341 | * in the appropriate order. With the GPIO library we need to specify | |
342 | * byte order explicitly, so this flag needs to be set when compiling | |
343 | * for big endian. | |
344 | */ | |
345 | #if defined(CONFIG_CPU_BIG_ENDIAN) | |
346 | flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; | |
347 | #else | |
348 | flags = 0; | |
349 | #endif | |
350 | ||
351 | /* Populate and register gpio chip */ | |
352 | ret = bgpio_init(&g->gc, dev, 4, | |
353 | g->base + IXP4XX_REG_GPIN, | |
354 | g->base + IXP4XX_REG_GPOUT, | |
355 | NULL, | |
356 | NULL, | |
357 | g->base + IXP4XX_REG_GPOE, | |
358 | flags); | |
359 | if (ret) { | |
360 | dev_err(dev, "unable to init generic GPIO\n"); | |
361 | return ret; | |
362 | } | |
363 | g->gc.to_irq = ixp4xx_gpio_to_irq; | |
364 | g->gc.ngpio = 16; | |
365 | g->gc.label = "IXP4XX_GPIO_CHIP"; | |
366 | /* | |
367 | * TODO: when we have migrated to device tree and all GPIOs | |
368 | * are fetched using phandles, set this to -1 to get rid of | |
369 | * the fixed gpiochip base. | |
370 | */ | |
371 | g->gc.base = 0; | |
372 | g->gc.parent = &pdev->dev; | |
373 | g->gc.owner = THIS_MODULE; | |
374 | ||
375 | ret = devm_gpiochip_add_data(dev, &g->gc, g); | |
376 | if (ret) { | |
377 | dev_err(dev, "failed to add SoC gpiochip\n"); | |
378 | return ret; | |
379 | } | |
380 | ||
381 | /* | |
382 | * When we convert to device tree we will simply look up the | |
383 | * parent irqdomain using irq_find_host(parent) as parent comes | |
384 | * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get | |
385 | * the fwnode. For now we need this boardfile style code. | |
386 | */ | |
e4bfb0ff LW |
387 | if (np) { |
388 | struct device_node *irq_parent; | |
389 | ||
390 | irq_parent = of_irq_find_parent(np); | |
391 | if (!irq_parent) { | |
392 | dev_err(dev, "no IRQ parent node\n"); | |
393 | return -ENODEV; | |
394 | } | |
395 | parent = irq_find_host(irq_parent); | |
396 | if (!parent) { | |
397 | dev_err(dev, "no IRQ parent domain\n"); | |
398 | return -ENODEV; | |
399 | } | |
400 | g->fwnode = of_node_to_fwnode(np); | |
401 | } else { | |
402 | parent = ixp4xx_get_irq_domain(); | |
403 | g->fwnode = irq_domain_alloc_fwnode(g->base); | |
404 | if (!g->fwnode) { | |
405 | dev_err(dev, "no domain base\n"); | |
406 | return -ENODEV; | |
407 | } | |
813e7d36 LW |
408 | } |
409 | g->domain = irq_domain_create_hierarchy(parent, | |
410 | IRQ_DOMAIN_FLAG_HIERARCHY, | |
411 | ARRAY_SIZE(ixp4xx_gpiomap), | |
412 | g->fwnode, | |
413 | &ixp4xx_gpio_irqdomain_ops, | |
414 | g); | |
415 | if (!g->domain) { | |
416 | irq_domain_free_fwnode(g->fwnode); | |
417 | dev_err(dev, "no hierarchical irq domain\n"); | |
418 | return ret; | |
419 | } | |
420 | ||
421 | /* | |
422 | * After adding OF support, this is no longer needed: irqs | |
423 | * will be allocated for the respective fwnodes. | |
424 | */ | |
e4bfb0ff LW |
425 | if (!np) { |
426 | for (i = 0; i < ARRAY_SIZE(ixp4xx_gpiomap); i++) { | |
427 | const struct ixp4xx_gpio_map *map = &ixp4xx_gpiomap[i]; | |
428 | struct irq_fwspec fwspec; | |
429 | ||
430 | fwspec.fwnode = g->fwnode; | |
431 | /* This is the hwirq for the GPIO line side of things */ | |
432 | fwspec.param[0] = map->gpio_offset; | |
433 | fwspec.param[1] = IRQ_TYPE_EDGE_RISING; | |
434 | fwspec.param_count = 2; | |
435 | ret = __irq_domain_alloc_irqs(g->domain, | |
436 | -1, /* just pick something */ | |
437 | 1, | |
438 | NUMA_NO_NODE, | |
439 | &fwspec, | |
440 | false, | |
441 | NULL); | |
442 | if (ret < 0) { | |
443 | irq_domain_free_fwnode(g->fwnode); | |
444 | dev_err(dev, | |
445 | "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", | |
446 | map->gpio_offset, map->parent_hwirq, | |
447 | ret); | |
448 | return ret; | |
449 | } | |
813e7d36 LW |
450 | } |
451 | } | |
452 | ||
453 | platform_set_drvdata(pdev, g); | |
454 | dev_info(dev, "IXP4 GPIO @%p registered\n", g->base); | |
455 | ||
456 | return 0; | |
457 | } | |
458 | ||
e4bfb0ff LW |
459 | static const struct of_device_id ixp4xx_gpio_of_match[] = { |
460 | { | |
461 | .compatible = "intel,ixp4xx-gpio", | |
462 | }, | |
463 | {}, | |
464 | }; | |
465 | ||
466 | ||
813e7d36 LW |
467 | static struct platform_driver ixp4xx_gpio_driver = { |
468 | .driver = { | |
469 | .name = "ixp4xx-gpio", | |
e4bfb0ff | 470 | .of_match_table = of_match_ptr(ixp4xx_gpio_of_match), |
813e7d36 LW |
471 | }, |
472 | .probe = ixp4xx_gpio_probe, | |
473 | }; | |
474 | builtin_platform_driver(ixp4xx_gpio_driver); |