diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-14 16:25:23 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-14 16:25:23 -0500 |
commit | f5e4e20faa1eee3feaa0394897bbd1aca544e809 (patch) | |
tree | 047a93ff025c46ed97d3192a79f55b38fa071ca1 /drivers/gpio/gpio-pl061.c | |
parent | 4964e0664c80680fa6b28ef91381c076a5b25c2c (diff) | |
parent | f408c985cefc9b1d99bc099e1208dd7df3445aa5 (diff) |
Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6
2nd round of GPIO changes for v3.3 merge window
* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6:
GPIO: sa1100: implement proper gpiolib gpio_to_irq conversion
gpio: pl061: remove combined interrupt
gpio: pl061: convert to use generic irq chip
GPIO: add bindings for managed devices
ARM: realview: convert pl061 no irq to 0 instead of -1
gpio: pl061: convert to use 0 for no irq
gpio: pl061: use chained_irq_* functions in irq handler
GPIO/pl061: Add suspend resume capability
drivers/gpio/gpio-tegra.c: use devm_request_and_ioremap
Diffstat (limited to 'drivers/gpio/gpio-pl061.c')
-rw-r--r-- | drivers/gpio/gpio-pl061.c | 200 |
1 files changed, 110 insertions, 90 deletions
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 8f79c03049f3..77c9cc70fa77 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include <linux/spinlock.h> | 12 | #include <linux/spinlock.h> |
13 | #include <linux/errno.h> | 13 | #include <linux/errno.h> |
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/list.h> | ||
16 | #include <linux/io.h> | 15 | #include <linux/io.h> |
17 | #include <linux/ioport.h> | 16 | #include <linux/ioport.h> |
18 | #include <linux/irq.h> | 17 | #include <linux/irq.h> |
@@ -23,6 +22,8 @@ | |||
23 | #include <linux/amba/bus.h> | 22 | #include <linux/amba/bus.h> |
24 | #include <linux/amba/pl061.h> | 23 | #include <linux/amba/pl061.h> |
25 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/pm.h> | ||
26 | #include <asm/mach/irq.h> | ||
26 | 27 | ||
27 | #define GPIODIR 0x400 | 28 | #define GPIODIR 0x400 |
28 | #define GPIOIS 0x404 | 29 | #define GPIOIS 0x404 |
@@ -35,25 +36,33 @@ | |||
35 | 36 | ||
36 | #define PL061_GPIO_NR 8 | 37 | #define PL061_GPIO_NR 8 |
37 | 38 | ||
38 | struct pl061_gpio { | 39 | #ifdef CONFIG_PM |
39 | /* We use a list of pl061_gpio structs for each trigger IRQ in the main | 40 | struct pl061_context_save_regs { |
40 | * interrupts controller of the system. We need this to support systems | 41 | u8 gpio_data; |
41 | * in which more that one PL061s are connected to the same IRQ. The ISR | 42 | u8 gpio_dir; |
42 | * interates through this list to find the source of the interrupt. | 43 | u8 gpio_is; |
43 | */ | 44 | u8 gpio_ibe; |
44 | struct list_head list; | 45 | u8 gpio_iev; |
46 | u8 gpio_ie; | ||
47 | }; | ||
48 | #endif | ||
45 | 49 | ||
50 | struct pl061_gpio { | ||
46 | /* Each of the two spinlocks protects a different set of hardware | 51 | /* Each of the two spinlocks protects a different set of hardware |
47 | * regiters and data structurs. This decouples the code of the IRQ from | 52 | * 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 | 53 | * the GPIO code. This also makes the case of a GPIO routine call from |
49 | * the IRQ code simpler. | 54 | * the IRQ code simpler. |
50 | */ | 55 | */ |
51 | spinlock_t lock; /* GPIO registers */ | 56 | spinlock_t lock; /* GPIO registers */ |
52 | spinlock_t irq_lock; /* IRQ registers */ | ||
53 | 57 | ||
54 | void __iomem *base; | 58 | void __iomem *base; |
55 | unsigned irq_base; | 59 | int irq_base; |
60 | struct irq_chip_generic *irq_gc; | ||
56 | struct gpio_chip gc; | 61 | struct gpio_chip gc; |
62 | |||
63 | #ifdef CONFIG_PM | ||
64 | struct pl061_context_save_regs csave_regs; | ||
65 | #endif | ||
57 | }; | 66 | }; |
58 | 67 | ||
59 | static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) | 68 | static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) |
@@ -118,46 +127,16 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) | |||
118 | { | 127 | { |
119 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); | 128 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
120 | 129 | ||
121 | if (chip->irq_base == NO_IRQ) | 130 | if (chip->irq_base <= 0) |
122 | return -EINVAL; | 131 | return -EINVAL; |
123 | 132 | ||
124 | return chip->irq_base + offset; | 133 | return chip->irq_base + offset; |
125 | } | 134 | } |
126 | 135 | ||
127 | /* | ||
128 | * PL061 GPIO IRQ | ||
129 | */ | ||
130 | static void pl061_irq_disable(struct irq_data *d) | ||
131 | { | ||
132 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); | ||
133 | int offset = d->irq - chip->irq_base; | ||
134 | unsigned long flags; | ||
135 | u8 gpioie; | ||
136 | |||
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); | ||
142 | } | ||
143 | |||
144 | static void pl061_irq_enable(struct irq_data *d) | ||
145 | { | ||
146 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); | ||
147 | int offset = d->irq - chip->irq_base; | ||
148 | unsigned long flags; | ||
149 | u8 gpioie; | ||
150 | |||
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); | ||
156 | } | ||
157 | |||
158 | static int pl061_irq_type(struct irq_data *d, unsigned trigger) | 136 | static int pl061_irq_type(struct irq_data *d, unsigned trigger) |
159 | { | 137 | { |
160 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); | 138 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
139 | struct pl061_gpio *chip = gc->private; | ||
161 | int offset = d->irq - chip->irq_base; | 140 | int offset = d->irq - chip->irq_base; |
162 | unsigned long flags; | 141 | unsigned long flags; |
163 | u8 gpiois, gpioibe, gpioiev; | 142 | u8 gpiois, gpioibe, gpioiev; |
@@ -165,7 +144,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) | |||
165 | if (offset < 0 || offset >= PL061_GPIO_NR) | 144 | if (offset < 0 || offset >= PL061_GPIO_NR) |
166 | return -EINVAL; | 145 | return -EINVAL; |
167 | 146 | ||
168 | spin_lock_irqsave(&chip->irq_lock, flags); | 147 | raw_spin_lock_irqsave(&gc->lock, flags); |
169 | 148 | ||
170 | gpioiev = readb(chip->base + GPIOIEV); | 149 | gpioiev = readb(chip->base + GPIOIEV); |
171 | 150 | ||
@@ -194,49 +173,54 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) | |||
194 | 173 | ||
195 | writeb(gpioiev, chip->base + GPIOIEV); | 174 | writeb(gpioiev, chip->base + GPIOIEV); |
196 | 175 | ||
197 | spin_unlock_irqrestore(&chip->irq_lock, flags); | 176 | raw_spin_unlock_irqrestore(&gc->lock, flags); |
198 | 177 | ||
199 | return 0; | 178 | return 0; |
200 | } | 179 | } |
201 | 180 | ||
202 | static struct irq_chip pl061_irqchip = { | ||
203 | .name = "GPIO", | ||
204 | .irq_enable = pl061_irq_enable, | ||
205 | .irq_disable = pl061_irq_disable, | ||
206 | .irq_set_type = pl061_irq_type, | ||
207 | }; | ||
208 | |||
209 | static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) | 181 | static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) |
210 | { | 182 | { |
211 | struct list_head *chip_list = irq_get_handler_data(irq); | 183 | unsigned long pending; |
212 | struct list_head *ptr; | 184 | int offset; |
213 | struct pl061_gpio *chip; | 185 | struct pl061_gpio *chip = irq_desc_get_handler_data(desc); |
214 | 186 | struct irq_chip *irqchip = irq_desc_get_chip(desc); | |
215 | desc->irq_data.chip->irq_ack(&desc->irq_data); | ||
216 | list_for_each(ptr, chip_list) { | ||
217 | unsigned long pending; | ||
218 | int offset; | ||
219 | 187 | ||
220 | chip = list_entry(ptr, struct pl061_gpio, list); | 188 | chained_irq_enter(irqchip, desc); |
221 | pending = readb(chip->base + GPIOMIS); | ||
222 | writeb(pending, chip->base + GPIOIC); | ||
223 | |||
224 | if (pending == 0) | ||
225 | continue; | ||
226 | 189 | ||
190 | pending = readb(chip->base + GPIOMIS); | ||
191 | writeb(pending, chip->base + GPIOIC); | ||
192 | if (pending) { | ||
227 | for_each_set_bit(offset, &pending, PL061_GPIO_NR) | 193 | for_each_set_bit(offset, &pending, PL061_GPIO_NR) |
228 | generic_handle_irq(pl061_to_irq(&chip->gc, offset)); | 194 | generic_handle_irq(pl061_to_irq(&chip->gc, offset)); |
229 | } | 195 | } |
230 | desc->irq_data.chip->irq_unmask(&desc->irq_data); | 196 | |
197 | chained_irq_exit(irqchip, desc); | ||
198 | } | ||
199 | |||
200 | static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base) | ||
201 | { | ||
202 | struct irq_chip_type *ct; | ||
203 | |||
204 | chip->irq_gc = irq_alloc_generic_chip("gpio-pl061", 1, irq_base, | ||
205 | chip->base, handle_simple_irq); | ||
206 | chip->irq_gc->private = chip; | ||
207 | |||
208 | ct = chip->irq_gc->chip_types; | ||
209 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | ||
210 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | ||
211 | ct->chip.irq_set_type = pl061_irq_type; | ||
212 | ct->chip.irq_set_wake = irq_gc_set_wake; | ||
213 | ct->regs.mask = GPIOIE; | ||
214 | |||
215 | irq_setup_generic_chip(chip->irq_gc, IRQ_MSK(PL061_GPIO_NR), | ||
216 | IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); | ||
231 | } | 217 | } |
232 | 218 | ||
233 | static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | 219 | static int pl061_probe(struct amba_device *dev, const struct amba_id *id) |
234 | { | 220 | { |
235 | struct pl061_platform_data *pdata; | 221 | struct pl061_platform_data *pdata; |
236 | struct pl061_gpio *chip; | 222 | struct pl061_gpio *chip; |
237 | struct list_head *chip_list; | ||
238 | int ret, irq, i; | 223 | int ret, irq, i; |
239 | static DECLARE_BITMAP(init_irq, NR_IRQS); | ||
240 | 224 | ||
241 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | 225 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); |
242 | if (chip == NULL) | 226 | if (chip == NULL) |
@@ -248,7 +232,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | |||
248 | chip->irq_base = pdata->irq_base; | 232 | chip->irq_base = pdata->irq_base; |
249 | } else if (dev->dev.of_node) { | 233 | } else if (dev->dev.of_node) { |
250 | chip->gc.base = -1; | 234 | chip->gc.base = -1; |
251 | chip->irq_base = NO_IRQ; | 235 | chip->irq_base = 0; |
252 | } else { | 236 | } else { |
253 | ret = -ENODEV; | 237 | ret = -ENODEV; |
254 | goto free_mem; | 238 | goto free_mem; |
@@ -267,8 +251,6 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | |||
267 | } | 251 | } |
268 | 252 | ||
269 | spin_lock_init(&chip->lock); | 253 | spin_lock_init(&chip->lock); |
270 | spin_lock_init(&chip->irq_lock); | ||
271 | INIT_LIST_HEAD(&chip->list); | ||
272 | 254 | ||
273 | chip->gc.direction_input = pl061_direction_input; | 255 | chip->gc.direction_input = pl061_direction_input; |
274 | chip->gc.direction_output = pl061_direction_output; | 256 | chip->gc.direction_output = pl061_direction_output; |
@@ -288,9 +270,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | |||
288 | * irq_chip support | 270 | * irq_chip support |
289 | */ | 271 | */ |
290 | 272 | ||
291 | if (chip->irq_base == NO_IRQ) | 273 | if (chip->irq_base <= 0) |
292 | return 0; | 274 | return 0; |
293 | 275 | ||
276 | pl061_init_gc(chip, chip->irq_base); | ||
277 | |||
294 | writeb(0, chip->base + GPIOIE); /* disable irqs */ | 278 | writeb(0, chip->base + GPIOIE); /* disable irqs */ |
295 | irq = dev->irq[0]; | 279 | irq = dev->irq[0]; |
296 | if (irq < 0) { | 280 | if (irq < 0) { |
@@ -298,18 +282,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | |||
298 | goto iounmap; | 282 | goto iounmap; |
299 | } | 283 | } |
300 | irq_set_chained_handler(irq, pl061_irq_handler); | 284 | irq_set_chained_handler(irq, pl061_irq_handler); |
301 | if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ | 285 | irq_set_handler_data(irq, chip); |
302 | chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); | ||
303 | if (chip_list == NULL) { | ||
304 | clear_bit(irq, init_irq); | ||
305 | ret = -ENOMEM; | ||
306 | goto iounmap; | ||
307 | } | ||
308 | INIT_LIST_HEAD(chip_list); | ||
309 | irq_set_handler_data(irq, chip_list); | ||
310 | } else | ||
311 | chip_list = irq_get_handler_data(irq); | ||
312 | list_add(&chip->list, chip_list); | ||
313 | 286 | ||
314 | for (i = 0; i < PL061_GPIO_NR; i++) { | 287 | for (i = 0; i < PL061_GPIO_NR; i++) { |
315 | if (pdata) { | 288 | if (pdata) { |
@@ -319,13 +292,10 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) | |||
319 | else | 292 | else |
320 | pl061_direction_input(&chip->gc, i); | 293 | pl061_direction_input(&chip->gc, i); |
321 | } | 294 | } |
322 | |||
323 | irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, | ||
324 | handle_simple_irq); | ||
325 | set_irq_flags(i+chip->irq_base, IRQF_VALID); | ||
326 | irq_set_chip_data(i + chip->irq_base, chip); | ||
327 | } | 295 | } |
328 | 296 | ||
297 | amba_set_drvdata(dev, chip); | ||
298 | |||
329 | return 0; | 299 | return 0; |
330 | 300 | ||
331 | iounmap: | 301 | iounmap: |
@@ -338,6 +308,53 @@ free_mem: | |||
338 | return ret; | 308 | return ret; |
339 | } | 309 | } |
340 | 310 | ||
311 | #ifdef CONFIG_PM | ||
312 | static int pl061_suspend(struct device *dev) | ||
313 | { | ||
314 | struct pl061_gpio *chip = dev_get_drvdata(dev); | ||
315 | int offset; | ||
316 | |||
317 | chip->csave_regs.gpio_data = 0; | ||
318 | chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR); | ||
319 | chip->csave_regs.gpio_is = readb(chip->base + GPIOIS); | ||
320 | chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE); | ||
321 | chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV); | ||
322 | chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE); | ||
323 | |||
324 | for (offset = 0; offset < PL061_GPIO_NR; offset++) { | ||
325 | if (chip->csave_regs.gpio_dir & (1 << offset)) | ||
326 | chip->csave_regs.gpio_data |= | ||
327 | pl061_get_value(&chip->gc, offset) << offset; | ||
328 | } | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | static int pl061_resume(struct device *dev) | ||
334 | { | ||
335 | struct pl061_gpio *chip = dev_get_drvdata(dev); | ||
336 | int offset; | ||
337 | |||
338 | for (offset = 0; offset < PL061_GPIO_NR; offset++) { | ||
339 | if (chip->csave_regs.gpio_dir & (1 << offset)) | ||
340 | pl061_direction_output(&chip->gc, offset, | ||
341 | chip->csave_regs.gpio_data & | ||
342 | (1 << offset)); | ||
343 | else | ||
344 | pl061_direction_input(&chip->gc, offset); | ||
345 | } | ||
346 | |||
347 | writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS); | ||
348 | writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE); | ||
349 | writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV); | ||
350 | writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume); | ||
356 | #endif | ||
357 | |||
341 | static struct amba_id pl061_ids[] = { | 358 | static struct amba_id pl061_ids[] = { |
342 | { | 359 | { |
343 | .id = 0x00041061, | 360 | .id = 0x00041061, |
@@ -351,6 +368,9 @@ MODULE_DEVICE_TABLE(amba, pl061_ids); | |||
351 | static struct amba_driver pl061_gpio_driver = { | 368 | static struct amba_driver pl061_gpio_driver = { |
352 | .drv = { | 369 | .drv = { |
353 | .name = "pl061_gpio", | 370 | .name = "pl061_gpio", |
371 | #ifdef CONFIG_PM | ||
372 | .pm = &pl061_dev_pm_ops, | ||
373 | #endif | ||
354 | }, | 374 | }, |
355 | .id_table = pl061_ids, | 375 | .id_table = pl061_ids, |
356 | .probe = pl061_probe, | 376 | .probe = pl061_probe, |