2 * linux/drivers/gpio/pl061.c
4 * Copyright (C) 2008, 2009 Provigent Ltd.
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.
10 * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
12 * Data sheet: ARM DDI 0190B, September 2000
14 #include <linux/spinlock.h>
15 #include <linux/errno.h>
16 #include <linux/module.h>
17 #include <linux/list.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 #include <linux/slab.h>
38 #define PL061_GPIO_NR 8
41 /* We use a list of pl061_gpio structs for each trigger IRQ in the main
42 * interrupts controller of the system. We need this to support systems
43 * in which more that one PL061s are connected to the same IRQ. The ISR
44 * interates through this list to find the source of the interrupt.
46 struct list_head list
;
48 /* Each of the two spinlocks protects a different set of hardware
49 * regiters and data structurs. This decouples the code of the IRQ from
50 * the GPIO code. This also makes the case of a GPIO routine call from
51 * the IRQ code simpler.
53 spinlock_t lock
; /* GPIO registers */
54 spinlock_t irq_lock
; /* IRQ registers */
61 static int pl061_direction_input(struct gpio_chip
*gc
, unsigned offset
)
63 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
65 unsigned char gpiodir
;
67 if (offset
>= gc
->ngpio
)
70 spin_lock_irqsave(&chip
->lock
, flags
);
71 gpiodir
= readb(chip
->base
+ GPIODIR
);
72 gpiodir
&= ~(1 << offset
);
73 writeb(gpiodir
, chip
->base
+ GPIODIR
);
74 spin_unlock_irqrestore(&chip
->lock
, flags
);
79 static int pl061_direction_output(struct gpio_chip
*gc
, unsigned offset
,
82 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
84 unsigned char gpiodir
;
86 if (offset
>= gc
->ngpio
)
89 spin_lock_irqsave(&chip
->lock
, flags
);
90 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
91 gpiodir
= readb(chip
->base
+ GPIODIR
);
92 gpiodir
|= 1 << offset
;
93 writeb(gpiodir
, chip
->base
+ GPIODIR
);
96 * gpio value is set again, because pl061 doesn't allow to set value of
97 * a gpio pin before configuring it in OUT mode.
99 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
100 spin_unlock_irqrestore(&chip
->lock
, flags
);
105 static int pl061_get_value(struct gpio_chip
*gc
, unsigned offset
)
107 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
109 return !!readb(chip
->base
+ (1 << (offset
+ 2)));
112 static void pl061_set_value(struct gpio_chip
*gc
, unsigned offset
, int value
)
114 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
116 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
119 static int pl061_to_irq(struct gpio_chip
*gc
, unsigned offset
)
121 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
123 if (chip
->irq_base
== (unsigned) -1)
126 return chip
->irq_base
+ offset
;
132 static void pl061_irq_disable(unsigned irq
)
134 struct pl061_gpio
*chip
= get_irq_chip_data(irq
);
135 int offset
= irq
- chip
->irq_base
;
139 spin_lock_irqsave(&chip
->irq_lock
, flags
);
140 gpioie
= readb(chip
->base
+ GPIOIE
);
141 gpioie
&= ~(1 << offset
);
142 writeb(gpioie
, chip
->base
+ GPIOIE
);
143 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
146 static void pl061_irq_enable(unsigned irq
)
148 struct pl061_gpio
*chip
= get_irq_chip_data(irq
);
149 int offset
= irq
- chip
->irq_base
;
153 spin_lock_irqsave(&chip
->irq_lock
, flags
);
154 gpioie
= readb(chip
->base
+ GPIOIE
);
155 gpioie
|= 1 << offset
;
156 writeb(gpioie
, chip
->base
+ GPIOIE
);
157 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
160 static int pl061_irq_type(unsigned irq
, unsigned trigger
)
162 struct pl061_gpio
*chip
= get_irq_chip_data(irq
);
163 int offset
= irq
- chip
->irq_base
;
165 u8 gpiois
, gpioibe
, gpioiev
;
167 if (offset
< 0 || offset
>= PL061_GPIO_NR
)
170 spin_lock_irqsave(&chip
->irq_lock
, flags
);
172 gpioiev
= readb(chip
->base
+ GPIOIEV
);
174 gpiois
= readb(chip
->base
+ GPIOIS
);
175 if (trigger
& (IRQ_TYPE_LEVEL_HIGH
| IRQ_TYPE_LEVEL_LOW
)) {
176 gpiois
|= 1 << offset
;
177 if (trigger
& IRQ_TYPE_LEVEL_HIGH
)
178 gpioiev
|= 1 << offset
;
180 gpioiev
&= ~(1 << offset
);
182 gpiois
&= ~(1 << offset
);
183 writeb(gpiois
, chip
->base
+ GPIOIS
);
185 gpioibe
= readb(chip
->base
+ GPIOIBE
);
186 if ((trigger
& IRQ_TYPE_EDGE_BOTH
) == IRQ_TYPE_EDGE_BOTH
)
187 gpioibe
|= 1 << offset
;
189 gpioibe
&= ~(1 << offset
);
190 if (trigger
& IRQ_TYPE_EDGE_RISING
)
191 gpioiev
|= 1 << offset
;
192 else if (trigger
& IRQ_TYPE_EDGE_FALLING
)
193 gpioiev
&= ~(1 << offset
);
195 writeb(gpioibe
, chip
->base
+ GPIOIBE
);
197 writeb(gpioiev
, chip
->base
+ GPIOIEV
);
199 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
204 static struct irq_chip pl061_irqchip
= {
206 .enable
= pl061_irq_enable
,
207 .disable
= pl061_irq_disable
,
208 .set_type
= pl061_irq_type
,
211 static void pl061_irq_handler(unsigned irq
, struct irq_desc
*desc
)
213 struct list_head
*chip_list
= get_irq_data(irq
);
214 struct list_head
*ptr
;
215 struct pl061_gpio
*chip
;
217 desc
->chip
->ack(irq
);
218 list_for_each(ptr
, chip_list
) {
219 unsigned long pending
;
222 chip
= list_entry(ptr
, struct pl061_gpio
, list
);
223 pending
= readb(chip
->base
+ GPIOMIS
);
224 writeb(pending
, chip
->base
+ GPIOIC
);
229 for_each_set_bit(offset
, &pending
, PL061_GPIO_NR
)
230 generic_handle_irq(pl061_to_irq(&chip
->gc
, offset
));
232 desc
->chip
->unmask(irq
);
235 static int pl061_probe(struct amba_device
*dev
, struct amba_id
*id
)
237 struct pl061_platform_data
*pdata
;
238 struct pl061_gpio
*chip
;
239 struct list_head
*chip_list
;
241 static DECLARE_BITMAP(init_irq
, NR_IRQS
);
243 pdata
= dev
->dev
.platform_data
;
247 chip
= kzalloc(sizeof(*chip
), GFP_KERNEL
);
251 if (!request_mem_region(dev
->res
.start
,
252 resource_size(&dev
->res
), "pl061")) {
257 chip
->base
= ioremap(dev
->res
.start
, resource_size(&dev
->res
));
258 if (chip
->base
== NULL
) {
263 spin_lock_init(&chip
->lock
);
264 spin_lock_init(&chip
->irq_lock
);
265 INIT_LIST_HEAD(&chip
->list
);
267 chip
->gc
.direction_input
= pl061_direction_input
;
268 chip
->gc
.direction_output
= pl061_direction_output
;
269 chip
->gc
.get
= pl061_get_value
;
270 chip
->gc
.set
= pl061_set_value
;
271 chip
->gc
.to_irq
= pl061_to_irq
;
272 chip
->gc
.base
= pdata
->gpio_base
;
273 chip
->gc
.ngpio
= PL061_GPIO_NR
;
274 chip
->gc
.label
= dev_name(&dev
->dev
);
275 chip
->gc
.dev
= &dev
->dev
;
276 chip
->gc
.owner
= THIS_MODULE
;
278 chip
->irq_base
= pdata
->irq_base
;
280 ret
= gpiochip_add(&chip
->gc
);
288 if (chip
->irq_base
== (unsigned) -1)
291 writeb(0, chip
->base
+ GPIOIE
); /* disable irqs */
297 set_irq_chained_handler(irq
, pl061_irq_handler
);
298 if (!test_and_set_bit(irq
, init_irq
)) { /* list initialized? */
299 chip_list
= kmalloc(sizeof(*chip_list
), GFP_KERNEL
);
300 if (chip_list
== NULL
) {
301 clear_bit(irq
, init_irq
);
305 INIT_LIST_HEAD(chip_list
);
306 set_irq_data(irq
, chip_list
);
308 chip_list
= get_irq_data(irq
);
309 list_add(&chip
->list
, chip_list
);
311 for (i
= 0; i
< PL061_GPIO_NR
; i
++) {
312 if (pdata
->directions
& (1 << i
))
313 pl061_direction_output(&chip
->gc
, i
,
314 pdata
->values
& (1 << i
));
316 pl061_direction_input(&chip
->gc
, i
);
318 set_irq_chip(i
+chip
->irq_base
, &pl061_irqchip
);
319 set_irq_handler(i
+chip
->irq_base
, handle_simple_irq
);
320 set_irq_flags(i
+chip
->irq_base
, IRQF_VALID
);
321 set_irq_chip_data(i
+chip
->irq_base
, chip
);
329 release_mem_region(dev
->res
.start
, resource_size(&dev
->res
));
336 static struct amba_id pl061_ids
[] = {
344 static struct amba_driver pl061_gpio_driver
= {
346 .name
= "pl061_gpio",
348 .id_table
= pl061_ids
,
349 .probe
= pl061_probe
,
352 static int __init
pl061_gpio_init(void)
354 return amba_driver_register(&pl061_gpio_driver
);
356 subsys_initcall(pl061_gpio_init
);
358 MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
359 MODULE_DESCRIPTION("PL061 GPIO driver");
360 MODULE_LICENSE("GPL");