diff options
author | Lennert Buytenhek <buytenh@wantstofly.org> | 2008-10-19 19:51:03 -0400 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2008-12-20 12:21:02 -0500 |
commit | 9569dae75f6f6987e79fa26cf6da3fc24006c996 (patch) | |
tree | b5982f41aea6ccee37af54fc8f7750aaff7e0bff /arch/arm/plat-orion/gpio.c | |
parent | 6fd7c7fe72a46dfd227fe8db0c7b6863af90a982 (diff) |
[ARM] Orion: share GPIO handling code
Split off Orion GPIO handling code into plat-orion/, and add
support for multiple sets of (32) GPIO pins.
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: Nicolas Pitre <nico@marvell.com>
Diffstat (limited to 'arch/arm/plat-orion/gpio.c')
-rw-r--r-- | arch/arm/plat-orion/gpio.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c new file mode 100644 index 000000000000..d86fc085e489 --- /dev/null +++ b/arch/arm/plat-orion/gpio.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * arch/arm/plat-orion/gpio.c | ||
3 | * | ||
4 | * Marvell Orion SoC GPIO handling. | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/bitops.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <asm/gpio.h> | ||
18 | |||
19 | static DEFINE_SPINLOCK(gpio_lock); | ||
20 | static const char *gpio_label[GPIO_MAX]; /* non null for allocated GPIOs */ | ||
21 | static unsigned long gpio_valid[BITS_TO_LONGS(GPIO_MAX)]; | ||
22 | |||
23 | static inline void __set_direction(unsigned pin, int input) | ||
24 | { | ||
25 | u32 u; | ||
26 | |||
27 | u = readl(GPIO_IO_CONF(pin)); | ||
28 | if (input) | ||
29 | u |= 1 << (pin & 31); | ||
30 | else | ||
31 | u &= ~(1 << (pin & 31)); | ||
32 | writel(u, GPIO_IO_CONF(pin)); | ||
33 | } | ||
34 | |||
35 | static void __set_level(unsigned pin, int high) | ||
36 | { | ||
37 | u32 u; | ||
38 | |||
39 | u = readl(GPIO_OUT(pin)); | ||
40 | if (high) | ||
41 | u |= 1 << (pin & 31); | ||
42 | else | ||
43 | u &= ~(1 << (pin & 31)); | ||
44 | writel(u, GPIO_OUT(pin)); | ||
45 | } | ||
46 | |||
47 | |||
48 | /* | ||
49 | * GENERIC_GPIO primitives. | ||
50 | */ | ||
51 | int gpio_direction_input(unsigned pin) | ||
52 | { | ||
53 | unsigned long flags; | ||
54 | |||
55 | if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { | ||
56 | pr_debug("%s: invalid GPIO %d\n", __func__, pin); | ||
57 | return -EINVAL; | ||
58 | } | ||
59 | |||
60 | spin_lock_irqsave(&gpio_lock, flags); | ||
61 | |||
62 | /* | ||
63 | * Some callers might not have used gpio_request(), | ||
64 | * so flag this pin as requested now. | ||
65 | */ | ||
66 | if (gpio_label[pin] == NULL) | ||
67 | gpio_label[pin] = "?"; | ||
68 | |||
69 | /* | ||
70 | * Configure GPIO direction. | ||
71 | */ | ||
72 | __set_direction(pin, 1); | ||
73 | |||
74 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | EXPORT_SYMBOL(gpio_direction_input); | ||
79 | |||
80 | int gpio_direction_output(unsigned pin, int value) | ||
81 | { | ||
82 | unsigned long flags; | ||
83 | u32 u; | ||
84 | |||
85 | if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { | ||
86 | pr_debug("%s: invalid GPIO %d\n", __func__, pin); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | spin_lock_irqsave(&gpio_lock, flags); | ||
91 | |||
92 | /* | ||
93 | * Some callers might not have used gpio_request(), | ||
94 | * so flag this pin as requested now. | ||
95 | */ | ||
96 | if (gpio_label[pin] == NULL) | ||
97 | gpio_label[pin] = "?"; | ||
98 | |||
99 | /* | ||
100 | * Disable blinking. | ||
101 | */ | ||
102 | u = readl(GPIO_BLINK_EN(pin)); | ||
103 | u &= ~(1 << (pin & 31)); | ||
104 | writel(u, GPIO_BLINK_EN(pin)); | ||
105 | |||
106 | /* | ||
107 | * Configure GPIO output value. | ||
108 | */ | ||
109 | __set_level(pin, value); | ||
110 | |||
111 | /* | ||
112 | * Configure GPIO direction. | ||
113 | */ | ||
114 | __set_direction(pin, 0); | ||
115 | |||
116 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | EXPORT_SYMBOL(gpio_direction_output); | ||
121 | |||
122 | int gpio_get_value(unsigned pin) | ||
123 | { | ||
124 | int val; | ||
125 | |||
126 | if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31))) | ||
127 | val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin)); | ||
128 | else | ||
129 | val = readl(GPIO_OUT(pin)); | ||
130 | |||
131 | return (val >> (pin & 31)) & 1; | ||
132 | } | ||
133 | EXPORT_SYMBOL(gpio_get_value); | ||
134 | |||
135 | void gpio_set_value(unsigned pin, int value) | ||
136 | { | ||
137 | unsigned long flags; | ||
138 | u32 u; | ||
139 | |||
140 | spin_lock_irqsave(&gpio_lock, flags); | ||
141 | |||
142 | /* | ||
143 | * Disable blinking. | ||
144 | */ | ||
145 | u = readl(GPIO_BLINK_EN(pin)); | ||
146 | u &= ~(1 << (pin & 31)); | ||
147 | writel(u, GPIO_BLINK_EN(pin)); | ||
148 | |||
149 | /* | ||
150 | * Configure GPIO output value. | ||
151 | */ | ||
152 | __set_level(pin, value); | ||
153 | |||
154 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
155 | } | ||
156 | EXPORT_SYMBOL(gpio_set_value); | ||
157 | |||
158 | int gpio_request(unsigned pin, const char *label) | ||
159 | { | ||
160 | unsigned long flags; | ||
161 | int ret; | ||
162 | |||
163 | if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { | ||
164 | pr_debug("%s: invalid GPIO %d\n", __func__, pin); | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | |||
168 | spin_lock_irqsave(&gpio_lock, flags); | ||
169 | if (gpio_label[pin] == NULL) { | ||
170 | gpio_label[pin] = label ? label : "?"; | ||
171 | ret = 0; | ||
172 | } else { | ||
173 | pr_debug("%s: GPIO %d already used as %s\n", | ||
174 | __func__, pin, gpio_label[pin]); | ||
175 | ret = -EBUSY; | ||
176 | } | ||
177 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | EXPORT_SYMBOL(gpio_request); | ||
182 | |||
183 | void gpio_free(unsigned pin) | ||
184 | { | ||
185 | if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { | ||
186 | pr_debug("%s: invalid GPIO %d\n", __func__, pin); | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | if (gpio_label[pin] == NULL) | ||
191 | pr_warning("%s: GPIO %d already freed\n", __func__, pin); | ||
192 | else | ||
193 | gpio_label[pin] = NULL; | ||
194 | } | ||
195 | EXPORT_SYMBOL(gpio_free); | ||
196 | |||
197 | |||
198 | /* | ||
199 | * Orion-specific GPIO API extensions. | ||
200 | */ | ||
201 | void __init orion_gpio_set_unused(unsigned pin) | ||
202 | { | ||
203 | /* | ||
204 | * Configure as output, drive low. | ||
205 | */ | ||
206 | __set_level(pin, 0); | ||
207 | __set_direction(pin, 0); | ||
208 | } | ||
209 | |||
210 | void __init orion_gpio_set_valid(unsigned pin, int valid) | ||
211 | { | ||
212 | if (valid) | ||
213 | __set_bit(pin, gpio_valid); | ||
214 | else | ||
215 | __clear_bit(pin, gpio_valid); | ||
216 | } | ||
217 | |||
218 | void orion_gpio_set_blink(unsigned pin, int blink) | ||
219 | { | ||
220 | unsigned long flags; | ||
221 | u32 u; | ||
222 | |||
223 | spin_lock_irqsave(&gpio_lock, flags); | ||
224 | |||
225 | /* | ||
226 | * Set output value to zero. | ||
227 | */ | ||
228 | __set_level(pin, 0); | ||
229 | |||
230 | u = readl(GPIO_BLINK_EN(pin)); | ||
231 | if (blink) | ||
232 | u |= 1 << (pin & 31); | ||
233 | else | ||
234 | u &= ~(1 << (pin & 31)); | ||
235 | writel(u, GPIO_BLINK_EN(pin)); | ||
236 | |||
237 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
238 | } | ||
239 | EXPORT_SYMBOL(orion_gpio_set_blink); | ||