diff options
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/pl061.c | 341 | ||||
-rw-r--r-- | include/linux/amba/pl061.h | 15 |
4 files changed, 363 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 11f373971fa5..3582c39f9725 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -67,6 +67,12 @@ config GPIO_SYSFS | |||
67 | 67 | ||
68 | comment "Memory mapped GPIO expanders:" | 68 | comment "Memory mapped GPIO expanders:" |
69 | 69 | ||
70 | config GPIO_PL061 | ||
71 | bool "PrimeCell PL061 GPIO support" | ||
72 | depends on ARM_AMBA | ||
73 | help | ||
74 | Say yes here to support the PrimeCell PL061 GPIO device | ||
75 | |||
70 | config GPIO_XILINX | 76 | config GPIO_XILINX |
71 | bool "Xilinx GPIO support" | 77 | bool "Xilinx GPIO support" |
72 | depends on PPC_OF || MICROBLAZE | 78 | depends on PPC_OF || MICROBLAZE |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 49ac64e515e6..ef90203e8f3c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_GPIO_MAX732X) += max732x.o | |||
9 | obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o | 9 | obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o |
10 | obj-$(CONFIG_GPIO_PCA953X) += pca953x.o | 10 | obj-$(CONFIG_GPIO_PCA953X) += pca953x.o |
11 | obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o | 11 | obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o |
12 | obj-$(CONFIG_GPIO_PL061) += pl061.o | ||
12 | obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o | 13 | obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o |
13 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | 14 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o |
14 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o | 15 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o |
diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c new file mode 100644 index 000000000000..aa8e7cb020d9 --- /dev/null +++ b/drivers/gpio/pl061.c | |||
@@ -0,0 +1,341 @@ | |||
1 | /* | ||
2 | * linux/drivers/gpio/pl061.c | ||
3 | * | ||
4 | * Copyright (C) 2008, 2009 Provigent Ltd. | ||
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 version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) | ||
11 | * | ||
12 | * Data sheet: ARM DDI 0190B, September 2000 | ||
13 | */ | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/ioport.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/bitops.h> | ||
22 | #include <linux/workqueue.h> | ||
23 | #include <linux/gpio.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/amba/bus.h> | ||
26 | #include <linux/amba/pl061.h> | ||
27 | |||
28 | #define GPIODIR 0x400 | ||
29 | #define GPIOIS 0x404 | ||
30 | #define GPIOIBE 0x408 | ||
31 | #define GPIOIEV 0x40C | ||
32 | #define GPIOIE 0x410 | ||
33 | #define GPIORIS 0x414 | ||
34 | #define GPIOMIS 0x418 | ||
35 | #define GPIOIC 0x41C | ||
36 | |||
37 | #define PL061_GPIO_NR 8 | ||
38 | |||
39 | struct pl061_gpio { | ||
40 | /* We use a list of pl061_gpio structs for each trigger IRQ in the main | ||
41 | * interrupts controller of the system. We need this to support systems | ||
42 | * in which more that one PL061s are connected to the same IRQ. The ISR | ||
43 | * interates through this list to find the source of the interrupt. | ||
44 | */ | ||
45 | struct list_head list; | ||
46 | |||
47 | /* Each of the two spinlocks protects a different set of hardware | ||
48 | * regiters and data structurs. This decouples the code of the IRQ from | ||
49 | * the GPIO code. This also makes the case of a GPIO routine call from | ||
50 | * the IRQ code simpler. | ||
51 | */ | ||
52 | spinlock_t lock; /* GPIO registers */ | ||
53 | spinlock_t irq_lock; /* IRQ registers */ | ||
54 | |||
55 | void __iomem *base; | ||
56 | unsigned irq_base; | ||
57 | struct gpio_chip gc; | ||
58 | }; | ||
59 | |||
60 | static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) | ||
61 | { | ||
62 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); | ||
63 | unsigned long flags; | ||
64 | unsigned char gpiodir; | ||
65 | |||
66 | if (offset >= gc->ngpio) | ||
67 | return -EINVAL; | ||
68 | |||
69 | spin_lock_irqsave(&chip->lock, flags); | ||
70 | gpiodir = readb(chip->base + GPIODIR); | ||
71 | gpiodir &= ~(1 << offset); | ||
72 | writeb(gpiodir, chip->base + GPIODIR); | ||
73 | spin_unlock_irqrestore(&chip->lock, flags); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, | ||
79 | int value) | ||
80 | { | ||
81 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); | ||
82 | unsigned long flags; | ||
83 | unsigned char gpiodir; | ||
84 | |||
85 | if (offset >= gc->ngpio) | ||
86 | return -EINVAL; | ||
87 | |||
88 | spin_lock_irqsave(&chip->lock, flags); | ||
89 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); | ||
90 | gpiodir = readb(chip->base + GPIODIR); | ||
91 | gpiodir |= 1 << offset; | ||
92 | writeb(gpiodir, chip->base + GPIODIR); | ||
93 | spin_unlock_irqrestore(&chip->lock, flags); | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int pl061_get_value(struct gpio_chip *gc, unsigned offset) | ||
99 | { | ||
100 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); | ||
101 | |||
102 | return !!readb(chip->base + (1 << (offset + 2))); | ||
103 | } | ||
104 | |||
105 | static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) | ||
106 | { | ||
107 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); | ||
108 | |||
109 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * PL061 GPIO IRQ | ||
114 | */ | ||
115 | static void pl061_irq_disable(unsigned irq) | ||
116 | { | ||
117 | struct pl061_gpio *chip = get_irq_chip_data(irq); | ||
118 | int offset = irq - chip->irq_base; | ||
119 | unsigned long flags; | ||
120 | u8 gpioie; | ||
121 | |||
122 | spin_lock_irqsave(&chip->irq_lock, flags); | ||
123 | gpioie = readb(chip->base + GPIOIE); | ||
124 | gpioie &= ~(1 << offset); | ||
125 | writeb(gpioie, chip->base + GPIOIE); | ||
126 | spin_unlock_irqrestore(&chip->irq_lock, flags); | ||
127 | } | ||
128 | |||
129 | static void pl061_irq_enable(unsigned irq) | ||
130 | { | ||
131 | struct pl061_gpio *chip = get_irq_chip_data(irq); | ||
132 | int offset = irq - chip->irq_base; | ||
133 | unsigned long flags; | ||
134 | u8 gpioie; | ||
135 | |||
136 | spin_lock_irqsave(&chip->irq_lock, flags); | ||
137 | gpioie = readb(chip->base + GPIOIE); | ||
138 | gpioie |= 1 << offset; | ||
139 | writeb(gpioie, chip->base + GPIOIE); | ||
140 | spin_unlock_irqrestore(&chip->irq_lock, flags); | ||
141 | } | ||
142 | |||
143 | static int pl061_irq_type(unsigned irq, unsigned trigger) | ||
144 | { | ||
145 | struct pl061_gpio *chip = get_irq_chip_data(irq); | ||
146 | int offset = irq - chip->irq_base; | ||
147 | unsigned long flags; | ||
148 | u8 gpiois, gpioibe, gpioiev; | ||
149 | |||
150 | if (offset < 0 || offset > PL061_GPIO_NR) | ||
151 | return -EINVAL; | ||
152 | |||
153 | spin_lock_irqsave(&chip->irq_lock, flags); | ||
154 | |||
155 | gpioiev = readb(chip->base + GPIOIEV); | ||
156 | |||
157 | gpiois = readb(chip->base + GPIOIS); | ||
158 | if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { | ||
159 | gpiois |= 1 << offset; | ||
160 | if (trigger & IRQ_TYPE_LEVEL_HIGH) | ||
161 | gpioiev |= 1 << offset; | ||
162 | else | ||
163 | gpioiev &= ~(1 << offset); | ||
164 | } else | ||
165 | gpiois &= ~(1 << offset); | ||
166 | writeb(gpiois, chip->base + GPIOIS); | ||
167 | |||
168 | gpioibe = readb(chip->base + GPIOIBE); | ||
169 | if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) | ||
170 | gpioibe |= 1 << offset; | ||
171 | else { | ||
172 | gpioibe &= ~(1 << offset); | ||
173 | if (trigger & IRQ_TYPE_EDGE_RISING) | ||
174 | gpioiev |= 1 << offset; | ||
175 | else | ||
176 | gpioiev &= ~(1 << offset); | ||
177 | } | ||
178 | writeb(gpioibe, chip->base + GPIOIBE); | ||
179 | |||
180 | writeb(gpioiev, chip->base + GPIOIEV); | ||
181 | |||
182 | spin_unlock_irqrestore(&chip->irq_lock, flags); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static struct irq_chip pl061_irqchip = { | ||
188 | .name = "GPIO", | ||
189 | .enable = pl061_irq_enable, | ||
190 | .disable = pl061_irq_disable, | ||
191 | .set_type = pl061_irq_type, | ||
192 | }; | ||
193 | |||
194 | static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) | ||
195 | { | ||
196 | struct list_head *chip_list = get_irq_chip_data(irq); | ||
197 | struct list_head *ptr; | ||
198 | struct pl061_gpio *chip; | ||
199 | |||
200 | desc->chip->ack(irq); | ||
201 | list_for_each(ptr, chip_list) { | ||
202 | unsigned long pending; | ||
203 | int gpio; | ||
204 | |||
205 | chip = list_entry(ptr, struct pl061_gpio, list); | ||
206 | pending = readb(chip->base + GPIOMIS); | ||
207 | writeb(pending, chip->base + GPIOIC); | ||
208 | |||
209 | if (pending == 0) | ||
210 | continue; | ||
211 | |||
212 | for_each_bit(gpio, &pending, PL061_GPIO_NR) | ||
213 | generic_handle_irq(gpio_to_irq(gpio)); | ||
214 | } | ||
215 | desc->chip->unmask(irq); | ||
216 | } | ||
217 | |||
218 | static int __init pl061_probe(struct amba_device *dev, struct amba_id *id) | ||
219 | { | ||
220 | struct pl061_platform_data *pdata; | ||
221 | struct pl061_gpio *chip; | ||
222 | struct list_head *chip_list; | ||
223 | int ret, irq, i; | ||
224 | static unsigned long init_irq[BITS_TO_LONGS(NR_IRQS)]; | ||
225 | |||
226 | pdata = dev->dev.platform_data; | ||
227 | if (pdata == NULL) | ||
228 | return -ENODEV; | ||
229 | |||
230 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
231 | if (chip == NULL) | ||
232 | return -ENOMEM; | ||
233 | |||
234 | if (!request_mem_region(dev->res.start, | ||
235 | resource_size(&dev->res), "pl061")) { | ||
236 | ret = -EBUSY; | ||
237 | goto free_mem; | ||
238 | } | ||
239 | |||
240 | chip->base = ioremap(dev->res.start, resource_size(&dev->res)); | ||
241 | if (chip->base == NULL) { | ||
242 | ret = -ENOMEM; | ||
243 | goto release_region; | ||
244 | } | ||
245 | |||
246 | spin_lock_init(&chip->lock); | ||
247 | spin_lock_init(&chip->irq_lock); | ||
248 | INIT_LIST_HEAD(&chip->list); | ||
249 | |||
250 | chip->gc.direction_input = pl061_direction_input; | ||
251 | chip->gc.direction_output = pl061_direction_output; | ||
252 | chip->gc.get = pl061_get_value; | ||
253 | chip->gc.set = pl061_set_value; | ||
254 | chip->gc.base = pdata->gpio_base; | ||
255 | chip->gc.ngpio = PL061_GPIO_NR; | ||
256 | chip->gc.label = dev_name(&dev->dev); | ||
257 | chip->gc.dev = &dev->dev; | ||
258 | chip->gc.owner = THIS_MODULE; | ||
259 | |||
260 | chip->irq_base = pdata->irq_base; | ||
261 | |||
262 | ret = gpiochip_add(&chip->gc); | ||
263 | if (ret) | ||
264 | goto iounmap; | ||
265 | |||
266 | /* | ||
267 | * irq_chip support | ||
268 | */ | ||
269 | |||
270 | if (chip->irq_base == (unsigned) -1) | ||
271 | return 0; | ||
272 | |||
273 | writeb(0, chip->base + GPIOIE); /* disable irqs */ | ||
274 | irq = dev->irq[0]; | ||
275 | if (irq < 0) { | ||
276 | ret = -ENODEV; | ||
277 | goto iounmap; | ||
278 | } | ||
279 | set_irq_chained_handler(irq, pl061_irq_handler); | ||
280 | if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ | ||
281 | chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); | ||
282 | if (chip_list == NULL) { | ||
283 | ret = -ENOMEM; | ||
284 | goto iounmap; | ||
285 | } | ||
286 | INIT_LIST_HEAD(chip_list); | ||
287 | set_irq_chip_data(irq, chip_list); | ||
288 | } else | ||
289 | chip_list = get_irq_chip_data(irq); | ||
290 | list_add(&chip->list, chip_list); | ||
291 | |||
292 | for (i = 0; i < PL061_GPIO_NR; i++) { | ||
293 | if (pdata->directions & (1 << i)) | ||
294 | pl061_direction_output(&chip->gc, i, | ||
295 | pdata->values & (1 << i)); | ||
296 | else | ||
297 | pl061_direction_input(&chip->gc, i); | ||
298 | |||
299 | set_irq_chip(i+chip->irq_base, &pl061_irqchip); | ||
300 | set_irq_handler(i+chip->irq_base, handle_simple_irq); | ||
301 | set_irq_flags(i+chip->irq_base, IRQF_VALID); | ||
302 | set_irq_chip_data(i+chip->irq_base, chip); | ||
303 | } | ||
304 | |||
305 | return 0; | ||
306 | |||
307 | iounmap: | ||
308 | iounmap(chip->base); | ||
309 | release_region: | ||
310 | release_mem_region(dev->res.start, resource_size(&dev->res)); | ||
311 | free_mem: | ||
312 | kfree(chip); | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | static struct amba_id pl061_ids[] __initdata = { | ||
318 | { | ||
319 | .id = 0x00041061, | ||
320 | .mask = 0x000fffff, | ||
321 | }, | ||
322 | { 0, 0 }, | ||
323 | }; | ||
324 | |||
325 | static struct amba_driver pl061_gpio_driver = { | ||
326 | .drv = { | ||
327 | .name = "pl061_gpio", | ||
328 | }, | ||
329 | .id_table = pl061_ids, | ||
330 | .probe = pl061_probe, | ||
331 | }; | ||
332 | |||
333 | static int __init pl061_gpio_init(void) | ||
334 | { | ||
335 | return amba_driver_register(&pl061_gpio_driver); | ||
336 | } | ||
337 | subsys_initcall(pl061_gpio_init); | ||
338 | |||
339 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | ||
340 | MODULE_DESCRIPTION("PL061 GPIO driver"); | ||
341 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/amba/pl061.h b/include/linux/amba/pl061.h new file mode 100644 index 000000000000..b4fbd9862606 --- /dev/null +++ b/include/linux/amba/pl061.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* platform data for the PL061 GPIO driver */ | ||
2 | |||
3 | struct pl061_platform_data { | ||
4 | /* number of the first GPIO */ | ||
5 | unsigned gpio_base; | ||
6 | |||
7 | /* number of the first IRQ. | ||
8 | * If the IRQ functionality in not desired this must be set to | ||
9 | * (unsigned) -1. | ||
10 | */ | ||
11 | unsigned irq_base; | ||
12 | |||
13 | u8 directions; /* startup directions, 1: out, 0: in */ | ||
14 | u8 values; /* startup values */ | ||
15 | }; | ||