2 * Copyright (C) 2008, 2009 Provigent Ltd.
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.
8 * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
10 * Data sheet: ARM DDI 0190B, September 2000
12 #include <linux/spinlock.h>
13 #include <linux/errno.h>
14 #include <linux/module.h>
15 #include <linux/list.h>
17 #include <linux/ioport.h>
18 #include <linux/irq.h>
19 #include <linux/bitops.h>
20 #include <linux/workqueue.h>
21 #include <linux/gpio.h>
22 #include <linux/device.h>
23 #include <linux/amba/bus.h>
24 #include <linux/amba/pl061.h>
25 #include <linux/slab.h>
36 #define PL061_GPIO_NR 8
39 /* We use a list of pl061_gpio structs for each trigger IRQ in the main
40 * interrupts controller of the system. We need this to support systems
41 * in which more that one PL061s are connected to the same IRQ. The ISR
42 * interates through this list to find the source of the interrupt.
44 struct list_head list
;
46 /* Each of the two spinlocks protects a different set of hardware
47 * regiters and data structurs. This decouples the code of the IRQ from
48 * the GPIO code. This also makes the case of a GPIO routine call from
49 * the IRQ code simpler.
51 spinlock_t lock
; /* GPIO registers */
52 spinlock_t irq_lock
; /* IRQ registers */
59 static int pl061_direction_input(struct gpio_chip
*gc
, unsigned offset
)
61 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
63 unsigned char gpiodir
;
65 if (offset
>= gc
->ngpio
)
68 spin_lock_irqsave(&chip
->lock
, flags
);
69 gpiodir
= readb(chip
->base
+ GPIODIR
);
70 gpiodir
&= ~(1 << offset
);
71 writeb(gpiodir
, chip
->base
+ GPIODIR
);
72 spin_unlock_irqrestore(&chip
->lock
, flags
);
77 static int pl061_direction_output(struct gpio_chip
*gc
, unsigned offset
,
80 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
82 unsigned char gpiodir
;
84 if (offset
>= gc
->ngpio
)
87 spin_lock_irqsave(&chip
->lock
, flags
);
88 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
89 gpiodir
= readb(chip
->base
+ GPIODIR
);
90 gpiodir
|= 1 << offset
;
91 writeb(gpiodir
, chip
->base
+ GPIODIR
);
94 * gpio value is set again, because pl061 doesn't allow to set value of
95 * a gpio pin before configuring it in OUT mode.
97 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
98 spin_unlock_irqrestore(&chip
->lock
, flags
);
103 static int pl061_get_value(struct gpio_chip
*gc
, unsigned offset
)
105 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
107 return !!readb(chip
->base
+ (1 << (offset
+ 2)));
110 static void pl061_set_value(struct gpio_chip
*gc
, unsigned offset
, int value
)
112 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
114 writeb(!!value
<< offset
, chip
->base
+ (1 << (offset
+ 2)));
117 static int pl061_to_irq(struct gpio_chip
*gc
, unsigned offset
)
119 struct pl061_gpio
*chip
= container_of(gc
, struct pl061_gpio
, gc
);
121 if (chip
->irq_base
== (unsigned) -1)
124 return chip
->irq_base
+ offset
;
130 static void pl061_irq_disable(struct irq_data
*d
)
132 struct pl061_gpio
*chip
= irq_data_get_irq_chip_data(d
);
133 int offset
= d
->irq
- chip
->irq_base
;
137 spin_lock_irqsave(&chip
->irq_lock
, flags
);
138 gpioie
= readb(chip
->base
+ GPIOIE
);
139 gpioie
&= ~(1 << offset
);
140 writeb(gpioie
, chip
->base
+ GPIOIE
);
141 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
144 static void pl061_irq_enable(struct irq_data
*d
)
146 struct pl061_gpio
*chip
= irq_data_get_irq_chip_data(d
);
147 int offset
= d
->irq
- chip
->irq_base
;
151 spin_lock_irqsave(&chip
->irq_lock
, flags
);
152 gpioie
= readb(chip
->base
+ GPIOIE
);
153 gpioie
|= 1 << offset
;
154 writeb(gpioie
, chip
->base
+ GPIOIE
);
155 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
158 static int pl061_irq_type(struct irq_data
*d
, unsigned trigger
)
160 struct pl061_gpio
*chip
= irq_data_get_irq_chip_data(d
);
161 int offset
= d
->irq
- chip
->irq_base
;
163 u8 gpiois
, gpioibe
, gpioiev
;
165 if (offset
< 0 || offset
>= PL061_GPIO_NR
)
168 spin_lock_irqsave(&chip
->irq_lock
, flags
);
170 gpioiev
= readb(chip
->base
+ GPIOIEV
);
172 gpiois
= readb(chip
->base
+ GPIOIS
);
173 if (trigger
& (IRQ_TYPE_LEVEL_HIGH
| IRQ_TYPE_LEVEL_LOW
)) {
174 gpiois
|= 1 << offset
;
175 if (trigger
& IRQ_TYPE_LEVEL_HIGH
)
176 gpioiev
|= 1 << offset
;
178 gpioiev
&= ~(1 << offset
);
180 gpiois
&= ~(1 << offset
);
181 writeb(gpiois
, chip
->base
+ GPIOIS
);
183 gpioibe
= readb(chip
->base
+ GPIOIBE
);
184 if ((trigger
& IRQ_TYPE_EDGE_BOTH
) == IRQ_TYPE_EDGE_BOTH
)
185 gpioibe
|= 1 << offset
;
187 gpioibe
&= ~(1 << offset
);
188 if (trigger
& IRQ_TYPE_EDGE_RISING
)
189 gpioiev
|= 1 << offset
;
190 else if (trigger
& IRQ_TYPE_EDGE_FALLING
)
191 gpioiev
&= ~(1 << offset
);
193 writeb(gpioibe
, chip
->base
+ GPIOIBE
);
195 writeb(gpioiev
, chip
->base
+ GPIOIEV
);
197 spin_unlock_irqrestore(&chip
->irq_lock
, flags
);
202 static struct irq_chip pl061_irqchip
= {
204 .irq_enable
= pl061_irq_enable
,
205 .irq_disable
= pl061_irq_disable
,
206 .irq_set_type
= pl061_irq_type
,
209 static void pl061_irq_handler(unsigned irq
, struct irq_desc
*desc
)
211 struct list_head
*chip_list
= irq_get_handler_data(irq
);
212 struct list_head
*ptr
;
213 struct pl061_gpio
*chip
;
215 desc
->irq_data
.chip
->irq_ack(&desc
->irq_data
);
216 list_for_each(ptr
, chip_list
) {
217 unsigned long pending
;
220 chip
= list_entry(ptr
, struct pl061_gpio
, list
);
221 pending
= readb(chip
->base
+ GPIOMIS
);
222 writeb(pending
, chip
->base
+ GPIOIC
);
227 for_each_set_bit(offset
, &pending
, PL061_GPIO_NR
)
228 generic_handle_irq(pl061_to_irq(&chip
->gc
, offset
));
230 desc
->irq_data
.chip
->irq_unmask(&desc
->irq_data
);
233 static int pl061_probe(struct amba_device
*dev
, const struct amba_id
*id
)
235 struct pl061_platform_data
*pdata
;
236 struct pl061_gpio
*chip
;
237 struct list_head
*chip_list
;
239 static DECLARE_BITMAP(init_irq
, NR_IRQS
);
241 pdata
= dev
->dev
.platform_data
;
245 chip
= kzalloc(sizeof(*chip
), GFP_KERNEL
);
249 if (!request_mem_region(dev
->res
.start
,
250 resource_size(&dev
->res
), "pl061")) {
255 chip
->base
= ioremap(dev
->res
.start
, resource_size(&dev
->res
));
256 if (chip
->base
== NULL
) {
261 spin_lock_init(&chip
->lock
);
262 spin_lock_init(&chip
->irq_lock
);
263 INIT_LIST_HEAD(&chip
->list
);
265 chip
->gc
.direction_input
= pl061_direction_input
;
266 chip
->gc
.direction_output
= pl061_direction_output
;
267 chip
->gc
.get
= pl061_get_value
;
268 chip
->gc
.set
= pl061_set_value
;
269 chip
->gc
.to_irq
= pl061_to_irq
;
270 chip
->gc
.base
= pdata
->gpio_base
;
271 chip
->gc
.ngpio
= PL061_GPIO_NR
;
272 chip
->gc
.label
= dev_name(&dev
->dev
);
273 chip
->gc
.dev
= &dev
->dev
;
274 chip
->gc
.owner
= THIS_MODULE
;
276 chip
->irq_base
= pdata
->irq_base
;
278 ret
= gpiochip_add(&chip
->gc
);
286 if (chip
->irq_base
== (unsigned) -1)
289 writeb(0, chip
->base
+ GPIOIE
); /* disable irqs */
295 irq_set_chained_handler(irq
, pl061_irq_handler
);
296 if (!test_and_set_bit(irq
, init_irq
)) { /* list initialized? */
297 chip_list
= kmalloc(sizeof(*chip_list
), GFP_KERNEL
);
298 if (chip_list
== NULL
) {
299 clear_bit(irq
, init_irq
);
303 INIT_LIST_HEAD(chip_list
);
304 irq_set_handler_data(irq
, chip_list
);
306 chip_list
= irq_get_handler_data(irq
);
307 list_add(&chip
->list
, chip_list
);
309 for (i
= 0; i
< PL061_GPIO_NR
; i
++) {
310 if (pdata
->directions
& (1 << i
))
311 pl061_direction_output(&chip
->gc
, i
,
312 pdata
->values
& (1 << i
));
314 pl061_direction_input(&chip
->gc
, i
);
316 irq_set_chip_and_handler(i
+ chip
->irq_base
, &pl061_irqchip
,
318 set_irq_flags(i
+chip
->irq_base
, IRQF_VALID
);
319 irq_set_chip_data(i
+ chip
->irq_base
, chip
);
327 release_mem_region(dev
->res
.start
, resource_size(&dev
->res
));
334 static struct amba_id pl061_ids
[] = {
342 static struct amba_driver pl061_gpio_driver
= {
344 .name
= "pl061_gpio",
346 .id_table
= pl061_ids
,
347 .probe
= pl061_probe
,
350 static int __init
pl061_gpio_init(void)
352 return amba_driver_register(&pl061_gpio_driver
);
354 subsys_initcall(pl061_gpio_init
);
356 MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
357 MODULE_DESCRIPTION("PL061 GPIO driver");
358 MODULE_LICENSE("GPL");