diff options
Diffstat (limited to 'drivers/gpio/gpio-dwapb.c')
-rw-r--r-- | drivers/gpio/gpio-dwapb.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c new file mode 100644 index 000000000000..ed5711f77e2d --- /dev/null +++ b/drivers/gpio/gpio-dwapb.c | |||
@@ -0,0 +1,438 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Jamie Iles | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * All enquiries to support@picochip.com | ||
9 | */ | ||
10 | #include <linux/basic_mmio_gpio.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/ioport.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/irqdomain.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_address.h> | ||
21 | #include <linux/of_irq.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | |||
25 | #define GPIO_SWPORTA_DR 0x00 | ||
26 | #define GPIO_SWPORTA_DDR 0x04 | ||
27 | #define GPIO_SWPORTB_DR 0x0c | ||
28 | #define GPIO_SWPORTB_DDR 0x10 | ||
29 | #define GPIO_SWPORTC_DR 0x18 | ||
30 | #define GPIO_SWPORTC_DDR 0x1c | ||
31 | #define GPIO_SWPORTD_DR 0x24 | ||
32 | #define GPIO_SWPORTD_DDR 0x28 | ||
33 | #define GPIO_INTEN 0x30 | ||
34 | #define GPIO_INTMASK 0x34 | ||
35 | #define GPIO_INTTYPE_LEVEL 0x38 | ||
36 | #define GPIO_INT_POLARITY 0x3c | ||
37 | #define GPIO_INTSTATUS 0x40 | ||
38 | #define GPIO_PORTA_EOI 0x4c | ||
39 | #define GPIO_EXT_PORTA 0x50 | ||
40 | #define GPIO_EXT_PORTB 0x54 | ||
41 | #define GPIO_EXT_PORTC 0x58 | ||
42 | #define GPIO_EXT_PORTD 0x5c | ||
43 | |||
44 | #define DWAPB_MAX_PORTS 4 | ||
45 | #define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA) | ||
46 | #define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR) | ||
47 | #define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR) | ||
48 | |||
49 | struct dwapb_gpio; | ||
50 | |||
51 | struct dwapb_gpio_port { | ||
52 | struct bgpio_chip bgc; | ||
53 | bool is_registered; | ||
54 | struct dwapb_gpio *gpio; | ||
55 | }; | ||
56 | |||
57 | struct dwapb_gpio { | ||
58 | struct device *dev; | ||
59 | void __iomem *regs; | ||
60 | struct dwapb_gpio_port *ports; | ||
61 | unsigned int nr_ports; | ||
62 | struct irq_domain *domain; | ||
63 | }; | ||
64 | |||
65 | static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) | ||
66 | { | ||
67 | struct bgpio_chip *bgc = to_bgpio_chip(gc); | ||
68 | struct dwapb_gpio_port *port = container_of(bgc, struct | ||
69 | dwapb_gpio_port, bgc); | ||
70 | struct dwapb_gpio *gpio = port->gpio; | ||
71 | |||
72 | return irq_find_mapping(gpio->domain, offset); | ||
73 | } | ||
74 | |||
75 | static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs) | ||
76 | { | ||
77 | u32 v = readl(gpio->regs + GPIO_INT_POLARITY); | ||
78 | |||
79 | if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs)) | ||
80 | v &= ~BIT(offs); | ||
81 | else | ||
82 | v |= BIT(offs); | ||
83 | |||
84 | writel(v, gpio->regs + GPIO_INT_POLARITY); | ||
85 | } | ||
86 | |||
87 | static void dwapb_irq_handler(u32 irq, struct irq_desc *desc) | ||
88 | { | ||
89 | struct dwapb_gpio *gpio = irq_get_handler_data(irq); | ||
90 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
91 | u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS); | ||
92 | |||
93 | while (irq_status) { | ||
94 | int hwirq = fls(irq_status) - 1; | ||
95 | int gpio_irq = irq_find_mapping(gpio->domain, hwirq); | ||
96 | |||
97 | generic_handle_irq(gpio_irq); | ||
98 | irq_status &= ~BIT(hwirq); | ||
99 | |||
100 | if ((irq_get_trigger_type(gpio_irq) & IRQ_TYPE_SENSE_MASK) | ||
101 | == IRQ_TYPE_EDGE_BOTH) | ||
102 | dwapb_toggle_trigger(gpio, hwirq); | ||
103 | } | ||
104 | |||
105 | if (chip->irq_eoi) | ||
106 | chip->irq_eoi(irq_desc_get_irq_data(desc)); | ||
107 | } | ||
108 | |||
109 | static void dwapb_irq_enable(struct irq_data *d) | ||
110 | { | ||
111 | struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); | ||
112 | struct dwapb_gpio *gpio = igc->private; | ||
113 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
114 | unsigned long flags; | ||
115 | u32 val; | ||
116 | |||
117 | spin_lock_irqsave(&bgc->lock, flags); | ||
118 | val = readl(gpio->regs + GPIO_INTEN); | ||
119 | val |= BIT(d->hwirq); | ||
120 | writel(val, gpio->regs + GPIO_INTEN); | ||
121 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
122 | } | ||
123 | |||
124 | static void dwapb_irq_disable(struct irq_data *d) | ||
125 | { | ||
126 | struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); | ||
127 | struct dwapb_gpio *gpio = igc->private; | ||
128 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
129 | unsigned long flags; | ||
130 | u32 val; | ||
131 | |||
132 | spin_lock_irqsave(&bgc->lock, flags); | ||
133 | val = readl(gpio->regs + GPIO_INTEN); | ||
134 | val &= ~BIT(d->hwirq); | ||
135 | writel(val, gpio->regs + GPIO_INTEN); | ||
136 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
137 | } | ||
138 | |||
139 | static int dwapb_irq_reqres(struct irq_data *d) | ||
140 | { | ||
141 | struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); | ||
142 | struct dwapb_gpio *gpio = igc->private; | ||
143 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
144 | |||
145 | if (gpio_lock_as_irq(&bgc->gc, irqd_to_hwirq(d))) { | ||
146 | dev_err(gpio->dev, "unable to lock HW IRQ %lu for IRQ\n", | ||
147 | irqd_to_hwirq(d)); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static void dwapb_irq_relres(struct irq_data *d) | ||
154 | { | ||
155 | struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); | ||
156 | struct dwapb_gpio *gpio = igc->private; | ||
157 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
158 | |||
159 | gpio_unlock_as_irq(&bgc->gc, irqd_to_hwirq(d)); | ||
160 | } | ||
161 | |||
162 | static int dwapb_irq_set_type(struct irq_data *d, u32 type) | ||
163 | { | ||
164 | struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); | ||
165 | struct dwapb_gpio *gpio = igc->private; | ||
166 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
167 | int bit = d->hwirq; | ||
168 | unsigned long level, polarity, flags; | ||
169 | |||
170 | if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | | ||
171 | IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) | ||
172 | return -EINVAL; | ||
173 | |||
174 | spin_lock_irqsave(&bgc->lock, flags); | ||
175 | level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); | ||
176 | polarity = readl(gpio->regs + GPIO_INT_POLARITY); | ||
177 | |||
178 | switch (type) { | ||
179 | case IRQ_TYPE_EDGE_BOTH: | ||
180 | level |= BIT(bit); | ||
181 | dwapb_toggle_trigger(gpio, bit); | ||
182 | break; | ||
183 | case IRQ_TYPE_EDGE_RISING: | ||
184 | level |= BIT(bit); | ||
185 | polarity |= BIT(bit); | ||
186 | break; | ||
187 | case IRQ_TYPE_EDGE_FALLING: | ||
188 | level |= BIT(bit); | ||
189 | polarity &= ~BIT(bit); | ||
190 | break; | ||
191 | case IRQ_TYPE_LEVEL_HIGH: | ||
192 | level &= ~BIT(bit); | ||
193 | polarity |= BIT(bit); | ||
194 | break; | ||
195 | case IRQ_TYPE_LEVEL_LOW: | ||
196 | level &= ~BIT(bit); | ||
197 | polarity &= ~BIT(bit); | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | writel(level, gpio->regs + GPIO_INTTYPE_LEVEL); | ||
202 | writel(polarity, gpio->regs + GPIO_INT_POLARITY); | ||
203 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static void dwapb_configure_irqs(struct dwapb_gpio *gpio, | ||
209 | struct dwapb_gpio_port *port) | ||
210 | { | ||
211 | struct gpio_chip *gc = &port->bgc.gc; | ||
212 | struct device_node *node = gc->of_node; | ||
213 | struct irq_chip_generic *irq_gc; | ||
214 | unsigned int hwirq, ngpio = gc->ngpio; | ||
215 | struct irq_chip_type *ct; | ||
216 | int err, irq; | ||
217 | |||
218 | irq = irq_of_parse_and_map(node, 0); | ||
219 | if (!irq) { | ||
220 | dev_warn(gpio->dev, "no irq for bank %s\n", | ||
221 | port->bgc.gc.of_node->full_name); | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | gpio->domain = irq_domain_add_linear(node, ngpio, | ||
226 | &irq_generic_chip_ops, gpio); | ||
227 | if (!gpio->domain) | ||
228 | return; | ||
229 | |||
230 | err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 1, | ||
231 | "gpio-dwapb", handle_level_irq, | ||
232 | IRQ_NOREQUEST, 0, | ||
233 | IRQ_GC_INIT_NESTED_LOCK); | ||
234 | if (err) { | ||
235 | dev_info(gpio->dev, "irq_alloc_domain_generic_chips failed\n"); | ||
236 | irq_domain_remove(gpio->domain); | ||
237 | gpio->domain = NULL; | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | irq_gc = irq_get_domain_generic_chip(gpio->domain, 0); | ||
242 | if (!irq_gc) { | ||
243 | irq_domain_remove(gpio->domain); | ||
244 | gpio->domain = NULL; | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | irq_gc->reg_base = gpio->regs; | ||
249 | irq_gc->private = gpio; | ||
250 | |||
251 | ct = irq_gc->chip_types; | ||
252 | ct->chip.irq_ack = irq_gc_ack_set_bit; | ||
253 | ct->chip.irq_mask = irq_gc_mask_set_bit; | ||
254 | ct->chip.irq_unmask = irq_gc_mask_clr_bit; | ||
255 | ct->chip.irq_set_type = dwapb_irq_set_type; | ||
256 | ct->chip.irq_enable = dwapb_irq_enable; | ||
257 | ct->chip.irq_disable = dwapb_irq_disable; | ||
258 | ct->chip.irq_request_resources = dwapb_irq_reqres; | ||
259 | ct->chip.irq_release_resources = dwapb_irq_relres; | ||
260 | ct->regs.ack = GPIO_PORTA_EOI; | ||
261 | ct->regs.mask = GPIO_INTMASK; | ||
262 | |||
263 | irq_setup_generic_chip(irq_gc, IRQ_MSK(port->bgc.gc.ngpio), | ||
264 | IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); | ||
265 | |||
266 | irq_set_chained_handler(irq, dwapb_irq_handler); | ||
267 | irq_set_handler_data(irq, gpio); | ||
268 | |||
269 | for (hwirq = 0 ; hwirq < ngpio ; hwirq++) | ||
270 | irq_create_mapping(gpio->domain, hwirq); | ||
271 | |||
272 | port->bgc.gc.to_irq = dwapb_gpio_to_irq; | ||
273 | } | ||
274 | |||
275 | static void dwapb_irq_teardown(struct dwapb_gpio *gpio) | ||
276 | { | ||
277 | struct dwapb_gpio_port *port = &gpio->ports[0]; | ||
278 | struct gpio_chip *gc = &port->bgc.gc; | ||
279 | unsigned int ngpio = gc->ngpio; | ||
280 | irq_hw_number_t hwirq; | ||
281 | |||
282 | if (!gpio->domain) | ||
283 | return; | ||
284 | |||
285 | for (hwirq = 0 ; hwirq < ngpio ; hwirq++) | ||
286 | irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq)); | ||
287 | |||
288 | irq_domain_remove(gpio->domain); | ||
289 | gpio->domain = NULL; | ||
290 | } | ||
291 | |||
292 | static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, | ||
293 | struct device_node *port_np, | ||
294 | unsigned int offs) | ||
295 | { | ||
296 | struct dwapb_gpio_port *port; | ||
297 | u32 port_idx, ngpio; | ||
298 | void __iomem *dat, *set, *dirout; | ||
299 | int err; | ||
300 | |||
301 | if (of_property_read_u32(port_np, "reg", &port_idx) || | ||
302 | port_idx >= DWAPB_MAX_PORTS) { | ||
303 | dev_err(gpio->dev, "missing/invalid port index for %s\n", | ||
304 | port_np->full_name); | ||
305 | return -EINVAL; | ||
306 | } | ||
307 | |||
308 | port = &gpio->ports[offs]; | ||
309 | port->gpio = gpio; | ||
310 | |||
311 | if (of_property_read_u32(port_np, "snps,nr-gpios", &ngpio)) { | ||
312 | dev_info(gpio->dev, "failed to get number of gpios for %s\n", | ||
313 | port_np->full_name); | ||
314 | ngpio = 32; | ||
315 | } | ||
316 | |||
317 | dat = gpio->regs + GPIO_EXT_PORTA + (port_idx * GPIO_EXT_PORT_SIZE); | ||
318 | set = gpio->regs + GPIO_SWPORTA_DR + (port_idx * GPIO_SWPORT_DR_SIZE); | ||
319 | dirout = gpio->regs + GPIO_SWPORTA_DDR + | ||
320 | (port_idx * GPIO_SWPORT_DDR_SIZE); | ||
321 | |||
322 | err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout, | ||
323 | NULL, false); | ||
324 | if (err) { | ||
325 | dev_err(gpio->dev, "failed to init gpio chip for %s\n", | ||
326 | port_np->full_name); | ||
327 | return err; | ||
328 | } | ||
329 | |||
330 | port->bgc.gc.ngpio = ngpio; | ||
331 | port->bgc.gc.of_node = port_np; | ||
332 | |||
333 | /* | ||
334 | * Only port A can provide interrupts in all configurations of the IP. | ||
335 | */ | ||
336 | if (port_idx == 0 && | ||
337 | of_property_read_bool(port_np, "interrupt-controller")) | ||
338 | dwapb_configure_irqs(gpio, port); | ||
339 | |||
340 | err = gpiochip_add(&port->bgc.gc); | ||
341 | if (err) | ||
342 | dev_err(gpio->dev, "failed to register gpiochip for %s\n", | ||
343 | port_np->full_name); | ||
344 | else | ||
345 | port->is_registered = true; | ||
346 | |||
347 | return err; | ||
348 | } | ||
349 | |||
350 | static void dwapb_gpio_unregister(struct dwapb_gpio *gpio) | ||
351 | { | ||
352 | unsigned int m; | ||
353 | |||
354 | for (m = 0; m < gpio->nr_ports; ++m) | ||
355 | if (gpio->ports[m].is_registered) | ||
356 | WARN_ON(gpiochip_remove(&gpio->ports[m].bgc.gc)); | ||
357 | } | ||
358 | |||
359 | static int dwapb_gpio_probe(struct platform_device *pdev) | ||
360 | { | ||
361 | struct resource *res; | ||
362 | struct dwapb_gpio *gpio; | ||
363 | struct device_node *np; | ||
364 | int err; | ||
365 | unsigned int offs = 0; | ||
366 | |||
367 | gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); | ||
368 | if (!gpio) | ||
369 | return -ENOMEM; | ||
370 | gpio->dev = &pdev->dev; | ||
371 | |||
372 | gpio->nr_ports = of_get_child_count(pdev->dev.of_node); | ||
373 | if (!gpio->nr_ports) { | ||
374 | err = -EINVAL; | ||
375 | goto out_err; | ||
376 | } | ||
377 | gpio->ports = devm_kzalloc(&pdev->dev, gpio->nr_ports * | ||
378 | sizeof(*gpio->ports), GFP_KERNEL); | ||
379 | if (!gpio->ports) { | ||
380 | err = -ENOMEM; | ||
381 | goto out_err; | ||
382 | } | ||
383 | |||
384 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
385 | gpio->regs = devm_ioremap_resource(&pdev->dev, res); | ||
386 | if (IS_ERR(gpio->regs)) { | ||
387 | err = PTR_ERR(gpio->regs); | ||
388 | goto out_err; | ||
389 | } | ||
390 | |||
391 | for_each_child_of_node(pdev->dev.of_node, np) { | ||
392 | err = dwapb_gpio_add_port(gpio, np, offs++); | ||
393 | if (err) | ||
394 | goto out_unregister; | ||
395 | } | ||
396 | platform_set_drvdata(pdev, gpio); | ||
397 | |||
398 | return 0; | ||
399 | |||
400 | out_unregister: | ||
401 | dwapb_gpio_unregister(gpio); | ||
402 | dwapb_irq_teardown(gpio); | ||
403 | |||
404 | out_err: | ||
405 | return err; | ||
406 | } | ||
407 | |||
408 | static int dwapb_gpio_remove(struct platform_device *pdev) | ||
409 | { | ||
410 | struct dwapb_gpio *gpio = platform_get_drvdata(pdev); | ||
411 | |||
412 | dwapb_gpio_unregister(gpio); | ||
413 | dwapb_irq_teardown(gpio); | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static const struct of_device_id dwapb_of_match[] = { | ||
419 | { .compatible = "snps,dw-apb-gpio" }, | ||
420 | { /* Sentinel */ } | ||
421 | }; | ||
422 | MODULE_DEVICE_TABLE(of, dwapb_of_match); | ||
423 | |||
424 | static struct platform_driver dwapb_gpio_driver = { | ||
425 | .driver = { | ||
426 | .name = "gpio-dwapb", | ||
427 | .owner = THIS_MODULE, | ||
428 | .of_match_table = of_match_ptr(dwapb_of_match), | ||
429 | }, | ||
430 | .probe = dwapb_gpio_probe, | ||
431 | .remove = dwapb_gpio_remove, | ||
432 | }; | ||
433 | |||
434 | module_platform_driver(dwapb_gpio_driver); | ||
435 | |||
436 | MODULE_LICENSE("GPL"); | ||
437 | MODULE_AUTHOR("Jamie Iles"); | ||
438 | MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver"); | ||