aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorMagnus Damm <damm@opensource.se>2013-03-13 07:32:13 -0400
committerSimon Horman <horms+renesas@verge.net.au>2013-04-02 21:30:24 -0400
commit119f5e448d32c11faf22fe81f6f2d78467a47149 (patch)
tree5f300b87c8cb92f6db9de3298637d4afaab2e7e8 /drivers/gpio
parent87bd63bfcf177daa272432482c17195f3c0ebb21 (diff)
gpio: Renesas R-Car GPIO driver V3
This patch is V3 of a GPIO driver for the R-Car series of SoCs from Renesas. This driver is designed to be reusable between multiple SoCs that share the same basic building block, but so far it has only been used on R-Car H1 (r8a7779). Each driver instance handles 32 GPIOs with individually maskable IRQs. The driver operates on a single I/O memory range and the 32 GPIOs are hooked up a single interrupt. In the case of R-Car H1 either external IRQ pins or GPIOs with interrupts can be used for on-board interupts. For external IRQs 4 pins are supported, and in the case of GPIO there are 202 GPIOS as 202 interrupts hooked up via 6 driver instances and to the GIC and the Cortex-A9 Quad. At this point this driver is interfacing as a regular platform device driver. In the future DT support will be submitted as an incremental feature patch. Signed-off-by: Magnus Damm <damm@opensource.se> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-rcar.c373
3 files changed, 380 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93aaadf99f28..d766e3cbef18 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -204,6 +204,12 @@ config GPIO_PXA
204 help 204 help
205 Say yes here to support the PXA GPIO device 205 Say yes here to support the PXA GPIO device
206 206
207config GPIO_RCAR
208 tristate "Renesas R-Car GPIO"
209 depends on ARM
210 help
211 Say yes here to support GPIO on Renesas R-Car SoCs.
212
207config GPIO_SPEAR_SPICS 213config GPIO_SPEAR_SPICS
208 bool "ST SPEAr13xx SPI Chip Select as GPIO support" 214 bool "ST SPEAr13xx SPI Chip Select as GPIO support"
209 depends on PLAT_SPEAR 215 depends on PLAT_SPEAR
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 22e07bc9fcb5..b41c74d45287 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
57obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o 57obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
58obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o 58obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
59obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o 59obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
60obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
60obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o 61obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
61obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o 62obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
62obj-$(CONFIG_GPIO_SCH) += gpio-sch.o 63obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
new file mode 100644
index 000000000000..581ba56131a7
--- /dev/null
+++ b/drivers/gpio/gpio-rcar.c
@@ -0,0 +1,373 @@
1/*
2 * Renesas R-Car GPIO Support
3 *
4 * Copyright (C) 2013 Magnus Damm
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/err.h>
17#include <linux/gpio.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/io.h>
21#include <linux/ioport.h>
22#include <linux/irq.h>
23#include <linux/irqdomain.h>
24#include <linux/module.h>
25#include <linux/platform_data/gpio-rcar.h>
26#include <linux/platform_device.h>
27#include <linux/spinlock.h>
28#include <linux/slab.h>
29
30struct gpio_rcar_priv {
31 void __iomem *base;
32 spinlock_t lock;
33 struct gpio_rcar_config config;
34 struct platform_device *pdev;
35 struct gpio_chip gpio_chip;
36 struct irq_chip irq_chip;
37 struct irq_domain *irq_domain;
38};
39
40#define IOINTSEL 0x00
41#define INOUTSEL 0x04
42#define OUTDT 0x08
43#define INDT 0x0c
44#define INTDT 0x10
45#define INTCLR 0x14
46#define INTMSK 0x18
47#define MSKCLR 0x1c
48#define POSNEG 0x20
49#define EDGLEVEL 0x24
50#define FILONOFF 0x28
51
52static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
53{
54 return ioread32(p->base + offs);
55}
56
57static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs,
58 u32 value)
59{
60 iowrite32(value, p->base + offs);
61}
62
63static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs,
64 int bit, bool value)
65{
66 u32 tmp = gpio_rcar_read(p, offs);
67
68 if (value)
69 tmp |= BIT(bit);
70 else
71 tmp &= ~BIT(bit);
72
73 gpio_rcar_write(p, offs, tmp);
74}
75
76static void gpio_rcar_irq_disable(struct irq_data *d)
77{
78 struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
79
80 gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d)));
81}
82
83static void gpio_rcar_irq_enable(struct irq_data *d)
84{
85 struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
86
87 gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d)));
88}
89
90static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
91 unsigned int hwirq,
92 bool active_high_rising_edge,
93 bool level_trigger)
94{
95 unsigned long flags;
96
97 /* follow steps in the GPIO documentation for
98 * "Setting Edge-Sensitive Interrupt Input Mode" and
99 * "Setting Level-Sensitive Interrupt Input Mode"
100 */
101
102 spin_lock_irqsave(&p->lock, flags);
103
104 /* Configure postive or negative logic in POSNEG */
105 gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
106
107 /* Configure edge or level trigger in EDGLEVEL */
108 gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger);
109
110 /* Select "Interrupt Input Mode" in IOINTSEL */
111 gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true);
112
113 /* Write INTCLR in case of edge trigger */
114 if (!level_trigger)
115 gpio_rcar_write(p, INTCLR, BIT(hwirq));
116
117 spin_unlock_irqrestore(&p->lock, flags);
118}
119
120static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
121{
122 struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
123 unsigned int hwirq = irqd_to_hwirq(d);
124
125 dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type);
126
127 switch (type & IRQ_TYPE_SENSE_MASK) {
128 case IRQ_TYPE_LEVEL_HIGH:
129 gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true);
130 break;
131 case IRQ_TYPE_LEVEL_LOW:
132 gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true);
133 break;
134 case IRQ_TYPE_EDGE_RISING:
135 gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false);
136 break;
137 case IRQ_TYPE_EDGE_FALLING:
138 gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false);
139 break;
140 default:
141 return -EINVAL;
142 }
143 return 0;
144}
145
146static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
147{
148 struct gpio_rcar_priv *p = dev_id;
149 u32 pending;
150 unsigned int offset, irqs_handled = 0;
151
152 while ((pending = gpio_rcar_read(p, INTDT))) {
153 offset = __ffs(pending);
154 gpio_rcar_write(p, INTCLR, BIT(offset));
155 generic_handle_irq(irq_find_mapping(p->irq_domain, offset));
156 irqs_handled++;
157 }
158
159 return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
160}
161
162static inline struct gpio_rcar_priv *gpio_to_priv(struct gpio_chip *chip)
163{
164 return container_of(chip, struct gpio_rcar_priv, gpio_chip);
165}
166
167static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
168 unsigned int gpio,
169 bool output)
170{
171 struct gpio_rcar_priv *p = gpio_to_priv(chip);
172 unsigned long flags;
173
174 /* follow steps in the GPIO documentation for
175 * "Setting General Output Mode" and
176 * "Setting General Input Mode"
177 */
178
179 spin_lock_irqsave(&p->lock, flags);
180
181 /* Configure postive logic in POSNEG */
182 gpio_rcar_modify_bit(p, POSNEG, gpio, false);
183
184 /* Select "General Input/Output Mode" in IOINTSEL */
185 gpio_rcar_modify_bit(p, IOINTSEL, gpio, false);
186
187 /* Select Input Mode or Output Mode in INOUTSEL */
188 gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
189
190 spin_unlock_irqrestore(&p->lock, flags);
191}
192
193static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
194{
195 gpio_rcar_config_general_input_output_mode(chip, offset, false);
196 return 0;
197}
198
199static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
200{
201 return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & BIT(offset));
202}
203
204static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)
205{
206 struct gpio_rcar_priv *p = gpio_to_priv(chip);
207 unsigned long flags;
208
209 spin_lock_irqsave(&p->lock, flags);
210 gpio_rcar_modify_bit(p, OUTDT, offset, value);
211 spin_unlock_irqrestore(&p->lock, flags);
212}
213
214static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
215 int value)
216{
217 /* write GPIO value to output before selecting output mode of pin */
218 gpio_rcar_set(chip, offset, value);
219 gpio_rcar_config_general_input_output_mode(chip, offset, true);
220 return 0;
221}
222
223static int gpio_rcar_to_irq(struct gpio_chip *chip, unsigned offset)
224{
225 return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
226}
227
228static int gpio_rcar_irq_domain_map(struct irq_domain *h, unsigned int virq,
229 irq_hw_number_t hw)
230{
231 struct gpio_rcar_priv *p = h->host_data;
232
233 dev_dbg(&p->pdev->dev, "map hw irq = %d, virq = %d\n", (int)hw, virq);
234
235 irq_set_chip_data(virq, h->host_data);
236 irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
237 set_irq_flags(virq, IRQF_VALID); /* kill me now */
238 return 0;
239}
240
241static struct irq_domain_ops gpio_rcar_irq_domain_ops = {
242 .map = gpio_rcar_irq_domain_map,
243};
244
245static int gpio_rcar_probe(struct platform_device *pdev)
246{
247 struct gpio_rcar_config *pdata = pdev->dev.platform_data;
248 struct gpio_rcar_priv *p;
249 struct resource *io, *irq;
250 struct gpio_chip *gpio_chip;
251 struct irq_chip *irq_chip;
252 const char *name = dev_name(&pdev->dev);
253 int ret;
254
255 p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
256 if (!p) {
257 dev_err(&pdev->dev, "failed to allocate driver data\n");
258 ret = -ENOMEM;
259 goto err0;
260 }
261
262 /* deal with driver instance configuration */
263 if (pdata)
264 p->config = *pdata;
265
266 p->pdev = pdev;
267 platform_set_drvdata(pdev, p);
268 spin_lock_init(&p->lock);
269
270 io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
271 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
272
273 if (!io || !irq) {
274 dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
275 ret = -EINVAL;
276 goto err0;
277 }
278
279 p->base = devm_ioremap_nocache(&pdev->dev, io->start,
280 resource_size(io));
281 if (!p->base) {
282 dev_err(&pdev->dev, "failed to remap I/O memory\n");
283 ret = -ENXIO;
284 goto err0;
285 }
286
287 gpio_chip = &p->gpio_chip;
288 gpio_chip->direction_input = gpio_rcar_direction_input;
289 gpio_chip->get = gpio_rcar_get;
290 gpio_chip->direction_output = gpio_rcar_direction_output;
291 gpio_chip->set = gpio_rcar_set;
292 gpio_chip->to_irq = gpio_rcar_to_irq;
293 gpio_chip->label = name;
294 gpio_chip->owner = THIS_MODULE;
295 gpio_chip->base = p->config.gpio_base;
296 gpio_chip->ngpio = p->config.number_of_pins;
297
298 irq_chip = &p->irq_chip;
299 irq_chip->name = name;
300 irq_chip->irq_mask = gpio_rcar_irq_disable;
301 irq_chip->irq_unmask = gpio_rcar_irq_enable;
302 irq_chip->irq_enable = gpio_rcar_irq_enable;
303 irq_chip->irq_disable = gpio_rcar_irq_disable;
304 irq_chip->irq_set_type = gpio_rcar_irq_set_type;
305 irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SET_TYPE_MASKED;
306
307 p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
308 p->config.number_of_pins,
309 p->config.irq_base,
310 &gpio_rcar_irq_domain_ops, p);
311 if (!p->irq_domain) {
312 ret = -ENXIO;
313 dev_err(&pdev->dev, "cannot initialize irq domain\n");
314 goto err1;
315 }
316
317 if (devm_request_irq(&pdev->dev, irq->start,
318 gpio_rcar_irq_handler, 0, name, p)) {
319 dev_err(&pdev->dev, "failed to request IRQ\n");
320 ret = -ENOENT;
321 goto err1;
322 }
323
324 ret = gpiochip_add(gpio_chip);
325 if (ret) {
326 dev_err(&pdev->dev, "failed to add GPIO controller\n");
327 goto err1;
328 }
329
330 dev_info(&pdev->dev, "driving %d GPIOs\n", p->config.number_of_pins);
331
332 /* warn in case of mismatch if irq base is specified */
333 if (p->config.irq_base) {
334 ret = irq_find_mapping(p->irq_domain, 0);
335 if (p->config.irq_base != ret)
336 dev_warn(&pdev->dev, "irq base mismatch (%u/%u)\n",
337 p->config.irq_base, ret);
338 }
339
340 return 0;
341
342err1:
343 irq_domain_remove(p->irq_domain);
344err0:
345 return ret;
346}
347
348static int gpio_rcar_remove(struct platform_device *pdev)
349{
350 struct gpio_rcar_priv *p = platform_get_drvdata(pdev);
351 int ret;
352
353 ret = gpiochip_remove(&p->gpio_chip);
354 if (ret)
355 return ret;
356
357 irq_domain_remove(p->irq_domain);
358 return 0;
359}
360
361static struct platform_driver gpio_rcar_device_driver = {
362 .probe = gpio_rcar_probe,
363 .remove = gpio_rcar_remove,
364 .driver = {
365 .name = "gpio_rcar",
366 }
367};
368
369module_platform_driver(gpio_rcar_device_driver);
370
371MODULE_AUTHOR("Magnus Damm");
372MODULE_DESCRIPTION("Renesas R-Car GPIO Driver");
373MODULE_LICENSE("GPL v2");