aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorYoichi Yuasa <yyuasa@linux.com>2009-06-28 22:11:05 -0400
committerRalf Baechle <ralf@linux-mips.org>2009-07-03 10:45:25 -0400
commit27fdd325dace4a1ebfa10e93ba6f3d25f25df674 (patch)
tree8bf75bbbe8110c71e2b61b2669e69fa4da802e98 /drivers/gpio
parent69f16c9a8630edc64cb1d6f1bfca4ee7bc16279f (diff)
MIPS: Update VR41xx GPIO driver to use gpiolib
Signed-off-by: Yoichi Yuasa <yyuasa@linux.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/vr41xx_giu.c586
3 files changed, 593 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3582c39f9725..96dda81c9228 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -79,6 +79,12 @@ config GPIO_XILINX
79 help 79 help
80 Say yes here to support the Xilinx FPGA GPIO device 80 Say yes here to support the Xilinx FPGA GPIO device
81 81
82config GPIO_VR41XX
83 tristate "NEC VR4100 series General-purpose I/O Uint support"
84 depends on CPU_VR41XX
85 help
86 Say yes here to support the NEC VR4100 series General-purpose I/O Uint
87
82comment "I2C GPIO expanders:" 88comment "I2C GPIO expanders:"
83 89
84config GPIO_MAX732X 90config GPIO_MAX732X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ef90203e8f3c..9244c6fcd8be 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_GPIO_PL061) += pl061.o
13obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o 13obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
14obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o 14obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
15obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o 15obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
16obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c
new file mode 100644
index 000000000000..f691ffa95fbb
--- /dev/null
+++ b/drivers/gpio/vr41xx_giu.c
@@ -0,0 +1,586 @@
1/*
2 * Driver for NEC VR4100 series General-purpose I/O Unit.
3 *
4 * Copyright (C) 2002 MontaVista Software Inc.
5 * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com>
6 * Copyright (C) 2003-2009 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
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 as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22#include <linux/errno.h>
23#include <linux/fs.h>
24#include <linux/gpio.h>
25#include <linux/init.h>
26#include <linux/interrupt.h>
27#include <linux/io.h>
28#include <linux/irq.h>
29#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/platform_device.h>
32#include <linux/smp_lock.h>
33#include <linux/spinlock.h>
34#include <linux/types.h>
35
36#include <asm/vr41xx/giu.h>
37#include <asm/vr41xx/irq.h>
38#include <asm/vr41xx/vr41xx.h>
39
40MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");
41MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver");
42MODULE_LICENSE("GPL");
43
44#define GIUIOSELL 0x00
45#define GIUIOSELH 0x02
46#define GIUPIODL 0x04
47#define GIUPIODH 0x06
48#define GIUINTSTATL 0x08
49#define GIUINTSTATH 0x0a
50#define GIUINTENL 0x0c
51#define GIUINTENH 0x0e
52#define GIUINTTYPL 0x10
53#define GIUINTTYPH 0x12
54#define GIUINTALSELL 0x14
55#define GIUINTALSELH 0x16
56#define GIUINTHTSELL 0x18
57#define GIUINTHTSELH 0x1a
58#define GIUPODATL 0x1c
59#define GIUPODATEN 0x1c
60#define GIUPODATH 0x1e
61 #define PIOEN0 0x0100
62 #define PIOEN1 0x0200
63#define GIUPODAT 0x1e
64#define GIUFEDGEINHL 0x20
65#define GIUFEDGEINHH 0x22
66#define GIUREDGEINHL 0x24
67#define GIUREDGEINHH 0x26
68
69#define GIUUSEUPDN 0x1e0
70#define GIUTERMUPDN 0x1e2
71
72#define GPIO_HAS_PULLUPDOWN_IO 0x0001
73#define GPIO_HAS_OUTPUT_ENABLE 0x0002
74#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100
75
76enum {
77 GPIO_INPUT,
78 GPIO_OUTPUT,
79};
80
81static DEFINE_SPINLOCK(giu_lock);
82static unsigned long giu_flags;
83
84static void __iomem *giu_base;
85
86#define giu_read(offset) readw(giu_base + (offset))
87#define giu_write(offset, value) writew((value), giu_base + (offset))
88
89#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE)
90#define GIUINT_HIGH_OFFSET 16
91#define GIUINT_HIGH_MAX 32
92
93static inline u16 giu_set(u16 offset, u16 set)
94{
95 u16 data;
96
97 data = giu_read(offset);
98 data |= set;
99 giu_write(offset, data);
100
101 return data;
102}
103
104static inline u16 giu_clear(u16 offset, u16 clear)
105{
106 u16 data;
107
108 data = giu_read(offset);
109 data &= ~clear;
110 giu_write(offset, data);
111
112 return data;
113}
114
115static void ack_giuint_low(unsigned int irq)
116{
117 giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq));
118}
119
120static void mask_giuint_low(unsigned int irq)
121{
122 giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
123}
124
125static void mask_ack_giuint_low(unsigned int irq)
126{
127 unsigned int pin;
128
129 pin = GPIO_PIN_OF_IRQ(irq);
130 giu_clear(GIUINTENL, 1 << pin);
131 giu_write(GIUINTSTATL, 1 << pin);
132}
133
134static void unmask_giuint_low(unsigned int irq)
135{
136 giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
137}
138
139static struct irq_chip giuint_low_irq_chip = {
140 .name = "GIUINTL",
141 .ack = ack_giuint_low,
142 .mask = mask_giuint_low,
143 .mask_ack = mask_ack_giuint_low,
144 .unmask = unmask_giuint_low,
145};
146
147static void ack_giuint_high(unsigned int irq)
148{
149 giu_write(GIUINTSTATH,
150 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
151}
152
153static void mask_giuint_high(unsigned int irq)
154{
155 giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
156}
157
158static void mask_ack_giuint_high(unsigned int irq)
159{
160 unsigned int pin;
161
162 pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET;
163 giu_clear(GIUINTENH, 1 << pin);
164 giu_write(GIUINTSTATH, 1 << pin);
165}
166
167static void unmask_giuint_high(unsigned int irq)
168{
169 giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
170}
171
172static struct irq_chip giuint_high_irq_chip = {
173 .name = "GIUINTH",
174 .ack = ack_giuint_high,
175 .mask = mask_giuint_high,
176 .mask_ack = mask_ack_giuint_high,
177 .unmask = unmask_giuint_high,
178};
179
180static int giu_get_irq(unsigned int irq)
181{
182 u16 pendl, pendh, maskl, maskh;
183 int i;
184
185 pendl = giu_read(GIUINTSTATL);
186 pendh = giu_read(GIUINTSTATH);
187 maskl = giu_read(GIUINTENL);
188 maskh = giu_read(GIUINTENH);
189
190 maskl &= pendl;
191 maskh &= pendh;
192
193 if (maskl) {
194 for (i = 0; i < 16; i++) {
195 if (maskl & (1 << i))
196 return GIU_IRQ(i);
197 }
198 } else if (maskh) {
199 for (i = 0; i < 16; i++) {
200 if (maskh & (1 << i))
201 return GIU_IRQ(i + GIUINT_HIGH_OFFSET);
202 }
203 }
204
205 printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n",
206 maskl, pendl, maskh, pendh);
207
208 atomic_inc(&irq_err_count);
209
210 return -EINVAL;
211}
212
213void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger,
214 irq_signal_t signal)
215{
216 u16 mask;
217
218 if (pin < GIUINT_HIGH_OFFSET) {
219 mask = 1 << pin;
220 if (trigger != IRQ_TRIGGER_LEVEL) {
221 giu_set(GIUINTTYPL, mask);
222 if (signal == IRQ_SIGNAL_HOLD)
223 giu_set(GIUINTHTSELL, mask);
224 else
225 giu_clear(GIUINTHTSELL, mask);
226 if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
227 switch (trigger) {
228 case IRQ_TRIGGER_EDGE_FALLING:
229 giu_set(GIUFEDGEINHL, mask);
230 giu_clear(GIUREDGEINHL, mask);
231 break;
232 case IRQ_TRIGGER_EDGE_RISING:
233 giu_clear(GIUFEDGEINHL, mask);
234 giu_set(GIUREDGEINHL, mask);
235 break;
236 default:
237 giu_set(GIUFEDGEINHL, mask);
238 giu_set(GIUREDGEINHL, mask);
239 break;
240 }
241 }
242 set_irq_chip_and_handler(GIU_IRQ(pin),
243 &giuint_low_irq_chip,
244 handle_edge_irq);
245 } else {
246 giu_clear(GIUINTTYPL, mask);
247 giu_clear(GIUINTHTSELL, mask);
248 set_irq_chip_and_handler(GIU_IRQ(pin),
249 &giuint_low_irq_chip,
250 handle_level_irq);
251 }
252 giu_write(GIUINTSTATL, mask);
253 } else if (pin < GIUINT_HIGH_MAX) {
254 mask = 1 << (pin - GIUINT_HIGH_OFFSET);
255 if (trigger != IRQ_TRIGGER_LEVEL) {
256 giu_set(GIUINTTYPH, mask);
257 if (signal == IRQ_SIGNAL_HOLD)
258 giu_set(GIUINTHTSELH, mask);
259 else
260 giu_clear(GIUINTHTSELH, mask);
261 if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
262 switch (trigger) {
263 case IRQ_TRIGGER_EDGE_FALLING:
264 giu_set(GIUFEDGEINHH, mask);
265 giu_clear(GIUREDGEINHH, mask);
266 break;
267 case IRQ_TRIGGER_EDGE_RISING:
268 giu_clear(GIUFEDGEINHH, mask);
269 giu_set(GIUREDGEINHH, mask);
270 break;
271 default:
272 giu_set(GIUFEDGEINHH, mask);
273 giu_set(GIUREDGEINHH, mask);
274 break;
275 }
276 }
277 set_irq_chip_and_handler(GIU_IRQ(pin),
278 &giuint_high_irq_chip,
279 handle_edge_irq);
280 } else {
281 giu_clear(GIUINTTYPH, mask);
282 giu_clear(GIUINTHTSELH, mask);
283 set_irq_chip_and_handler(GIU_IRQ(pin),
284 &giuint_high_irq_chip,
285 handle_level_irq);
286 }
287 giu_write(GIUINTSTATH, mask);
288 }
289}
290EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger);
291
292void vr41xx_set_irq_level(unsigned int pin, irq_level_t level)
293{
294 u16 mask;
295
296 if (pin < GIUINT_HIGH_OFFSET) {
297 mask = 1 << pin;
298 if (level == IRQ_LEVEL_HIGH)
299 giu_set(GIUINTALSELL, mask);
300 else
301 giu_clear(GIUINTALSELL, mask);
302 giu_write(GIUINTSTATL, mask);
303 } else if (pin < GIUINT_HIGH_MAX) {
304 mask = 1 << (pin - GIUINT_HIGH_OFFSET);
305 if (level == IRQ_LEVEL_HIGH)
306 giu_set(GIUINTALSELH, mask);
307 else
308 giu_clear(GIUINTALSELH, mask);
309 giu_write(GIUINTSTATH, mask);
310 }
311}
312EXPORT_SYMBOL_GPL(vr41xx_set_irq_level);
313
314static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir)
315{
316 u16 offset, mask, reg;
317 unsigned long flags;
318
319 if (pin >= chip->ngpio)
320 return -EINVAL;
321
322 if (pin < 16) {
323 offset = GIUIOSELL;
324 mask = 1 << pin;
325 } else if (pin < 32) {
326 offset = GIUIOSELH;
327 mask = 1 << (pin - 16);
328 } else {
329 if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) {
330 offset = GIUPODATEN;
331 mask = 1 << (pin - 32);
332 } else {
333 switch (pin) {
334 case 48:
335 offset = GIUPODATH;
336 mask = PIOEN0;
337 break;
338 case 49:
339 offset = GIUPODATH;
340 mask = PIOEN1;
341 break;
342 default:
343 return -EINVAL;
344 }
345 }
346 }
347
348 spin_lock_irqsave(&giu_lock, flags);
349
350 reg = giu_read(offset);
351 if (dir == GPIO_OUTPUT)
352 reg |= mask;
353 else
354 reg &= ~mask;
355 giu_write(offset, reg);
356
357 spin_unlock_irqrestore(&giu_lock, flags);
358
359 return 0;
360}
361
362int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
363{
364 u16 reg, mask;
365 unsigned long flags;
366
367 if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO)
368 return -EPERM;
369
370 if (pin >= 15)
371 return -EINVAL;
372
373 mask = 1 << pin;
374
375 spin_lock_irqsave(&giu_lock, flags);
376
377 if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) {
378 reg = giu_read(GIUTERMUPDN);
379 if (pull == GPIO_PULL_UP)
380 reg |= mask;
381 else
382 reg &= ~mask;
383 giu_write(GIUTERMUPDN, reg);
384
385 reg = giu_read(GIUUSEUPDN);
386 reg |= mask;
387 giu_write(GIUUSEUPDN, reg);
388 } else {
389 reg = giu_read(GIUUSEUPDN);
390 reg &= ~mask;
391 giu_write(GIUUSEUPDN, reg);
392 }
393
394 spin_unlock_irqrestore(&giu_lock, flags);
395
396 return 0;
397}
398EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
399
400static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin)
401{
402 u16 reg, mask;
403
404 if (pin >= chip->ngpio)
405 return -EINVAL;
406
407 if (pin < 16) {
408 reg = giu_read(GIUPIODL);
409 mask = 1 << pin;
410 } else if (pin < 32) {
411 reg = giu_read(GIUPIODH);
412 mask = 1 << (pin - 16);
413 } else if (pin < 48) {
414 reg = giu_read(GIUPODATL);
415 mask = 1 << (pin - 32);
416 } else {
417 reg = giu_read(GIUPODATH);
418 mask = 1 << (pin - 48);
419 }
420
421 if (reg & mask)
422 return 1;
423
424 return 0;
425}
426
427static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin,
428 int value)
429{
430 u16 offset, mask, reg;
431 unsigned long flags;
432
433 if (pin >= chip->ngpio)
434 return;
435
436 if (pin < 16) {
437 offset = GIUPIODL;
438 mask = 1 << pin;
439 } else if (pin < 32) {
440 offset = GIUPIODH;
441 mask = 1 << (pin - 16);
442 } else if (pin < 48) {
443 offset = GIUPODATL;
444 mask = 1 << (pin - 32);
445 } else {
446 offset = GIUPODATH;
447 mask = 1 << (pin - 48);
448 }
449
450 spin_lock_irqsave(&giu_lock, flags);
451
452 reg = giu_read(offset);
453 if (value)
454 reg |= mask;
455 else
456 reg &= ~mask;
457 giu_write(offset, reg);
458
459 spin_unlock_irqrestore(&giu_lock, flags);
460}
461
462
463static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
464{
465 return giu_set_direction(chip, offset, GPIO_INPUT);
466}
467
468static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
469 int value)
470{
471 vr41xx_gpio_set(chip, offset, value);
472
473 return giu_set_direction(chip, offset, GPIO_OUTPUT);
474}
475
476static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
477{
478 if (offset >= chip->ngpio)
479 return -EINVAL;
480
481 return GIU_IRQ_BASE + offset;
482}
483
484static struct gpio_chip vr41xx_gpio_chip = {
485 .label = "vr41xx",
486 .owner = THIS_MODULE,
487 .direction_input = vr41xx_gpio_direction_input,
488 .get = vr41xx_gpio_get,
489 .direction_output = vr41xx_gpio_direction_output,
490 .set = vr41xx_gpio_set,
491 .to_irq = vr41xx_gpio_to_irq,
492};
493
494static int __devinit giu_probe(struct platform_device *pdev)
495{
496 struct resource *res;
497 unsigned int trigger, i, pin;
498 struct irq_chip *chip;
499 int irq, retval;
500
501 switch (pdev->id) {
502 case GPIO_50PINS_PULLUPDOWN:
503 giu_flags = GPIO_HAS_PULLUPDOWN_IO;
504 vr41xx_gpio_chip.ngpio = 50;
505 break;
506 case GPIO_36PINS:
507 vr41xx_gpio_chip.ngpio = 36;
508 break;
509 case GPIO_48PINS_EDGE_SELECT:
510 giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT;
511 vr41xx_gpio_chip.ngpio = 48;
512 break;
513 default:
514 dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id);
515 return -ENODEV;
516 }
517
518 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
519 if (!res)
520 return -EBUSY;
521
522 giu_base = ioremap(res->start, res->end - res->start + 1);
523 if (!giu_base)
524 return -ENOMEM;
525
526 vr41xx_gpio_chip.dev = &pdev->dev;
527
528 retval = gpiochip_add(&vr41xx_gpio_chip);
529
530 giu_write(GIUINTENL, 0);
531 giu_write(GIUINTENH, 0);
532
533 trigger = giu_read(GIUINTTYPH) << 16;
534 trigger |= giu_read(GIUINTTYPL);
535 for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
536 pin = GPIO_PIN_OF_IRQ(i);
537 if (pin < GIUINT_HIGH_OFFSET)
538 chip = &giuint_low_irq_chip;
539 else
540 chip = &giuint_high_irq_chip;
541
542 if (trigger & (1 << pin))
543 set_irq_chip_and_handler(i, chip, handle_edge_irq);
544 else
545 set_irq_chip_and_handler(i, chip, handle_level_irq);
546
547 }
548
549 irq = platform_get_irq(pdev, 0);
550 if (irq < 0 || irq >= nr_irqs)
551 return -EBUSY;
552
553 return cascade_irq(irq, giu_get_irq);
554}
555
556static int __devexit giu_remove(struct platform_device *pdev)
557{
558 if (giu_base) {
559 iounmap(giu_base);
560 giu_base = NULL;
561 }
562
563 return 0;
564}
565
566static struct platform_driver giu_device_driver = {
567 .probe = giu_probe,
568 .remove = __devexit_p(giu_remove),
569 .driver = {
570 .name = "GIU",
571 .owner = THIS_MODULE,
572 },
573};
574
575static int __init vr41xx_giu_init(void)
576{
577 return platform_driver_register(&giu_device_driver);
578}
579
580static void __exit vr41xx_giu_exit(void)
581{
582 platform_driver_unregister(&giu_device_driver);
583}
584
585module_init(vr41xx_giu_init);
586module_exit(vr41xx_giu_exit);