]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/blackfin/mach-bf548/gpio.c
Blackfin arch: store labels so we later know who allocated GPIO/Peripheral resources
[karo-tx-linux.git] / arch / blackfin / mach-bf548 / gpio.c
1 /*
2  * File:         arch/blackfin/mach-bf548/gpio.c
3  * Based on:
4  * Author:       Michael Hennerich (hennerich@blackfin.uclinux.org)
5  *
6  * Created:
7  * Description:  GPIO Abstraction Layer
8  *
9  * Modified:
10  *               Copyright 2007 Analog Devices Inc.
11  *
12  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, see the file COPYING, or write
26  * to the Free Software Foundation, Inc.,
27  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
28  */
29
30 #include <linux/module.h>
31 #include <linux/err.h>
32 #include <asm/blackfin.h>
33 #include <asm/gpio.h>
34 #include <asm/portmux.h>
35 #include <linux/irq.h>
36
37 static struct gpio_port_t *gpio_array[gpio_bank(MAX_BLACKFIN_GPIOS)] = {
38         (struct gpio_port_t *)PORTA_FER,
39         (struct gpio_port_t *)PORTB_FER,
40         (struct gpio_port_t *)PORTC_FER,
41         (struct gpio_port_t *)PORTD_FER,
42         (struct gpio_port_t *)PORTE_FER,
43         (struct gpio_port_t *)PORTF_FER,
44         (struct gpio_port_t *)PORTG_FER,
45         (struct gpio_port_t *)PORTH_FER,
46         (struct gpio_port_t *)PORTI_FER,
47         (struct gpio_port_t *)PORTJ_FER,
48 };
49
50 static unsigned short reserved_gpio_map[gpio_bank(MAX_BLACKFIN_GPIOS)];
51 static unsigned short reserved_peri_map[gpio_bank(MAX_BLACKFIN_GPIOS)];
52 char *str_ident = NULL;
53
54 #define RESOURCE_LABEL_SIZE 16
55
56 inline int check_gpio(unsigned short gpio)
57 {
58         if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15
59             || gpio == GPIO_PH14 || gpio == GPIO_PH15
60             || gpio == GPIO_PJ14 || gpio == GPIO_PJ15
61             || gpio > MAX_BLACKFIN_GPIOS)
62                 return -EINVAL;
63         return 0;
64 }
65
66 inline void portmux_setup(unsigned short portno, unsigned short function)
67 {
68         u32 pmux;
69
70         pmux = gpio_array[gpio_bank(portno)]->port_mux;
71
72         pmux &= ~(0x3 << (2 * gpio_sub_n(portno)));
73         pmux |= (function & 0x3) << (2 * gpio_sub_n(portno));
74
75         gpio_array[gpio_bank(portno)]->port_mux = pmux;
76 }
77
78 inline u16 get_portmux(unsigned short portno)
79 {
80         u32 pmux;
81
82         pmux = gpio_array[gpio_bank(portno)]->port_mux;
83
84         return (pmux >> (2 * gpio_sub_n(portno)) & 0x3);
85 }
86
87 static void port_setup(unsigned short gpio, unsigned short usage)
88 {
89         if (usage == GPIO_USAGE) {
90                 gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio);
91         } else
92                 gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio);
93         SSYNC();
94 }
95
96 static int __init bfin_gpio_init(void)
97 {
98
99         str_ident = kzalloc(RESOURCE_LABEL_SIZE * 256, GFP_KERNEL);
100         if (!str_ident)
101                 return -ENOMEM;
102
103         printk(KERN_INFO "Blackfin GPIO Controller\n");
104
105         return 0;
106 }
107
108 arch_initcall(bfin_gpio_init);
109
110 static void set_label(unsigned short ident, const char *label)
111 {
112
113         if (label && str_ident) {
114                 strncpy(str_ident + ident * RESOURCE_LABEL_SIZE, label,
115                          RESOURCE_LABEL_SIZE);
116                 str_ident[ident * RESOURCE_LABEL_SIZE +
117                          RESOURCE_LABEL_SIZE - 1] = 0;
118         }
119 }
120
121 static char *get_label(unsigned short ident)
122 {
123         if (!str_ident)
124                 return "UNKNOWN";
125
126         return (str_ident[ident * RESOURCE_LABEL_SIZE] ?
127                 (str_ident + ident * RESOURCE_LABEL_SIZE) : "UNKNOWN");
128 }
129
130 static int cmp_label(unsigned short ident, const char *label)
131 {
132         if (label && str_ident)
133                 return strncmp(str_ident + ident * RESOURCE_LABEL_SIZE,
134                                  label, strlen(label));
135         else
136                 return -EINVAL;
137 }
138
139 int peripheral_request(unsigned short per, const char *label)
140 {
141         unsigned long flags;
142         unsigned short ident = P_IDENT(per);
143
144         /*
145          * Don't cares are pins with only one dedicated function
146          */
147
148         if (per & P_DONTCARE)
149                 return 0;
150
151         if (!(per & P_DEFINED))
152                 return -ENODEV;
153
154         if (check_gpio(ident) < 0)
155                 return -EINVAL;
156
157         local_irq_save(flags);
158
159         if (unlikely(reserved_gpio_map[gpio_bank(ident)] & gpio_bit(ident))) {
160                 printk(KERN_ERR
161                        "%s: Peripheral %d is already reserved as GPIO by %s !\n",
162                        __FUNCTION__, ident, get_label(ident));
163                 dump_stack();
164                 local_irq_restore(flags);
165                 return -EBUSY;
166         }
167
168         if (unlikely(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident))) {
169
170                 u16 funct = get_portmux(ident);
171
172         /*
173          * Pin functions like AMC address strobes my
174          * be requested and used by several drivers
175          */
176
177                 if (!((per & P_MAYSHARE) && (funct == P_FUNCT2MUX(per)))) {
178
179                 /*
180                  * Allow that the identical pin function can
181                  * be requested from the same driver twice
182                  */
183
184                 if (cmp_label(ident, label) == 0)
185                         goto anyway;
186
187                         printk(KERN_ERR
188                                "%s: Peripheral %d function %d is already reserved by %s !\n",
189                                __FUNCTION__, ident, P_FUNCT2MUX(per), get_label(ident));
190                         dump_stack();
191                         local_irq_restore(flags);
192                         return -EBUSY;
193                 }
194         }
195
196 anyway:
197         reserved_peri_map[gpio_bank(ident)] |= gpio_bit(ident);
198
199         portmux_setup(ident, P_FUNCT2MUX(per));
200         port_setup(ident, PERIPHERAL_USAGE);
201
202         local_irq_restore(flags);
203         set_label(ident, label);
204
205         return 0;
206 }
207 EXPORT_SYMBOL(peripheral_request);
208
209 int peripheral_request_list(unsigned short per[], const char *label)
210 {
211         u16 cnt;
212         int ret;
213
214         for (cnt = 0; per[cnt] != 0; cnt++) {
215                 ret = peripheral_request(per[cnt], label);
216                 if (ret < 0)
217                         return ret;
218         }
219
220         return 0;
221 }
222 EXPORT_SYMBOL(peripheral_request_list);
223
224 void peripheral_free(unsigned short per)
225 {
226         unsigned long flags;
227         unsigned short ident = P_IDENT(per);
228
229         if (per & P_DONTCARE)
230                 return;
231
232         if (!(per & P_DEFINED))
233                 return;
234
235         if (check_gpio(ident) < 0)
236                 return;
237
238         local_irq_save(flags);
239
240         if (unlikely(!(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident)))) {
241                 local_irq_restore(flags);
242                 return;
243         }
244
245         if (!(per & P_MAYSHARE)) {
246                 port_setup(ident, GPIO_USAGE);
247         }
248
249         reserved_peri_map[gpio_bank(ident)] &= ~gpio_bit(ident);
250
251         local_irq_restore(flags);
252 }
253 EXPORT_SYMBOL(peripheral_free);
254
255 void peripheral_free_list(unsigned short per[])
256 {
257         u16 cnt;
258
259         for (cnt = 0; per[cnt] != 0; cnt++) {
260                 peripheral_free(per[cnt]);
261         }
262
263 }
264 EXPORT_SYMBOL(peripheral_free_list);
265
266 /***********************************************************
267 *
268 * FUNCTIONS: Blackfin GPIO Driver
269 *
270 * INPUTS/OUTPUTS:
271 * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS
272 *
273 *
274 * DESCRIPTION: Blackfin GPIO Driver API
275 *
276 * CAUTION:
277 *************************************************************
278 * MODIFICATION HISTORY :
279 **************************************************************/
280
281 int gpio_request(unsigned short gpio, const char *label)
282 {
283         unsigned long flags;
284
285         if (check_gpio(gpio) < 0)
286                 return -EINVAL;
287
288         local_irq_save(flags);
289
290         if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
291                 printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n",
292                          gpio, get_label(gpio));
293                 dump_stack();
294                 local_irq_restore(flags);
295                 return -EBUSY;
296         }
297
298         if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
299                 printk(KERN_ERR
300                        "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n",
301                        gpio, get_label(gpio));
302                 dump_stack();
303                 local_irq_restore(flags);
304                 return -EBUSY;
305         }
306
307         reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio);
308
309         local_irq_restore(flags);
310
311         port_setup(gpio, GPIO_USAGE);
312         set_label(gpio, label);
313
314         return 0;
315 }
316 EXPORT_SYMBOL(gpio_request);
317
318 void gpio_free(unsigned short gpio)
319 {
320         unsigned long flags;
321
322         if (check_gpio(gpio) < 0)
323                 return;
324
325         local_irq_save(flags);
326
327         if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) {
328                 printk(KERN_ERR "bfin-gpio: GPIO %d wasn't reserved!\n", gpio);
329                 dump_stack();
330                 local_irq_restore(flags);
331                 return;
332         }
333
334         reserved_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
335
336         local_irq_restore(flags);
337 }
338 EXPORT_SYMBOL(gpio_free);
339
340 void gpio_direction_input(unsigned short gpio)
341 {
342         unsigned long flags;
343
344         BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)));
345
346         local_irq_save(flags);
347         gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio);
348         gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio);
349         local_irq_restore(flags);
350 }
351 EXPORT_SYMBOL(gpio_direction_input);
352
353 void gpio_direction_output(unsigned short gpio)
354 {
355         unsigned long flags;
356
357         BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)));
358
359         local_irq_save(flags);
360         gpio_array[gpio_bank(gpio)]->port_inen &= ~gpio_bit(gpio);
361         gpio_array[gpio_bank(gpio)]->port_dir_set = gpio_bit(gpio);
362         local_irq_restore(flags);
363 }
364 EXPORT_SYMBOL(gpio_direction_output);
365
366 void gpio_set_value(unsigned short gpio, unsigned short arg)
367 {
368         if (arg)
369                 gpio_array[gpio_bank(gpio)]->port_set = gpio_bit(gpio);
370         else
371                 gpio_array[gpio_bank(gpio)]->port_clear = gpio_bit(gpio);
372
373 }
374 EXPORT_SYMBOL(gpio_set_value);
375
376 unsigned short gpio_get_value(unsigned short gpio)
377 {
378         return (1 & (gpio_array[gpio_bank(gpio)]->port_data >> gpio_sub_n(gpio)));
379 }
380 EXPORT_SYMBOL(gpio_get_value);