diff options
Diffstat (limited to 'drivers/gpio/gpio-clps711x.c')
-rw-r--r-- | drivers/gpio/gpio-clps711x.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c new file mode 100644 index 000000000000..ea21822b3de9 --- /dev/null +++ b/drivers/gpio/gpio-clps711x.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * CLPS711X GPIO driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/io.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | |||
19 | #include <mach/hardware.h> | ||
20 | |||
21 | #define CLPS711X_GPIO_PORTS 5 | ||
22 | #define CLPS711X_GPIO_NAME "gpio-clps711x" | ||
23 | |||
24 | struct clps711x_gpio { | ||
25 | struct gpio_chip chip[CLPS711X_GPIO_PORTS]; | ||
26 | spinlock_t lock; | ||
27 | }; | ||
28 | |||
29 | static void __iomem *clps711x_ports[] = { | ||
30 | CLPS711X_VIRT_BASE + PADR, | ||
31 | CLPS711X_VIRT_BASE + PBDR, | ||
32 | CLPS711X_VIRT_BASE + PCDR, | ||
33 | CLPS711X_VIRT_BASE + PDDR, | ||
34 | CLPS711X_VIRT_BASE + PEDR, | ||
35 | }; | ||
36 | |||
37 | static void __iomem *clps711x_pdirs[] = { | ||
38 | CLPS711X_VIRT_BASE + PADDR, | ||
39 | CLPS711X_VIRT_BASE + PBDDR, | ||
40 | CLPS711X_VIRT_BASE + PCDDR, | ||
41 | CLPS711X_VIRT_BASE + PDDDR, | ||
42 | CLPS711X_VIRT_BASE + PEDDR, | ||
43 | }; | ||
44 | |||
45 | #define clps711x_port(x) clps711x_ports[x->base / 8] | ||
46 | #define clps711x_pdir(x) clps711x_pdirs[x->base / 8] | ||
47 | |||
48 | static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset) | ||
49 | { | ||
50 | return !!readb(clps711x_port(chip)) & (1 << offset); | ||
51 | } | ||
52 | |||
53 | static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset, | ||
54 | int value) | ||
55 | { | ||
56 | int tmp; | ||
57 | unsigned long flags; | ||
58 | struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); | ||
59 | |||
60 | spin_lock_irqsave(&gpio->lock, flags); | ||
61 | tmp = readb(clps711x_port(chip)) & ~(1 << offset); | ||
62 | if (value) | ||
63 | tmp |= 1 << offset; | ||
64 | writeb(tmp, clps711x_port(chip)); | ||
65 | spin_unlock_irqrestore(&gpio->lock, flags); | ||
66 | } | ||
67 | |||
68 | static int gpio_clps711x_direction_in(struct gpio_chip *chip, unsigned offset) | ||
69 | { | ||
70 | int tmp; | ||
71 | unsigned long flags; | ||
72 | struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); | ||
73 | |||
74 | spin_lock_irqsave(&gpio->lock, flags); | ||
75 | tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); | ||
76 | writeb(tmp, clps711x_pdir(chip)); | ||
77 | spin_unlock_irqrestore(&gpio->lock, flags); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int gpio_clps711x_direction_out(struct gpio_chip *chip, unsigned offset, | ||
83 | int value) | ||
84 | { | ||
85 | int tmp; | ||
86 | unsigned long flags; | ||
87 | struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); | ||
88 | |||
89 | spin_lock_irqsave(&gpio->lock, flags); | ||
90 | tmp = readb(clps711x_pdir(chip)) | (1 << offset); | ||
91 | writeb(tmp, clps711x_pdir(chip)); | ||
92 | tmp = readb(clps711x_port(chip)) & ~(1 << offset); | ||
93 | if (value) | ||
94 | tmp |= 1 << offset; | ||
95 | writeb(tmp, clps711x_port(chip)); | ||
96 | spin_unlock_irqrestore(&gpio->lock, flags); | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | struct clps711x_gpio_port { | ||
102 | char *name; | ||
103 | int nr; | ||
104 | }; | ||
105 | |||
106 | static const struct clps711x_gpio_port clps711x_gpio_ports[] __initconst = { | ||
107 | { "PORTA", 8, }, | ||
108 | { "PORTB", 8, }, | ||
109 | { "PORTC", 8, }, | ||
110 | { "PORTD", 8, }, | ||
111 | { "PORTE", 3, }, | ||
112 | }; | ||
113 | |||
114 | static int __init gpio_clps711x_init(void) | ||
115 | { | ||
116 | int i; | ||
117 | struct platform_device *pdev; | ||
118 | struct clps711x_gpio *gpio; | ||
119 | |||
120 | pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0); | ||
121 | if (!pdev) { | ||
122 | pr_err("Cannot create platform device: %s\n", | ||
123 | CLPS711X_GPIO_NAME); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | platform_device_add(pdev); | ||
128 | |||
129 | gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio), | ||
130 | GFP_KERNEL); | ||
131 | if (!gpio) { | ||
132 | dev_err(&pdev->dev, "GPIO allocating memory error\n"); | ||
133 | platform_device_del(pdev); | ||
134 | platform_device_put(pdev); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | platform_set_drvdata(pdev, gpio); | ||
139 | |||
140 | spin_lock_init(&gpio->lock); | ||
141 | |||
142 | for (i = 0; i < CLPS711X_GPIO_PORTS; i++) { | ||
143 | gpio->chip[i].owner = THIS_MODULE; | ||
144 | gpio->chip[i].dev = &pdev->dev; | ||
145 | gpio->chip[i].label = clps711x_gpio_ports[i].name; | ||
146 | gpio->chip[i].base = i * 8; | ||
147 | gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr; | ||
148 | gpio->chip[i].direction_input = gpio_clps711x_direction_in; | ||
149 | gpio->chip[i].get = gpio_clps711x_get; | ||
150 | gpio->chip[i].direction_output = gpio_clps711x_direction_out; | ||
151 | gpio->chip[i].set = gpio_clps711x_set; | ||
152 | WARN_ON(gpiochip_add(&gpio->chip[i])); | ||
153 | } | ||
154 | |||
155 | dev_info(&pdev->dev, "GPIO driver initialized\n"); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | arch_initcall(gpio_clps711x_init); | ||
160 | |||
161 | MODULE_LICENSE("GPL v2"); | ||
162 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); | ||
163 | MODULE_DESCRIPTION("CLPS711X GPIO driver"); | ||