diff options
Diffstat (limited to 'arch/arm/plat-nomadik/gpio.c')
-rw-r--r-- | arch/arm/plat-nomadik/gpio.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c new file mode 100644 index 000000000000..d28900cfa541 --- /dev/null +++ b/arch/arm/plat-nomadik/gpio.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Generic GPIO driver for logic cells found in the Nomadik SoC | ||
3 | * | ||
4 | * Copyright (C) 2008,2009 STMicroelectronics | ||
5 | * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> | ||
6 | * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/irq.h> | ||
24 | |||
25 | #include <mach/hardware.h> | ||
26 | #include <mach/gpio.h> | ||
27 | |||
28 | /* | ||
29 | * The GPIO module in the Nomadik family of Systems-on-Chip is an | ||
30 | * AMBA device, managing 32 pins and alternate functions. The logic block | ||
31 | * is currently only used in the Nomadik. | ||
32 | * | ||
33 | * Symbols in this file are called "nmk_gpio" for "nomadik gpio" | ||
34 | */ | ||
35 | |||
36 | #define NMK_GPIO_PER_CHIP 32 | ||
37 | struct nmk_gpio_chip { | ||
38 | struct gpio_chip chip; | ||
39 | void __iomem *addr; | ||
40 | struct clk *clk; | ||
41 | unsigned int parent_irq; | ||
42 | spinlock_t lock; | ||
43 | /* Keep track of configured edges */ | ||
44 | u32 edge_rising; | ||
45 | u32 edge_falling; | ||
46 | }; | ||
47 | |||
48 | /* Mode functions */ | ||
49 | int nmk_gpio_set_mode(int gpio, int gpio_mode) | ||
50 | { | ||
51 | struct nmk_gpio_chip *nmk_chip; | ||
52 | unsigned long flags; | ||
53 | u32 afunc, bfunc, bit; | ||
54 | |||
55 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); | ||
56 | if (!nmk_chip) | ||
57 | return -EINVAL; | ||
58 | |||
59 | bit = 1 << (gpio - nmk_chip->chip.base); | ||
60 | |||
61 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
62 | afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit; | ||
63 | bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit; | ||
64 | if (gpio_mode & NMK_GPIO_ALT_A) | ||
65 | afunc |= bit; | ||
66 | if (gpio_mode & NMK_GPIO_ALT_B) | ||
67 | bfunc |= bit; | ||
68 | writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA); | ||
69 | writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB); | ||
70 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | EXPORT_SYMBOL(nmk_gpio_set_mode); | ||
75 | |||
76 | int nmk_gpio_get_mode(int gpio) | ||
77 | { | ||
78 | struct nmk_gpio_chip *nmk_chip; | ||
79 | u32 afunc, bfunc, bit; | ||
80 | |||
81 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); | ||
82 | if (!nmk_chip) | ||
83 | return -EINVAL; | ||
84 | |||
85 | bit = 1 << (gpio - nmk_chip->chip.base); | ||
86 | |||
87 | afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; | ||
88 | bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; | ||
89 | |||
90 | return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); | ||
91 | } | ||
92 | EXPORT_SYMBOL(nmk_gpio_get_mode); | ||
93 | |||
94 | |||
95 | /* IRQ functions */ | ||
96 | static inline int nmk_gpio_get_bitmask(int gpio) | ||
97 | { | ||
98 | return 1 << (gpio % 32); | ||
99 | } | ||
100 | |||
101 | static void nmk_gpio_irq_ack(unsigned int irq) | ||
102 | { | ||
103 | int gpio; | ||
104 | struct nmk_gpio_chip *nmk_chip; | ||
105 | |||
106 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
107 | nmk_chip = get_irq_chip_data(irq); | ||
108 | if (!nmk_chip) | ||
109 | return; | ||
110 | writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); | ||
111 | } | ||
112 | |||
113 | static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, | ||
114 | int gpio, bool enable) | ||
115 | { | ||
116 | u32 bitmask = nmk_gpio_get_bitmask(gpio); | ||
117 | u32 reg; | ||
118 | |||
119 | /* we must individually set/clear the two edges */ | ||
120 | if (nmk_chip->edge_rising & bitmask) { | ||
121 | reg = readl(nmk_chip->addr + NMK_GPIO_RIMSC); | ||
122 | if (enable) | ||
123 | reg |= bitmask; | ||
124 | else | ||
125 | reg &= ~bitmask; | ||
126 | writel(reg, nmk_chip->addr + NMK_GPIO_RIMSC); | ||
127 | } | ||
128 | if (nmk_chip->edge_falling & bitmask) { | ||
129 | reg = readl(nmk_chip->addr + NMK_GPIO_FIMSC); | ||
130 | if (enable) | ||
131 | reg |= bitmask; | ||
132 | else | ||
133 | reg &= ~bitmask; | ||
134 | writel(reg, nmk_chip->addr + NMK_GPIO_FIMSC); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | static void nmk_gpio_irq_modify(unsigned int irq, bool enable) | ||
139 | { | ||
140 | int gpio; | ||
141 | struct nmk_gpio_chip *nmk_chip; | ||
142 | unsigned long flags; | ||
143 | u32 bitmask; | ||
144 | |||
145 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
146 | nmk_chip = get_irq_chip_data(irq); | ||
147 | bitmask = nmk_gpio_get_bitmask(gpio); | ||
148 | if (!nmk_chip) | ||
149 | return; | ||
150 | |||
151 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
152 | __nmk_gpio_irq_modify(nmk_chip, gpio, enable); | ||
153 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
154 | } | ||
155 | |||
156 | static void nmk_gpio_irq_mask(unsigned int irq) | ||
157 | { | ||
158 | nmk_gpio_irq_modify(irq, false); | ||
159 | }; | ||
160 | |||
161 | static void nmk_gpio_irq_unmask(unsigned int irq) | ||
162 | { | ||
163 | nmk_gpio_irq_modify(irq, true); | ||
164 | } | ||
165 | |||
166 | static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) | ||
167 | { | ||
168 | bool enabled = !(irq_to_desc(irq)->status & IRQ_DISABLED); | ||
169 | int gpio; | ||
170 | struct nmk_gpio_chip *nmk_chip; | ||
171 | unsigned long flags; | ||
172 | u32 bitmask; | ||
173 | |||
174 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
175 | nmk_chip = get_irq_chip_data(irq); | ||
176 | bitmask = nmk_gpio_get_bitmask(gpio); | ||
177 | if (!nmk_chip) | ||
178 | return -EINVAL; | ||
179 | |||
180 | if (type & IRQ_TYPE_LEVEL_HIGH) | ||
181 | return -EINVAL; | ||
182 | if (type & IRQ_TYPE_LEVEL_LOW) | ||
183 | return -EINVAL; | ||
184 | |||
185 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
186 | |||
187 | if (enabled) | ||
188 | __nmk_gpio_irq_modify(nmk_chip, gpio, false); | ||
189 | |||
190 | nmk_chip->edge_rising &= ~bitmask; | ||
191 | if (type & IRQ_TYPE_EDGE_RISING) | ||
192 | nmk_chip->edge_rising |= bitmask; | ||
193 | |||
194 | nmk_chip->edge_falling &= ~bitmask; | ||
195 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
196 | nmk_chip->edge_falling |= bitmask; | ||
197 | |||
198 | if (enabled) | ||
199 | __nmk_gpio_irq_modify(nmk_chip, gpio, true); | ||
200 | |||
201 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct irq_chip nmk_gpio_irq_chip = { | ||
207 | .name = "Nomadik-GPIO", | ||
208 | .ack = nmk_gpio_irq_ack, | ||
209 | .mask = nmk_gpio_irq_mask, | ||
210 | .unmask = nmk_gpio_irq_unmask, | ||
211 | .set_type = nmk_gpio_irq_set_type, | ||
212 | }; | ||
213 | |||
214 | static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | ||
215 | { | ||
216 | struct nmk_gpio_chip *nmk_chip; | ||
217 | struct irq_chip *host_chip = get_irq_chip(irq); | ||
218 | unsigned int gpio_irq; | ||
219 | u32 pending; | ||
220 | unsigned int first_irq; | ||
221 | |||
222 | if (host_chip->mask_ack) | ||
223 | host_chip->mask_ack(irq); | ||
224 | else { | ||
225 | host_chip->mask(irq); | ||
226 | if (host_chip->ack) | ||
227 | host_chip->ack(irq); | ||
228 | } | ||
229 | |||
230 | nmk_chip = get_irq_data(irq); | ||
231 | first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); | ||
232 | while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) { | ||
233 | gpio_irq = first_irq + __ffs(pending); | ||
234 | generic_handle_irq(gpio_irq); | ||
235 | } | ||
236 | |||
237 | host_chip->unmask(irq); | ||
238 | } | ||
239 | |||
240 | static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) | ||
241 | { | ||
242 | unsigned int first_irq; | ||
243 | int i; | ||
244 | |||
245 | first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); | ||
246 | for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) { | ||
247 | set_irq_chip(i, &nmk_gpio_irq_chip); | ||
248 | set_irq_handler(i, handle_edge_irq); | ||
249 | set_irq_flags(i, IRQF_VALID); | ||
250 | set_irq_chip_data(i, nmk_chip); | ||
251 | set_irq_type(i, IRQ_TYPE_EDGE_FALLING); | ||
252 | } | ||
253 | set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler); | ||
254 | set_irq_data(nmk_chip->parent_irq, nmk_chip); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | /* I/O Functions */ | ||
259 | static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset) | ||
260 | { | ||
261 | struct nmk_gpio_chip *nmk_chip = | ||
262 | container_of(chip, struct nmk_gpio_chip, chip); | ||
263 | |||
264 | writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, | ||
269 | int val) | ||
270 | { | ||
271 | struct nmk_gpio_chip *nmk_chip = | ||
272 | container_of(chip, struct nmk_gpio_chip, chip); | ||
273 | |||
274 | writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset) | ||
279 | { | ||
280 | struct nmk_gpio_chip *nmk_chip = | ||
281 | container_of(chip, struct nmk_gpio_chip, chip); | ||
282 | u32 bit = 1 << offset; | ||
283 | |||
284 | return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; | ||
285 | } | ||
286 | |||
287 | static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, | ||
288 | int val) | ||
289 | { | ||
290 | struct nmk_gpio_chip *nmk_chip = | ||
291 | container_of(chip, struct nmk_gpio_chip, chip); | ||
292 | u32 bit = 1 << offset; | ||
293 | |||
294 | if (val) | ||
295 | writel(bit, nmk_chip->addr + NMK_GPIO_DATS); | ||
296 | else | ||
297 | writel(bit, nmk_chip->addr + NMK_GPIO_DATC); | ||
298 | } | ||
299 | |||
300 | /* This structure is replicated for each GPIO block allocated at probe time */ | ||
301 | static struct gpio_chip nmk_gpio_template = { | ||
302 | .direction_input = nmk_gpio_make_input, | ||
303 | .get = nmk_gpio_get_input, | ||
304 | .direction_output = nmk_gpio_make_output, | ||
305 | .set = nmk_gpio_set_output, | ||
306 | .ngpio = NMK_GPIO_PER_CHIP, | ||
307 | .can_sleep = 0, | ||
308 | }; | ||
309 | |||
310 | static int __init nmk_gpio_probe(struct platform_device *dev) | ||
311 | { | ||
312 | struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; | ||
313 | struct nmk_gpio_chip *nmk_chip; | ||
314 | struct gpio_chip *chip; | ||
315 | struct resource *res; | ||
316 | struct clk *clk; | ||
317 | int irq; | ||
318 | int ret; | ||
319 | |||
320 | if (!pdata) | ||
321 | return -ENODEV; | ||
322 | |||
323 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
324 | if (!res) { | ||
325 | ret = -ENOENT; | ||
326 | goto out; | ||
327 | } | ||
328 | |||
329 | irq = platform_get_irq(dev, 0); | ||
330 | if (irq < 0) { | ||
331 | ret = irq; | ||
332 | goto out; | ||
333 | } | ||
334 | |||
335 | if (request_mem_region(res->start, resource_size(res), | ||
336 | dev_name(&dev->dev)) == NULL) { | ||
337 | ret = -EBUSY; | ||
338 | goto out; | ||
339 | } | ||
340 | |||
341 | clk = clk_get(&dev->dev, NULL); | ||
342 | if (IS_ERR(clk)) { | ||
343 | ret = PTR_ERR(clk); | ||
344 | goto out_release; | ||
345 | } | ||
346 | |||
347 | clk_enable(clk); | ||
348 | |||
349 | nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); | ||
350 | if (!nmk_chip) { | ||
351 | ret = -ENOMEM; | ||
352 | goto out_clk; | ||
353 | } | ||
354 | /* | ||
355 | * The virt address in nmk_chip->addr is in the nomadik register space, | ||
356 | * so we can simply convert the resource address, without remapping | ||
357 | */ | ||
358 | nmk_chip->clk = clk; | ||
359 | nmk_chip->addr = io_p2v(res->start); | ||
360 | nmk_chip->chip = nmk_gpio_template; | ||
361 | nmk_chip->parent_irq = irq; | ||
362 | spin_lock_init(&nmk_chip->lock); | ||
363 | |||
364 | chip = &nmk_chip->chip; | ||
365 | chip->base = pdata->first_gpio; | ||
366 | chip->label = pdata->name; | ||
367 | chip->dev = &dev->dev; | ||
368 | chip->owner = THIS_MODULE; | ||
369 | |||
370 | ret = gpiochip_add(&nmk_chip->chip); | ||
371 | if (ret) | ||
372 | goto out_free; | ||
373 | |||
374 | platform_set_drvdata(dev, nmk_chip); | ||
375 | |||
376 | nmk_gpio_init_irq(nmk_chip); | ||
377 | |||
378 | dev_info(&dev->dev, "Bits %i-%i at address %p\n", | ||
379 | nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr); | ||
380 | return 0; | ||
381 | |||
382 | out_free: | ||
383 | kfree(nmk_chip); | ||
384 | out_clk: | ||
385 | clk_disable(clk); | ||
386 | clk_put(clk); | ||
387 | out_release: | ||
388 | release_mem_region(res->start, resource_size(res)); | ||
389 | out: | ||
390 | dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, | ||
391 | pdata->first_gpio, pdata->first_gpio+31); | ||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | static int __exit nmk_gpio_remove(struct platform_device *dev) | ||
396 | { | ||
397 | struct nmk_gpio_chip *nmk_chip; | ||
398 | struct resource *res; | ||
399 | |||
400 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
401 | |||
402 | nmk_chip = platform_get_drvdata(dev); | ||
403 | gpiochip_remove(&nmk_chip->chip); | ||
404 | clk_disable(nmk_chip->clk); | ||
405 | clk_put(nmk_chip->clk); | ||
406 | kfree(nmk_chip); | ||
407 | release_mem_region(res->start, resource_size(res)); | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | |||
412 | static struct platform_driver nmk_gpio_driver = { | ||
413 | .driver = { | ||
414 | .owner = THIS_MODULE, | ||
415 | .name = "gpio", | ||
416 | }, | ||
417 | .probe = nmk_gpio_probe, | ||
418 | .remove = __exit_p(nmk_gpio_remove), | ||
419 | .suspend = NULL, /* to be done */ | ||
420 | .resume = NULL, | ||
421 | }; | ||
422 | |||
423 | static int __init nmk_gpio_init(void) | ||
424 | { | ||
425 | return platform_driver_register(&nmk_gpio_driver); | ||
426 | } | ||
427 | |||
428 | arch_initcall(nmk_gpio_init); | ||
429 | |||
430 | MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini"); | ||
431 | MODULE_DESCRIPTION("Nomadik GPIO Driver"); | ||
432 | MODULE_LICENSE("GPL"); | ||
433 | |||
434 | |||