Root/
1 | /* |
2 | * Copyright (C) 2008, 2009 Provigent Ltd. |
3 | * |
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. |
7 | * |
8 | * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) |
9 | * |
10 | * Data sheet: ARM DDI 0190B, September 2000 |
11 | */ |
12 | #include <linux/spinlock.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/module.h> |
15 | #include <linux/io.h> |
16 | #include <linux/ioport.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/bitops.h> |
19 | #include <linux/workqueue.h> |
20 | #include <linux/gpio.h> |
21 | #include <linux/device.h> |
22 | #include <linux/amba/bus.h> |
23 | #include <linux/amba/pl061.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/pm.h> |
26 | #include <asm/mach/irq.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 | #ifdef CONFIG_PM |
40 | struct pl061_context_save_regs { |
41 | u8 gpio_data; |
42 | u8 gpio_dir; |
43 | u8 gpio_is; |
44 | u8 gpio_ibe; |
45 | u8 gpio_iev; |
46 | u8 gpio_ie; |
47 | }; |
48 | #endif |
49 | |
50 | struct pl061_gpio { |
51 | /* Each of the two spinlocks protects a different set of hardware |
52 | * regiters and data structurs. This decouples the code of the IRQ from |
53 | * the GPIO code. This also makes the case of a GPIO routine call from |
54 | * the IRQ code simpler. |
55 | */ |
56 | spinlock_t lock; /* GPIO registers */ |
57 | |
58 | void __iomem *base; |
59 | int irq_base; |
60 | struct irq_chip_generic *irq_gc; |
61 | struct gpio_chip gc; |
62 | |
63 | #ifdef CONFIG_PM |
64 | struct pl061_context_save_regs csave_regs; |
65 | #endif |
66 | }; |
67 | |
68 | static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) |
69 | { |
70 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
71 | unsigned long flags; |
72 | unsigned char gpiodir; |
73 | |
74 | if (offset >= gc->ngpio) |
75 | return -EINVAL; |
76 | |
77 | spin_lock_irqsave(&chip->lock, flags); |
78 | gpiodir = readb(chip->base + GPIODIR); |
79 | gpiodir &= ~(1 << offset); |
80 | writeb(gpiodir, chip->base + GPIODIR); |
81 | spin_unlock_irqrestore(&chip->lock, flags); |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, |
87 | int value) |
88 | { |
89 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
90 | unsigned long flags; |
91 | unsigned char gpiodir; |
92 | |
93 | if (offset >= gc->ngpio) |
94 | return -EINVAL; |
95 | |
96 | spin_lock_irqsave(&chip->lock, flags); |
97 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
98 | gpiodir = readb(chip->base + GPIODIR); |
99 | gpiodir |= 1 << offset; |
100 | writeb(gpiodir, chip->base + GPIODIR); |
101 | |
102 | /* |
103 | * gpio value is set again, because pl061 doesn't allow to set value of |
104 | * a gpio pin before configuring it in OUT mode. |
105 | */ |
106 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
107 | spin_unlock_irqrestore(&chip->lock, flags); |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int pl061_get_value(struct gpio_chip *gc, unsigned offset) |
113 | { |
114 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
115 | |
116 | return !!readb(chip->base + (1 << (offset + 2))); |
117 | } |
118 | |
119 | static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) |
120 | { |
121 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
122 | |
123 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
124 | } |
125 | |
126 | static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) |
127 | { |
128 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
129 | |
130 | if (chip->irq_base <= 0) |
131 | return -EINVAL; |
132 | |
133 | return chip->irq_base + offset; |
134 | } |
135 | |
136 | static int pl061_irq_type(struct irq_data *d, unsigned trigger) |
137 | { |
138 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
139 | struct pl061_gpio *chip = gc->private; |
140 | int offset = d->irq - chip->irq_base; |
141 | unsigned long flags; |
142 | u8 gpiois, gpioibe, gpioiev; |
143 | |
144 | if (offset < 0 || offset >= PL061_GPIO_NR) |
145 | return -EINVAL; |
146 | |
147 | raw_spin_lock_irqsave(&gc->lock, flags); |
148 | |
149 | gpioiev = readb(chip->base + GPIOIEV); |
150 | |
151 | gpiois = readb(chip->base + GPIOIS); |
152 | if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { |
153 | gpiois |= 1 << offset; |
154 | if (trigger & IRQ_TYPE_LEVEL_HIGH) |
155 | gpioiev |= 1 << offset; |
156 | else |
157 | gpioiev &= ~(1 << offset); |
158 | } else |
159 | gpiois &= ~(1 << offset); |
160 | writeb(gpiois, chip->base + GPIOIS); |
161 | |
162 | gpioibe = readb(chip->base + GPIOIBE); |
163 | if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) |
164 | gpioibe |= 1 << offset; |
165 | else { |
166 | gpioibe &= ~(1 << offset); |
167 | if (trigger & IRQ_TYPE_EDGE_RISING) |
168 | gpioiev |= 1 << offset; |
169 | else if (trigger & IRQ_TYPE_EDGE_FALLING) |
170 | gpioiev &= ~(1 << offset); |
171 | } |
172 | writeb(gpioibe, chip->base + GPIOIBE); |
173 | |
174 | writeb(gpioiev, chip->base + GPIOIEV); |
175 | |
176 | raw_spin_unlock_irqrestore(&gc->lock, flags); |
177 | |
178 | return 0; |
179 | } |
180 | |
181 | static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) |
182 | { |
183 | unsigned long pending; |
184 | int offset; |
185 | struct pl061_gpio *chip = irq_desc_get_handler_data(desc); |
186 | struct irq_chip *irqchip = irq_desc_get_chip(desc); |
187 | |
188 | chained_irq_enter(irqchip, desc); |
189 | |
190 | pending = readb(chip->base + GPIOMIS); |
191 | writeb(pending, chip->base + GPIOIC); |
192 | if (pending) { |
193 | for_each_set_bit(offset, &pending, PL061_GPIO_NR) |
194 | generic_handle_irq(pl061_to_irq(&chip->gc, offset)); |
195 | } |
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); |
217 | } |
218 | |
219 | static int pl061_probe(struct amba_device *dev, const struct amba_id *id) |
220 | { |
221 | struct pl061_platform_data *pdata; |
222 | struct pl061_gpio *chip; |
223 | int ret, irq, i; |
224 | |
225 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); |
226 | if (chip == NULL) |
227 | return -ENOMEM; |
228 | |
229 | pdata = dev->dev.platform_data; |
230 | if (pdata) { |
231 | chip->gc.base = pdata->gpio_base; |
232 | chip->irq_base = pdata->irq_base; |
233 | } else if (dev->dev.of_node) { |
234 | chip->gc.base = -1; |
235 | chip->irq_base = 0; |
236 | } else { |
237 | ret = -ENODEV; |
238 | goto free_mem; |
239 | } |
240 | |
241 | if (!request_mem_region(dev->res.start, |
242 | resource_size(&dev->res), "pl061")) { |
243 | ret = -EBUSY; |
244 | goto free_mem; |
245 | } |
246 | |
247 | chip->base = ioremap(dev->res.start, resource_size(&dev->res)); |
248 | if (chip->base == NULL) { |
249 | ret = -ENOMEM; |
250 | goto release_region; |
251 | } |
252 | |
253 | spin_lock_init(&chip->lock); |
254 | |
255 | chip->gc.direction_input = pl061_direction_input; |
256 | chip->gc.direction_output = pl061_direction_output; |
257 | chip->gc.get = pl061_get_value; |
258 | chip->gc.set = pl061_set_value; |
259 | chip->gc.to_irq = pl061_to_irq; |
260 | chip->gc.ngpio = PL061_GPIO_NR; |
261 | chip->gc.label = dev_name(&dev->dev); |
262 | chip->gc.dev = &dev->dev; |
263 | chip->gc.owner = THIS_MODULE; |
264 | |
265 | ret = gpiochip_add(&chip->gc); |
266 | if (ret) |
267 | goto iounmap; |
268 | |
269 | /* |
270 | * irq_chip support |
271 | */ |
272 | |
273 | if (chip->irq_base <= 0) |
274 | return 0; |
275 | |
276 | pl061_init_gc(chip, chip->irq_base); |
277 | |
278 | writeb(0, chip->base + GPIOIE); /* disable irqs */ |
279 | irq = dev->irq[0]; |
280 | if (irq < 0) { |
281 | ret = -ENODEV; |
282 | goto iounmap; |
283 | } |
284 | irq_set_chained_handler(irq, pl061_irq_handler); |
285 | irq_set_handler_data(irq, chip); |
286 | |
287 | for (i = 0; i < PL061_GPIO_NR; i++) { |
288 | if (pdata) { |
289 | if (pdata->directions & (1 << i)) |
290 | pl061_direction_output(&chip->gc, i, |
291 | pdata->values & (1 << i)); |
292 | else |
293 | pl061_direction_input(&chip->gc, i); |
294 | } |
295 | } |
296 | |
297 | amba_set_drvdata(dev, chip); |
298 | |
299 | return 0; |
300 | |
301 | iounmap: |
302 | iounmap(chip->base); |
303 | release_region: |
304 | release_mem_region(dev->res.start, resource_size(&dev->res)); |
305 | free_mem: |
306 | kfree(chip); |
307 | |
308 | return ret; |
309 | } |
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 const struct dev_pm_ops pl061_dev_pm_ops = { |
356 | .suspend = pl061_suspend, |
357 | .resume = pl061_resume, |
358 | .freeze = pl061_suspend, |
359 | .restore = pl061_resume, |
360 | }; |
361 | #endif |
362 | |
363 | static struct amba_id pl061_ids[] = { |
364 | { |
365 | .id = 0x00041061, |
366 | .mask = 0x000fffff, |
367 | }, |
368 | { 0, 0 }, |
369 | }; |
370 | |
371 | MODULE_DEVICE_TABLE(amba, pl061_ids); |
372 | |
373 | static struct amba_driver pl061_gpio_driver = { |
374 | .drv = { |
375 | .name = "pl061_gpio", |
376 | #ifdef CONFIG_PM |
377 | .pm = &pl061_dev_pm_ops, |
378 | #endif |
379 | }, |
380 | .id_table = pl061_ids, |
381 | .probe = pl061_probe, |
382 | }; |
383 | |
384 | static int __init pl061_gpio_init(void) |
385 | { |
386 | return amba_driver_register(&pl061_gpio_driver); |
387 | } |
388 | subsys_initcall(pl061_gpio_init); |
389 | |
390 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); |
391 | MODULE_DESCRIPTION("PL061 GPIO driver"); |
392 | MODULE_LICENSE("GPL"); |
393 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9