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/irqdomain.h> |
19 | #include <linux/irqchip/chained_irq.h> |
20 | #include <linux/bitops.h> |
21 | #include <linux/workqueue.h> |
22 | #include <linux/gpio.h> |
23 | #include <linux/device.h> |
24 | #include <linux/amba/bus.h> |
25 | #include <linux/amba/pl061.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/pinctrl/consumer.h> |
28 | #include <linux/pm.h> |
29 | |
30 | #define GPIODIR 0x400 |
31 | #define GPIOIS 0x404 |
32 | #define GPIOIBE 0x408 |
33 | #define GPIOIEV 0x40C |
34 | #define GPIOIE 0x410 |
35 | #define GPIORIS 0x414 |
36 | #define GPIOMIS 0x418 |
37 | #define GPIOIC 0x41C |
38 | |
39 | #define PL061_GPIO_NR 8 |
40 | |
41 | #ifdef CONFIG_PM |
42 | struct pl061_context_save_regs { |
43 | u8 gpio_data; |
44 | u8 gpio_dir; |
45 | u8 gpio_is; |
46 | u8 gpio_ibe; |
47 | u8 gpio_iev; |
48 | u8 gpio_ie; |
49 | }; |
50 | #endif |
51 | |
52 | struct pl061_gpio { |
53 | spinlock_t lock; |
54 | |
55 | void __iomem *base; |
56 | struct irq_domain *domain; |
57 | struct gpio_chip gc; |
58 | |
59 | #ifdef CONFIG_PM |
60 | struct pl061_context_save_regs csave_regs; |
61 | #endif |
62 | }; |
63 | |
64 | static int pl061_gpio_request(struct gpio_chip *chip, unsigned offset) |
65 | { |
66 | /* |
67 | * Map back to global GPIO space and request muxing, the direction |
68 | * parameter does not matter for this controller. |
69 | */ |
70 | int gpio = chip->base + offset; |
71 | |
72 | return pinctrl_request_gpio(gpio); |
73 | } |
74 | |
75 | static void pl061_gpio_free(struct gpio_chip *chip, unsigned offset) |
76 | { |
77 | int gpio = chip->base + offset; |
78 | |
79 | pinctrl_free_gpio(gpio); |
80 | } |
81 | |
82 | static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) |
83 | { |
84 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
85 | unsigned long flags; |
86 | unsigned char gpiodir; |
87 | |
88 | if (offset >= gc->ngpio) |
89 | return -EINVAL; |
90 | |
91 | spin_lock_irqsave(&chip->lock, flags); |
92 | gpiodir = readb(chip->base + GPIODIR); |
93 | gpiodir &= ~(1 << offset); |
94 | writeb(gpiodir, chip->base + GPIODIR); |
95 | spin_unlock_irqrestore(&chip->lock, flags); |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, |
101 | int value) |
102 | { |
103 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
104 | unsigned long flags; |
105 | unsigned char gpiodir; |
106 | |
107 | if (offset >= gc->ngpio) |
108 | return -EINVAL; |
109 | |
110 | spin_lock_irqsave(&chip->lock, flags); |
111 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
112 | gpiodir = readb(chip->base + GPIODIR); |
113 | gpiodir |= 1 << offset; |
114 | writeb(gpiodir, chip->base + GPIODIR); |
115 | |
116 | /* |
117 | * gpio value is set again, because pl061 doesn't allow to set value of |
118 | * a gpio pin before configuring it in OUT mode. |
119 | */ |
120 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
121 | spin_unlock_irqrestore(&chip->lock, flags); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static int pl061_get_value(struct gpio_chip *gc, unsigned offset) |
127 | { |
128 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
129 | |
130 | return !!readb(chip->base + (1 << (offset + 2))); |
131 | } |
132 | |
133 | static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) |
134 | { |
135 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
136 | |
137 | writeb(!!value << offset, chip->base + (1 << (offset + 2))); |
138 | } |
139 | |
140 | static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) |
141 | { |
142 | struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); |
143 | |
144 | return irq_create_mapping(chip->domain, offset); |
145 | } |
146 | |
147 | static int pl061_irq_type(struct irq_data *d, unsigned trigger) |
148 | { |
149 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); |
150 | int offset = irqd_to_hwirq(d); |
151 | unsigned long flags; |
152 | u8 gpiois, gpioibe, gpioiev; |
153 | |
154 | if (offset < 0 || offset >= PL061_GPIO_NR) |
155 | return -EINVAL; |
156 | |
157 | spin_lock_irqsave(&chip->lock, flags); |
158 | |
159 | gpioiev = readb(chip->base + GPIOIEV); |
160 | |
161 | gpiois = readb(chip->base + GPIOIS); |
162 | if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { |
163 | gpiois |= 1 << offset; |
164 | if (trigger & IRQ_TYPE_LEVEL_HIGH) |
165 | gpioiev |= 1 << offset; |
166 | else |
167 | gpioiev &= ~(1 << offset); |
168 | } else |
169 | gpiois &= ~(1 << offset); |
170 | writeb(gpiois, chip->base + GPIOIS); |
171 | |
172 | gpioibe = readb(chip->base + GPIOIBE); |
173 | if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) |
174 | gpioibe |= 1 << offset; |
175 | else { |
176 | gpioibe &= ~(1 << offset); |
177 | if (trigger & IRQ_TYPE_EDGE_RISING) |
178 | gpioiev |= 1 << offset; |
179 | else if (trigger & IRQ_TYPE_EDGE_FALLING) |
180 | gpioiev &= ~(1 << offset); |
181 | } |
182 | writeb(gpioibe, chip->base + GPIOIBE); |
183 | |
184 | writeb(gpioiev, chip->base + GPIOIEV); |
185 | |
186 | spin_unlock_irqrestore(&chip->lock, flags); |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) |
192 | { |
193 | unsigned long pending; |
194 | int offset; |
195 | struct pl061_gpio *chip = irq_desc_get_handler_data(desc); |
196 | struct irq_chip *irqchip = irq_desc_get_chip(desc); |
197 | |
198 | chained_irq_enter(irqchip, desc); |
199 | |
200 | pending = readb(chip->base + GPIOMIS); |
201 | writeb(pending, chip->base + GPIOIC); |
202 | if (pending) { |
203 | for_each_set_bit(offset, &pending, PL061_GPIO_NR) |
204 | generic_handle_irq(pl061_to_irq(&chip->gc, offset)); |
205 | } |
206 | |
207 | chained_irq_exit(irqchip, desc); |
208 | } |
209 | |
210 | static void pl061_irq_mask(struct irq_data *d) |
211 | { |
212 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); |
213 | u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR); |
214 | u8 gpioie; |
215 | |
216 | spin_lock(&chip->lock); |
217 | gpioie = readb(chip->base + GPIOIE) & ~mask; |
218 | writeb(gpioie, chip->base + GPIOIE); |
219 | spin_unlock(&chip->lock); |
220 | } |
221 | |
222 | static void pl061_irq_unmask(struct irq_data *d) |
223 | { |
224 | struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); |
225 | u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR); |
226 | u8 gpioie; |
227 | |
228 | spin_lock(&chip->lock); |
229 | gpioie = readb(chip->base + GPIOIE) | mask; |
230 | writeb(gpioie, chip->base + GPIOIE); |
231 | spin_unlock(&chip->lock); |
232 | } |
233 | |
234 | static struct irq_chip pl061_irqchip = { |
235 | .name = "pl061 gpio", |
236 | .irq_mask = pl061_irq_mask, |
237 | .irq_unmask = pl061_irq_unmask, |
238 | .irq_set_type = pl061_irq_type, |
239 | }; |
240 | |
241 | static int pl061_irq_map(struct irq_domain *d, unsigned int virq, |
242 | irq_hw_number_t hw) |
243 | { |
244 | struct pl061_gpio *chip = d->host_data; |
245 | |
246 | irq_set_chip_and_handler_name(virq, &pl061_irqchip, handle_simple_irq, |
247 | "pl061"); |
248 | irq_set_chip_data(virq, chip); |
249 | irq_set_irq_type(virq, IRQ_TYPE_NONE); |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static const struct irq_domain_ops pl061_domain_ops = { |
255 | .map = pl061_irq_map, |
256 | .xlate = irq_domain_xlate_twocell, |
257 | }; |
258 | |
259 | static int pl061_probe(struct amba_device *adev, const struct amba_id *id) |
260 | { |
261 | struct device *dev = &adev->dev; |
262 | struct pl061_platform_data *pdata = dev->platform_data; |
263 | struct pl061_gpio *chip; |
264 | int ret, irq, i, irq_base; |
265 | |
266 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); |
267 | if (chip == NULL) |
268 | return -ENOMEM; |
269 | |
270 | if (pdata) { |
271 | chip->gc.base = pdata->gpio_base; |
272 | irq_base = pdata->irq_base; |
273 | if (irq_base <= 0) |
274 | return -ENODEV; |
275 | } else { |
276 | chip->gc.base = -1; |
277 | irq_base = 0; |
278 | } |
279 | |
280 | if (!devm_request_mem_region(dev, adev->res.start, |
281 | resource_size(&adev->res), "pl061")) |
282 | return -EBUSY; |
283 | |
284 | chip->base = devm_ioremap(dev, adev->res.start, |
285 | resource_size(&adev->res)); |
286 | if (!chip->base) |
287 | return -ENOMEM; |
288 | |
289 | chip->domain = irq_domain_add_simple(adev->dev.of_node, PL061_GPIO_NR, |
290 | irq_base, &pl061_domain_ops, chip); |
291 | if (!chip->domain) |
292 | return -ENODEV; |
293 | |
294 | spin_lock_init(&chip->lock); |
295 | |
296 | chip->gc.request = pl061_gpio_request; |
297 | chip->gc.free = pl061_gpio_free; |
298 | chip->gc.direction_input = pl061_direction_input; |
299 | chip->gc.direction_output = pl061_direction_output; |
300 | chip->gc.get = pl061_get_value; |
301 | chip->gc.set = pl061_set_value; |
302 | chip->gc.to_irq = pl061_to_irq; |
303 | chip->gc.ngpio = PL061_GPIO_NR; |
304 | chip->gc.label = dev_name(dev); |
305 | chip->gc.dev = dev; |
306 | chip->gc.owner = THIS_MODULE; |
307 | |
308 | ret = gpiochip_add(&chip->gc); |
309 | if (ret) |
310 | return ret; |
311 | |
312 | /* |
313 | * irq_chip support |
314 | */ |
315 | writeb(0, chip->base + GPIOIE); /* disable irqs */ |
316 | irq = adev->irq[0]; |
317 | if (irq < 0) |
318 | return -ENODEV; |
319 | |
320 | irq_set_chained_handler(irq, pl061_irq_handler); |
321 | irq_set_handler_data(irq, chip); |
322 | |
323 | for (i = 0; i < PL061_GPIO_NR; i++) { |
324 | if (pdata) { |
325 | if (pdata->directions & (1 << i)) |
326 | pl061_direction_output(&chip->gc, i, |
327 | pdata->values & (1 << i)); |
328 | else |
329 | pl061_direction_input(&chip->gc, i); |
330 | } |
331 | } |
332 | |
333 | amba_set_drvdata(adev, chip); |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | #ifdef CONFIG_PM |
339 | static int pl061_suspend(struct device *dev) |
340 | { |
341 | struct pl061_gpio *chip = dev_get_drvdata(dev); |
342 | int offset; |
343 | |
344 | chip->csave_regs.gpio_data = 0; |
345 | chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR); |
346 | chip->csave_regs.gpio_is = readb(chip->base + GPIOIS); |
347 | chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE); |
348 | chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV); |
349 | chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE); |
350 | |
351 | for (offset = 0; offset < PL061_GPIO_NR; offset++) { |
352 | if (chip->csave_regs.gpio_dir & (1 << offset)) |
353 | chip->csave_regs.gpio_data |= |
354 | pl061_get_value(&chip->gc, offset) << offset; |
355 | } |
356 | |
357 | return 0; |
358 | } |
359 | |
360 | static int pl061_resume(struct device *dev) |
361 | { |
362 | struct pl061_gpio *chip = dev_get_drvdata(dev); |
363 | int offset; |
364 | |
365 | for (offset = 0; offset < PL061_GPIO_NR; offset++) { |
366 | if (chip->csave_regs.gpio_dir & (1 << offset)) |
367 | pl061_direction_output(&chip->gc, offset, |
368 | chip->csave_regs.gpio_data & |
369 | (1 << offset)); |
370 | else |
371 | pl061_direction_input(&chip->gc, offset); |
372 | } |
373 | |
374 | writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS); |
375 | writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE); |
376 | writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV); |
377 | writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE); |
378 | |
379 | return 0; |
380 | } |
381 | |
382 | static const struct dev_pm_ops pl061_dev_pm_ops = { |
383 | .suspend = pl061_suspend, |
384 | .resume = pl061_resume, |
385 | .freeze = pl061_suspend, |
386 | .restore = pl061_resume, |
387 | }; |
388 | #endif |
389 | |
390 | static struct amba_id pl061_ids[] = { |
391 | { |
392 | .id = 0x00041061, |
393 | .mask = 0x000fffff, |
394 | }, |
395 | { 0, 0 }, |
396 | }; |
397 | |
398 | MODULE_DEVICE_TABLE(amba, pl061_ids); |
399 | |
400 | static struct amba_driver pl061_gpio_driver = { |
401 | .drv = { |
402 | .name = "pl061_gpio", |
403 | #ifdef CONFIG_PM |
404 | .pm = &pl061_dev_pm_ops, |
405 | #endif |
406 | }, |
407 | .id_table = pl061_ids, |
408 | .probe = pl061_probe, |
409 | }; |
410 | |
411 | static int __init pl061_gpio_init(void) |
412 | { |
413 | return amba_driver_register(&pl061_gpio_driver); |
414 | } |
415 | module_init(pl061_gpio_init); |
416 | |
417 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); |
418 | MODULE_DESCRIPTION("PL061 GPIO driver"); |
419 | MODULE_LICENSE("GPL"); |
420 |
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