]>
Commit | Line | Data |
---|---|---|
da34aae5 SZ |
1 | /* |
2 | * ADI GPIO2 Abstraction Layer | |
3 | * Support BF54x, BF60x and future processors. | |
4 | * | |
5 | * Copyright 2008-2013 Analog Devices Inc. | |
6 | * | |
7 | * Licensed under the GPL-2 or later | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
1221ce45 | 11 | #include <linux/errno.h> |
da34aae5 | 12 | #include <asm/gpio.h> |
da34aae5 SZ |
13 | |
14 | #define RESOURCE_LABEL_SIZE 16 | |
15 | ||
16 | static struct str_ident { | |
17 | char name[RESOURCE_LABEL_SIZE]; | |
18 | } str_ident[MAX_RESOURCES]; | |
19 | ||
20 | static void gpio_error(unsigned gpio) | |
21 | { | |
22 | printf("adi_gpio2: GPIO %d wasn't requested!\n", gpio); | |
23 | } | |
24 | ||
25 | static void set_label(unsigned short ident, const char *label) | |
26 | { | |
27 | if (label) { | |
28 | strncpy(str_ident[ident].name, label, | |
29 | RESOURCE_LABEL_SIZE); | |
30 | str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; | |
31 | } | |
32 | } | |
33 | ||
34 | static char *get_label(unsigned short ident) | |
35 | { | |
36 | return *str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"; | |
37 | } | |
38 | ||
39 | static int cmp_label(unsigned short ident, const char *label) | |
40 | { | |
41 | if (label == NULL) | |
42 | printf("adi_gpio2: please provide none-null label\n"); | |
43 | ||
44 | if (label) | |
45 | return strcmp(str_ident[ident].name, label); | |
46 | else | |
47 | return -EINVAL; | |
48 | } | |
49 | ||
50 | #define map_entry(m, i) reserved_##m##_map[gpio_bank(i)] | |
51 | #define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) | |
52 | #define reserve(m, i) (map_entry(m, i) |= gpio_bit(i)) | |
53 | #define unreserve(m, i) (map_entry(m, i) &= ~gpio_bit(i)) | |
54 | #define DECLARE_RESERVED_MAP(m, c) unsigned short reserved_##m##_map[c] | |
55 | ||
56 | static DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); | |
57 | static DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); | |
58 | ||
59 | inline int check_gpio(unsigned gpio) | |
60 | { | |
61 | #if defined(CONFIG_BF54x) | |
62 | if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 || | |
63 | gpio == GPIO_PH14 || gpio == GPIO_PH15 || | |
64 | gpio == GPIO_PJ14 || gpio == GPIO_PJ15) | |
65 | return -EINVAL; | |
66 | #endif | |
67 | if (gpio >= MAX_GPIOS) | |
68 | return -EINVAL; | |
69 | return 0; | |
70 | } | |
71 | ||
72 | static void port_setup(unsigned gpio, unsigned short usage) | |
73 | { | |
74 | #if defined(CONFIG_BF54x) | |
75 | if (usage == GPIO_USAGE) | |
76 | gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); | |
77 | else | |
78 | gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); | |
79 | #else | |
80 | if (usage == GPIO_USAGE) | |
81 | gpio_array[gpio_bank(gpio)]->port_fer_clear = gpio_bit(gpio); | |
82 | else | |
83 | gpio_array[gpio_bank(gpio)]->port_fer_set = gpio_bit(gpio); | |
84 | #endif | |
da34aae5 SZ |
85 | } |
86 | ||
87 | inline void portmux_setup(unsigned short per) | |
88 | { | |
89 | u32 pmux; | |
90 | u16 ident = P_IDENT(per); | |
91 | u16 function = P_FUNCT2MUX(per); | |
92 | ||
93 | pmux = gpio_array[gpio_bank(ident)]->port_mux; | |
94 | ||
95 | pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); | |
96 | pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); | |
97 | ||
98 | gpio_array[gpio_bank(ident)]->port_mux = pmux; | |
99 | } | |
100 | ||
101 | inline u16 get_portmux(unsigned short per) | |
102 | { | |
103 | u32 pmux; | |
104 | u16 ident = P_IDENT(per); | |
105 | ||
106 | pmux = gpio_array[gpio_bank(ident)]->port_mux; | |
107 | ||
108 | return pmux >> (2 * gpio_sub_n(ident)) & 0x3; | |
109 | } | |
110 | ||
111 | unsigned short get_gpio_dir(unsigned gpio) | |
112 | { | |
113 | return 0x01 & | |
114 | (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); | |
115 | } | |
116 | ||
117 | /*********************************************************** | |
118 | * | |
119 | * FUNCTIONS: Peripheral Resource Allocation | |
120 | * and PortMux Setup | |
121 | * | |
122 | * INPUTS/OUTPUTS: | |
123 | * per Peripheral Identifier | |
124 | * label String | |
125 | * | |
126 | * DESCRIPTION: Peripheral Resource Allocation and Setup API | |
127 | **************************************************************/ | |
128 | ||
129 | int peripheral_request(unsigned short per, const char *label) | |
130 | { | |
131 | unsigned short ident = P_IDENT(per); | |
132 | ||
133 | /* | |
134 | * Don't cares are pins with only one dedicated function | |
135 | */ | |
136 | ||
137 | if (per & P_DONTCARE) | |
138 | return 0; | |
139 | ||
140 | if (!(per & P_DEFINED)) | |
7c84319a | 141 | return -EINVAL; |
da34aae5 SZ |
142 | |
143 | BUG_ON(ident >= MAX_RESOURCES); | |
144 | ||
145 | /* If a pin can be muxed as either GPIO or peripheral, make | |
146 | * sure it is not already a GPIO pin when we request it. | |
147 | */ | |
148 | if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { | |
149 | printf("%s: Peripheral %d is already reserved as GPIO by %s!\n", | |
150 | __func__, ident, get_label(ident)); | |
151 | return -EBUSY; | |
152 | } | |
153 | ||
154 | if (unlikely(is_reserved(peri, ident, 1))) { | |
155 | /* | |
156 | * Pin functions like AMC address strobes my | |
157 | * be requested and used by several drivers | |
158 | */ | |
159 | ||
160 | if (!((per & P_MAYSHARE) && | |
161 | get_portmux(per) == P_FUNCT2MUX(per))) { | |
162 | /* | |
163 | * Allow that the identical pin function can | |
164 | * be requested from the same driver twice | |
165 | */ | |
166 | ||
167 | if (cmp_label(ident, label) == 0) | |
168 | goto anyway; | |
169 | ||
170 | printf("%s: Peripheral %d function %d is already " | |
171 | "reserved by %s!\n", __func__, ident, | |
172 | P_FUNCT2MUX(per), get_label(ident)); | |
173 | return -EBUSY; | |
174 | } | |
175 | } | |
176 | ||
177 | anyway: | |
178 | reserve(peri, ident); | |
179 | ||
180 | portmux_setup(per); | |
181 | port_setup(ident, PERIPHERAL_USAGE); | |
182 | ||
183 | set_label(ident, label); | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | int peripheral_request_list(const unsigned short per[], const char *label) | |
189 | { | |
190 | u16 cnt; | |
191 | int ret; | |
192 | ||
193 | for (cnt = 0; per[cnt] != 0; cnt++) { | |
194 | ret = peripheral_request(per[cnt], label); | |
195 | ||
196 | if (ret < 0) { | |
197 | for (; cnt > 0; cnt--) | |
198 | peripheral_free(per[cnt - 1]); | |
199 | ||
200 | return ret; | |
201 | } | |
202 | } | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | void peripheral_free(unsigned short per) | |
208 | { | |
209 | unsigned short ident = P_IDENT(per); | |
210 | ||
211 | if (per & P_DONTCARE) | |
212 | return; | |
213 | ||
214 | if (!(per & P_DEFINED)) | |
215 | return; | |
216 | ||
217 | if (unlikely(!is_reserved(peri, ident, 0))) | |
218 | return; | |
219 | ||
220 | if (!(per & P_MAYSHARE)) | |
221 | port_setup(ident, GPIO_USAGE); | |
222 | ||
223 | unreserve(peri, ident); | |
224 | ||
225 | set_label(ident, "free"); | |
226 | } | |
227 | ||
228 | void peripheral_free_list(const unsigned short per[]) | |
229 | { | |
230 | u16 cnt; | |
231 | for (cnt = 0; per[cnt] != 0; cnt++) | |
232 | peripheral_free(per[cnt]); | |
233 | } | |
234 | ||
235 | /*********************************************************** | |
236 | * | |
237 | * FUNCTIONS: GPIO Driver | |
238 | * | |
239 | * INPUTS/OUTPUTS: | |
240 | * gpio PIO Number between 0 and MAX_GPIOS | |
241 | * label String | |
242 | * | |
243 | * DESCRIPTION: GPIO Driver API | |
244 | **************************************************************/ | |
245 | ||
246 | int gpio_request(unsigned gpio, const char *label) | |
247 | { | |
248 | if (check_gpio(gpio) < 0) | |
249 | return -EINVAL; | |
250 | ||
251 | /* | |
252 | * Allow that the identical GPIO can | |
253 | * be requested from the same driver twice | |
254 | * Do nothing and return - | |
255 | */ | |
256 | ||
257 | if (cmp_label(gpio, label) == 0) | |
258 | return 0; | |
259 | ||
260 | if (unlikely(is_reserved(gpio, gpio, 1))) { | |
261 | printf("adi_gpio2: GPIO %d is already reserved by %s!\n", | |
262 | gpio, get_label(gpio)); | |
263 | return -EBUSY; | |
264 | } | |
265 | if (unlikely(is_reserved(peri, gpio, 1))) { | |
266 | printf("adi_gpio2: GPIO %d is already reserved as Peripheral " | |
267 | "by %s!\n", gpio, get_label(gpio)); | |
268 | return -EBUSY; | |
269 | } | |
270 | ||
271 | reserve(gpio, gpio); | |
272 | set_label(gpio, label); | |
273 | ||
274 | port_setup(gpio, GPIO_USAGE); | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | int gpio_free(unsigned gpio) | |
280 | { | |
281 | if (check_gpio(gpio) < 0) | |
282 | return -1; | |
283 | ||
284 | if (unlikely(!is_reserved(gpio, gpio, 0))) { | |
285 | gpio_error(gpio); | |
286 | return -1; | |
287 | } | |
288 | ||
289 | unreserve(gpio, gpio); | |
290 | ||
291 | set_label(gpio, "free"); | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | #ifdef ADI_SPECIAL_GPIO_BANKS | |
297 | static DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); | |
298 | ||
299 | int special_gpio_request(unsigned gpio, const char *label) | |
300 | { | |
301 | /* | |
302 | * Allow that the identical GPIO can | |
303 | * be requested from the same driver twice | |
304 | * Do nothing and return - | |
305 | */ | |
306 | ||
307 | if (cmp_label(gpio, label) == 0) | |
308 | return 0; | |
309 | ||
310 | if (unlikely(is_reserved(special_gpio, gpio, 1))) { | |
311 | printf("adi_gpio2: GPIO %d is already reserved by %s!\n", | |
312 | gpio, get_label(gpio)); | |
313 | return -EBUSY; | |
314 | } | |
315 | if (unlikely(is_reserved(peri, gpio, 1))) { | |
316 | printf("adi_gpio2: GPIO %d is already reserved as Peripheral " | |
317 | "by %s!\n", gpio, get_label(gpio)); | |
318 | ||
319 | return -EBUSY; | |
320 | } | |
321 | ||
322 | reserve(special_gpio, gpio); | |
323 | reserve(peri, gpio); | |
324 | ||
325 | set_label(gpio, label); | |
326 | port_setup(gpio, GPIO_USAGE); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | void special_gpio_free(unsigned gpio) | |
332 | { | |
333 | if (unlikely(!is_reserved(special_gpio, gpio, 0))) { | |
334 | gpio_error(gpio); | |
335 | return; | |
336 | } | |
337 | ||
2ced773a AL |
338 | unreserve(special_gpio, gpio); |
339 | unreserve(peri, gpio); | |
da34aae5 SZ |
340 | set_label(gpio, "free"); |
341 | } | |
342 | #endif | |
343 | ||
344 | static inline void __gpio_direction_input(unsigned gpio) | |
345 | { | |
346 | gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); | |
347 | #if defined(CONFIG_BF54x) | |
348 | gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); | |
349 | #else | |
350 | gpio_array[gpio_bank(gpio)]->inen_set = gpio_bit(gpio); | |
351 | #endif | |
352 | } | |
353 | ||
354 | int gpio_direction_input(unsigned gpio) | |
355 | { | |
356 | unsigned long flags; | |
357 | ||
358 | if (!is_reserved(gpio, gpio, 0)) { | |
359 | gpio_error(gpio); | |
360 | return -EINVAL; | |
361 | } | |
362 | ||
363 | local_irq_save(flags); | |
364 | __gpio_direction_input(gpio); | |
365 | local_irq_restore(flags); | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | int gpio_set_value(unsigned gpio, int arg) | |
371 | { | |
372 | if (arg) | |
373 | gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); | |
374 | else | |
375 | gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | int gpio_direction_output(unsigned gpio, int value) | |
381 | { | |
382 | unsigned long flags; | |
383 | ||
384 | if (!is_reserved(gpio, gpio, 0)) { | |
385 | gpio_error(gpio); | |
386 | return -EINVAL; | |
387 | } | |
388 | ||
389 | local_irq_save(flags); | |
390 | ||
391 | #if defined(CONFIG_BF54x) | |
392 | gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); | |
393 | #else | |
394 | gpio_array[gpio_bank(gpio)]->inen_clear = gpio_bit(gpio); | |
395 | #endif | |
396 | gpio_set_value(gpio, value); | |
397 | gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); | |
398 | ||
399 | local_irq_restore(flags); | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
404 | int gpio_get_value(unsigned gpio) | |
405 | { | |
406 | return 1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); | |
407 | } | |
408 | ||
409 | void gpio_labels(void) | |
410 | { | |
411 | int c, gpio; | |
412 | ||
413 | for (c = 0; c < MAX_RESOURCES; c++) { | |
414 | gpio = is_reserved(gpio, c, 1); | |
415 | if (!check_gpio(c) && gpio) | |
416 | printf("GPIO_%d:\t%s\tGPIO %s\n", c, get_label(c), | |
417 | get_gpio_dir(c) ? "OUTPUT" : "INPUT"); | |
418 | else if (is_reserved(peri, c, 1)) | |
419 | printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); | |
420 | else | |
421 | continue; | |
422 | } | |
423 | } |