diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2007-08-28 04:47:46 -0400 |
---|---|---|
committer | Bryan Wu <bryan.wu@analog.com> | 2007-08-28 04:47:46 -0400 |
commit | d2b11a468a49716debd96532552a72b6078f1cf5 (patch) | |
tree | b30d94732f9d01b823e60975147b284aead495d9 /arch/blackfin | |
parent | 2296fb7ff04531dd8d50394da24f49302ecf103b (diff) |
Blackfin arch: Merge GPIO/Peripheral Resource Allocation back into a single file
Signed-off-by: Michael Hennerich <michale.hennerich@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Diffstat (limited to 'arch/blackfin')
-rw-r--r-- | arch/blackfin/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 225 | ||||
-rw-r--r-- | arch/blackfin/mach-bf548/Makefile | 2 | ||||
-rw-r--r-- | arch/blackfin/mach-bf548/gpio.c | 392 |
4 files changed, 216 insertions, 407 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index ae0a2203c3da..243883ec6de1 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile | |||
@@ -7,10 +7,8 @@ extra-y := init_task.o vmlinux.lds | |||
7 | obj-y := \ | 7 | obj-y := \ |
8 | entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ | 8 | entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ |
9 | sys_bfin.o time.o traps.o irqchip.o dma-mapping.o flat.o \ | 9 | sys_bfin.o time.o traps.o irqchip.o dma-mapping.o flat.o \ |
10 | fixed_code.o cplbinit.o cacheinit.o reboot.o | 10 | fixed_code.o cplbinit.o cacheinit.o reboot.o bfin_gpio.o |
11 | 11 | ||
12 | obj-$(CONFIG_BF53x) += bfin_gpio.o | ||
13 | obj-$(CONFIG_BF561) += bfin_gpio.o | ||
14 | obj-$(CONFIG_MODULES) += module.o | 12 | obj-$(CONFIG_MODULES) += module.o |
15 | obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o | 13 | obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o |
16 | obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o | 14 | obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o |
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index 0d1e87b9c93d..78438d88d22a 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Description: GPIO Abstraction Layer | 7 | * Description: GPIO Abstraction Layer |
8 | * | 8 | * |
9 | * Modified: | 9 | * Modified: |
10 | * Copyright 2006 Analog Devices Inc. | 10 | * Copyright 2007 Analog Devices Inc. |
11 | * | 11 | * |
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | 12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ |
13 | * | 13 | * |
@@ -28,9 +28,9 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | /* | 30 | /* |
31 | * Number BF537/6/4 BF561 BF533/2/1 | 31 | * Number BF537/6/4 BF561 BF533/2/1 BF549/8/4/2 |
32 | * | 32 | * |
33 | * GPIO_0 PF0 PF0 PF0 | 33 | * GPIO_0 PF0 PF0 PF0 PA0...PJ13 |
34 | * GPIO_1 PF1 PF1 PF1 | 34 | * GPIO_1 PF1 PF1 PF1 |
35 | * GPIO_2 PF2 PF2 PF2 | 35 | * GPIO_2 PF2 PF2 PF2 |
36 | * GPIO_3 PF3 PF3 PF3 | 36 | * GPIO_3 PF3 PF3 PF3 |
@@ -117,6 +117,21 @@ static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | |||
117 | }; | 117 | }; |
118 | #endif | 118 | #endif |
119 | 119 | ||
120 | #ifdef BF548_FAMILY | ||
121 | static struct gpio_port_t *gpio_array[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | ||
122 | (struct gpio_port_t *)PORTA_FER, | ||
123 | (struct gpio_port_t *)PORTB_FER, | ||
124 | (struct gpio_port_t *)PORTC_FER, | ||
125 | (struct gpio_port_t *)PORTD_FER, | ||
126 | (struct gpio_port_t *)PORTE_FER, | ||
127 | (struct gpio_port_t *)PORTF_FER, | ||
128 | (struct gpio_port_t *)PORTG_FER, | ||
129 | (struct gpio_port_t *)PORTH_FER, | ||
130 | (struct gpio_port_t *)PORTI_FER, | ||
131 | (struct gpio_port_t *)PORTJ_FER, | ||
132 | }; | ||
133 | #endif | ||
134 | |||
120 | static unsigned short reserved_gpio_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; | 135 | static unsigned short reserved_gpio_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; |
121 | static unsigned short reserved_peri_map[gpio_bank(MAX_BLACKFIN_GPIOS + 16)]; | 136 | static unsigned short reserved_peri_map[gpio_bank(MAX_BLACKFIN_GPIOS + 16)]; |
122 | 137 | ||
@@ -147,12 +162,24 @@ static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG0_INT | |||
147 | 162 | ||
148 | #endif /* CONFIG_PM */ | 163 | #endif /* CONFIG_PM */ |
149 | 164 | ||
165 | #if defined(BF548_FAMILY) | ||
166 | inline int check_gpio(unsigned short gpio) | ||
167 | { | ||
168 | if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 | ||
169 | || gpio == GPIO_PH14 || gpio == GPIO_PH15 | ||
170 | || gpio == GPIO_PJ14 || gpio == GPIO_PJ15 | ||
171 | || gpio > MAX_BLACKFIN_GPIOS) | ||
172 | return -EINVAL; | ||
173 | return 0; | ||
174 | } | ||
175 | #else | ||
150 | inline int check_gpio(unsigned short gpio) | 176 | inline int check_gpio(unsigned short gpio) |
151 | { | 177 | { |
152 | if (gpio >= MAX_BLACKFIN_GPIOS) | 178 | if (gpio >= MAX_BLACKFIN_GPIOS) |
153 | return -EINVAL; | 179 | return -EINVAL; |
154 | return 0; | 180 | return 0; |
155 | } | 181 | } |
182 | #endif | ||
156 | 183 | ||
157 | static void set_label(unsigned short ident, const char *label) | 184 | static void set_label(unsigned short ident, const char *label) |
158 | { | 185 | { |
@@ -185,19 +212,27 @@ static int cmp_label(unsigned short ident, const char *label) | |||
185 | static void port_setup(unsigned short gpio, unsigned short usage) | 212 | static void port_setup(unsigned short gpio, unsigned short usage) |
186 | { | 213 | { |
187 | if (!check_gpio(gpio)) { | 214 | if (!check_gpio(gpio)) { |
188 | if (usage == GPIO_USAGE) { | 215 | if (usage == GPIO_USAGE) |
189 | *port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); | 216 | *port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); |
190 | } else | 217 | else |
191 | *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); | 218 | *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); |
192 | SSYNC(); | 219 | SSYNC(); |
193 | } | 220 | } |
194 | } | 221 | } |
222 | #elif defined(BF548_FAMILY) | ||
223 | static void port_setup(unsigned short gpio, unsigned short usage) | ||
224 | { | ||
225 | if (usage == GPIO_USAGE) | ||
226 | gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); | ||
227 | else | ||
228 | gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); | ||
229 | SSYNC(); | ||
230 | } | ||
195 | #else | 231 | #else |
196 | # define port_setup(...) do { } while (0) | 232 | # define port_setup(...) do { } while (0) |
197 | #endif | 233 | #endif |
198 | 234 | ||
199 | #ifdef BF537_FAMILY | 235 | #ifdef BF537_FAMILY |
200 | |||
201 | static struct { | 236 | static struct { |
202 | unsigned short res; | 237 | unsigned short res; |
203 | unsigned short offset; | 238 | unsigned short offset; |
@@ -268,11 +303,32 @@ static void portmux_setup(unsigned short per, unsigned short function) | |||
268 | } | 303 | } |
269 | } | 304 | } |
270 | } | 305 | } |
306 | #elif defined(BF548_FAMILY) | ||
307 | inline void portmux_setup(unsigned short portno, unsigned short function) | ||
308 | { | ||
309 | u32 pmux; | ||
310 | |||
311 | pmux = gpio_array[gpio_bank(portno)]->port_mux; | ||
312 | |||
313 | pmux &= ~(0x3 << (2 * gpio_sub_n(portno))); | ||
314 | pmux |= (function & 0x3) << (2 * gpio_sub_n(portno)); | ||
315 | |||
316 | gpio_array[gpio_bank(portno)]->port_mux = pmux; | ||
317 | } | ||
318 | |||
319 | inline u16 get_portmux(unsigned short portno) | ||
320 | { | ||
321 | u32 pmux; | ||
271 | 322 | ||
323 | pmux = gpio_array[gpio_bank(portno)]->port_mux; | ||
324 | |||
325 | return (pmux >> (2 * gpio_sub_n(portno)) & 0x3); | ||
326 | } | ||
272 | #else | 327 | #else |
273 | # define portmux_setup(...) do { } while (0) | 328 | # define portmux_setup(...) do { } while (0) |
274 | #endif | 329 | #endif |
275 | 330 | ||
331 | #ifndef BF548_FAMILY | ||
276 | static void default_gpio(unsigned short gpio) | 332 | static void default_gpio(unsigned short gpio) |
277 | { | 333 | { |
278 | unsigned short bank, bitmask; | 334 | unsigned short bank, bitmask; |
@@ -289,6 +345,9 @@ static void default_gpio(unsigned short gpio) | |||
289 | gpio_bankb[bank]->both &= ~bitmask; | 345 | gpio_bankb[bank]->both &= ~bitmask; |
290 | gpio_bankb[bank]->edge &= ~bitmask; | 346 | gpio_bankb[bank]->edge &= ~bitmask; |
291 | } | 347 | } |
348 | #else | ||
349 | # define default_gpio(...) do { } while (0) | ||
350 | #endif | ||
292 | 351 | ||
293 | static int __init bfin_gpio_init(void) | 352 | static int __init bfin_gpio_init(void) |
294 | { | 353 | { |
@@ -307,6 +366,7 @@ static int __init bfin_gpio_init(void) | |||
307 | arch_initcall(bfin_gpio_init); | 366 | arch_initcall(bfin_gpio_init); |
308 | 367 | ||
309 | 368 | ||
369 | #ifndef BF548_FAMILY | ||
310 | /*********************************************************** | 370 | /*********************************************************** |
311 | * | 371 | * |
312 | * FUNCTIONS: Blackfin General Purpose Ports Access Functions | 372 | * FUNCTIONS: Blackfin General Purpose Ports Access Functions |
@@ -658,9 +718,95 @@ void gpio_pm_restore(void) | |||
658 | } | 718 | } |
659 | 719 | ||
660 | #endif | 720 | #endif |
721 | #endif /* BF548_FAMILY */ | ||
661 | 722 | ||
723 | /*********************************************************** | ||
724 | * | ||
725 | * FUNCTIONS: Blackfin Peripheral Resource Allocation | ||
726 | * and PortMux Setup | ||
727 | * | ||
728 | * INPUTS/OUTPUTS: | ||
729 | * per Peripheral Identifier | ||
730 | * label String | ||
731 | * | ||
732 | * DESCRIPTION: Blackfin Peripheral Resource Allocation and Setup API | ||
733 | * | ||
734 | * CAUTION: | ||
735 | ************************************************************* | ||
736 | * MODIFICATION HISTORY : | ||
737 | **************************************************************/ | ||
738 | |||
739 | #ifdef BF548_FAMILY | ||
740 | int peripheral_request(unsigned short per, const char *label) | ||
741 | { | ||
742 | unsigned long flags; | ||
743 | unsigned short ident = P_IDENT(per); | ||
744 | |||
745 | /* | ||
746 | * Don't cares are pins with only one dedicated function | ||
747 | */ | ||
662 | 748 | ||
749 | if (per & P_DONTCARE) | ||
750 | return 0; | ||
751 | |||
752 | if (!(per & P_DEFINED)) | ||
753 | return -ENODEV; | ||
754 | |||
755 | if (check_gpio(ident) < 0) | ||
756 | return -EINVAL; | ||
663 | 757 | ||
758 | local_irq_save(flags); | ||
759 | |||
760 | if (unlikely(reserved_gpio_map[gpio_bank(ident)] & gpio_bit(ident))) { | ||
761 | printk(KERN_ERR | ||
762 | "%s: Peripheral %d is already reserved as GPIO by %s !\n", | ||
763 | __FUNCTION__, ident, get_label(ident)); | ||
764 | dump_stack(); | ||
765 | local_irq_restore(flags); | ||
766 | return -EBUSY; | ||
767 | } | ||
768 | |||
769 | if (unlikely(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident))) { | ||
770 | |||
771 | u16 funct = get_portmux(ident); | ||
772 | |||
773 | /* | ||
774 | * Pin functions like AMC address strobes my | ||
775 | * be requested and used by several drivers | ||
776 | */ | ||
777 | |||
778 | if (!((per & P_MAYSHARE) && (funct == P_FUNCT2MUX(per)))) { | ||
779 | |||
780 | /* | ||
781 | * Allow that the identical pin function can | ||
782 | * be requested from the same driver twice | ||
783 | */ | ||
784 | |||
785 | if (cmp_label(ident, label) == 0) | ||
786 | goto anyway; | ||
787 | |||
788 | printk(KERN_ERR | ||
789 | "%s: Peripheral %d function %d is already reserved by %s !\n", | ||
790 | __FUNCTION__, ident, P_FUNCT2MUX(per), get_label(ident)); | ||
791 | dump_stack(); | ||
792 | local_irq_restore(flags); | ||
793 | return -EBUSY; | ||
794 | } | ||
795 | } | ||
796 | |||
797 | anyway: | ||
798 | reserved_peri_map[gpio_bank(ident)] |= gpio_bit(ident); | ||
799 | |||
800 | portmux_setup(ident, P_FUNCT2MUX(per)); | ||
801 | port_setup(ident, PERIPHERAL_USAGE); | ||
802 | |||
803 | local_irq_restore(flags); | ||
804 | set_label(ident, label); | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | EXPORT_SYMBOL(peripheral_request); | ||
809 | #else | ||
664 | 810 | ||
665 | int peripheral_request(unsigned short per, const char *label) | 811 | int peripheral_request(unsigned short per, const char *label) |
666 | { | 812 | { |
@@ -722,8 +868,6 @@ int peripheral_request(unsigned short per, const char *label) | |||
722 | } | 868 | } |
723 | 869 | ||
724 | anyway: | 870 | anyway: |
725 | |||
726 | |||
727 | portmux_setup(per, P_FUNCT2MUX(per)); | 871 | portmux_setup(per, P_FUNCT2MUX(per)); |
728 | 872 | ||
729 | port_setup(ident, PERIPHERAL_USAGE); | 873 | port_setup(ident, PERIPHERAL_USAGE); |
@@ -735,6 +879,7 @@ anyway: | |||
735 | return 0; | 879 | return 0; |
736 | } | 880 | } |
737 | EXPORT_SYMBOL(peripheral_request); | 881 | EXPORT_SYMBOL(peripheral_request); |
882 | #endif | ||
738 | 883 | ||
739 | int peripheral_request_list(unsigned short per[], const char *label) | 884 | int peripheral_request_list(unsigned short per[], const char *label) |
740 | { | 885 | { |
@@ -805,8 +950,8 @@ EXPORT_SYMBOL(peripheral_free_list); | |||
805 | * FUNCTIONS: Blackfin GPIO Driver | 950 | * FUNCTIONS: Blackfin GPIO Driver |
806 | * | 951 | * |
807 | * INPUTS/OUTPUTS: | 952 | * INPUTS/OUTPUTS: |
808 | * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS | 953 | * gpio PIO Number between 0 and MAX_BLACKFIN_GPIOS |
809 | * | 954 | * label String |
810 | * | 955 | * |
811 | * DESCRIPTION: Blackfin GPIO Driver API | 956 | * DESCRIPTION: Blackfin GPIO Driver API |
812 | * | 957 | * |
@@ -825,16 +970,27 @@ int gpio_request(unsigned short gpio, const char *label) | |||
825 | local_irq_save(flags); | 970 | local_irq_save(flags); |
826 | 971 | ||
827 | if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { | 972 | if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { |
828 | printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved!\n", gpio); | 973 | printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n", |
974 | gpio, get_label(gpio)); | ||
975 | dump_stack(); | ||
976 | local_irq_restore(flags); | ||
977 | return -EBUSY; | ||
978 | } | ||
979 | if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) { | ||
980 | printk(KERN_ERR | ||
981 | "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", | ||
982 | gpio, get_label(gpio)); | ||
829 | dump_stack(); | 983 | dump_stack(); |
830 | local_irq_restore(flags); | 984 | local_irq_restore(flags); |
831 | return -EBUSY; | 985 | return -EBUSY; |
832 | } | 986 | } |
987 | |||
833 | reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); | 988 | reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); |
834 | 989 | ||
835 | local_irq_restore(flags); | 990 | local_irq_restore(flags); |
836 | 991 | ||
837 | port_setup(gpio, GPIO_USAGE); | 992 | port_setup(gpio, GPIO_USAGE); |
993 | set_label(gpio, label); | ||
838 | 994 | ||
839 | return 0; | 995 | return 0; |
840 | } | 996 | } |
@@ -864,6 +1020,51 @@ void gpio_free(unsigned short gpio) | |||
864 | } | 1020 | } |
865 | EXPORT_SYMBOL(gpio_free); | 1021 | EXPORT_SYMBOL(gpio_free); |
866 | 1022 | ||
1023 | #ifdef BF548_FAMILY | ||
1024 | void gpio_direction_input(unsigned short gpio) | ||
1025 | { | ||
1026 | unsigned long flags; | ||
1027 | |||
1028 | BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
1029 | |||
1030 | local_irq_save(flags); | ||
1031 | gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); | ||
1032 | gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); | ||
1033 | local_irq_restore(flags); | ||
1034 | } | ||
1035 | EXPORT_SYMBOL(gpio_direction_input); | ||
1036 | |||
1037 | void gpio_direction_output(unsigned short gpio) | ||
1038 | { | ||
1039 | unsigned long flags; | ||
1040 | |||
1041 | BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
1042 | |||
1043 | local_irq_save(flags); | ||
1044 | gpio_array[gpio_bank(gpio)]->port_inen &= ~gpio_bit(gpio); | ||
1045 | gpio_array[gpio_bank(gpio)]->port_dir_set = gpio_bit(gpio); | ||
1046 | local_irq_restore(flags); | ||
1047 | } | ||
1048 | EXPORT_SYMBOL(gpio_direction_output); | ||
1049 | |||
1050 | void gpio_set_value(unsigned short gpio, unsigned short arg) | ||
1051 | { | ||
1052 | if (arg) | ||
1053 | gpio_array[gpio_bank(gpio)]->port_set = gpio_bit(gpio); | ||
1054 | else | ||
1055 | gpio_array[gpio_bank(gpio)]->port_clear = gpio_bit(gpio); | ||
1056 | |||
1057 | } | ||
1058 | EXPORT_SYMBOL(gpio_set_value); | ||
1059 | |||
1060 | unsigned short gpio_get_value(unsigned short gpio) | ||
1061 | { | ||
1062 | return (1 & (gpio_array[gpio_bank(gpio)]->port_data >> gpio_sub_n(gpio))); | ||
1063 | } | ||
1064 | EXPORT_SYMBOL(gpio_get_value); | ||
1065 | |||
1066 | #else | ||
1067 | |||
867 | void gpio_direction_input(unsigned short gpio) | 1068 | void gpio_direction_input(unsigned short gpio) |
868 | { | 1069 | { |
869 | unsigned long flags; | 1070 | unsigned long flags; |
@@ -908,3 +1109,5 @@ void bfin_gpio_reset_spi0_ssel1(void) | |||
908 | gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio); | 1109 | gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio); |
909 | udelay(1); | 1110 | udelay(1); |
910 | } | 1111 | } |
1112 | |||
1113 | #endif /*BF548_FAMILY */ | ||
diff --git a/arch/blackfin/mach-bf548/Makefile b/arch/blackfin/mach-bf548/Makefile index 060ad78ebf1d..7e7c9c8ac5b2 100644 --- a/arch/blackfin/mach-bf548/Makefile +++ b/arch/blackfin/mach-bf548/Makefile | |||
@@ -4,6 +4,6 @@ | |||
4 | 4 | ||
5 | extra-y := head.o | 5 | extra-y := head.o |
6 | 6 | ||
7 | obj-y := ints-priority.o dma.o gpio.o | 7 | obj-y := ints-priority.o dma.o |
8 | 8 | ||
9 | obj-$(CONFIG_CPU_FREQ) += cpu.o | 9 | obj-$(CONFIG_CPU_FREQ) += cpu.o |
diff --git a/arch/blackfin/mach-bf548/gpio.c b/arch/blackfin/mach-bf548/gpio.c deleted file mode 100644 index 390dd8c12430..000000000000 --- a/arch/blackfin/mach-bf548/gpio.c +++ /dev/null | |||
@@ -1,392 +0,0 @@ | |||
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 | |||
53 | #define MAX_RESOURCES 256 | ||
54 | #define RESOURCE_LABEL_SIZE 16 | ||
55 | |||
56 | struct str_ident { | ||
57 | char name[RESOURCE_LABEL_SIZE]; | ||
58 | } *str_ident; | ||
59 | |||
60 | inline int check_gpio(unsigned short gpio) | ||
61 | { | ||
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 | || gpio > MAX_BLACKFIN_GPIOS) | ||
66 | return -EINVAL; | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | inline void portmux_setup(unsigned short portno, unsigned short function) | ||
71 | { | ||
72 | u32 pmux; | ||
73 | |||
74 | pmux = gpio_array[gpio_bank(portno)]->port_mux; | ||
75 | |||
76 | pmux &= ~(0x3 << (2 * gpio_sub_n(portno))); | ||
77 | pmux |= (function & 0x3) << (2 * gpio_sub_n(portno)); | ||
78 | |||
79 | gpio_array[gpio_bank(portno)]->port_mux = pmux; | ||
80 | } | ||
81 | |||
82 | inline u16 get_portmux(unsigned short portno) | ||
83 | { | ||
84 | u32 pmux; | ||
85 | |||
86 | pmux = gpio_array[gpio_bank(portno)]->port_mux; | ||
87 | |||
88 | return (pmux >> (2 * gpio_sub_n(portno)) & 0x3); | ||
89 | } | ||
90 | |||
91 | static void port_setup(unsigned short gpio, unsigned short usage) | ||
92 | { | ||
93 | if (usage == GPIO_USAGE) { | ||
94 | gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); | ||
95 | } else | ||
96 | gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); | ||
97 | SSYNC(); | ||
98 | } | ||
99 | |||
100 | static int __init bfin_gpio_init(void) | ||
101 | { | ||
102 | |||
103 | str_ident = kcalloc(MAX_RESOURCES, | ||
104 | sizeof(struct str_ident), GFP_KERNEL); | ||
105 | if (str_ident == NULL) | ||
106 | return -ENOMEM; | ||
107 | |||
108 | memset(str_ident, 0, MAX_RESOURCES * sizeof(struct str_ident)); | ||
109 | |||
110 | printk(KERN_INFO "Blackfin GPIO Controller\n"); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | arch_initcall(bfin_gpio_init); | ||
116 | |||
117 | static void set_label(unsigned short ident, const char *label) | ||
118 | { | ||
119 | |||
120 | if (label && str_ident) { | ||
121 | strncpy(str_ident[ident].name, label, | ||
122 | RESOURCE_LABEL_SIZE); | ||
123 | str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | static char *get_label(unsigned short ident) | ||
128 | { | ||
129 | if (!str_ident) | ||
130 | return "UNKNOWN"; | ||
131 | |||
132 | return (*str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"); | ||
133 | } | ||
134 | |||
135 | static int cmp_label(unsigned short ident, const char *label) | ||
136 | { | ||
137 | if (label && str_ident) | ||
138 | return strncmp(str_ident[ident].name, | ||
139 | label, strlen(label)); | ||
140 | else | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | int peripheral_request(unsigned short per, const char *label) | ||
145 | { | ||
146 | unsigned long flags; | ||
147 | unsigned short ident = P_IDENT(per); | ||
148 | |||
149 | /* | ||
150 | * Don't cares are pins with only one dedicated function | ||
151 | */ | ||
152 | |||
153 | if (per & P_DONTCARE) | ||
154 | return 0; | ||
155 | |||
156 | if (!(per & P_DEFINED)) | ||
157 | return -ENODEV; | ||
158 | |||
159 | if (check_gpio(ident) < 0) | ||
160 | return -EINVAL; | ||
161 | |||
162 | local_irq_save(flags); | ||
163 | |||
164 | if (unlikely(reserved_gpio_map[gpio_bank(ident)] & gpio_bit(ident))) { | ||
165 | printk(KERN_ERR | ||
166 | "%s: Peripheral %d is already reserved as GPIO by %s !\n", | ||
167 | __FUNCTION__, ident, get_label(ident)); | ||
168 | dump_stack(); | ||
169 | local_irq_restore(flags); | ||
170 | return -EBUSY; | ||
171 | } | ||
172 | |||
173 | if (unlikely(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident))) { | ||
174 | |||
175 | u16 funct = get_portmux(ident); | ||
176 | |||
177 | /* | ||
178 | * Pin functions like AMC address strobes my | ||
179 | * be requested and used by several drivers | ||
180 | */ | ||
181 | |||
182 | if (!((per & P_MAYSHARE) && (funct == P_FUNCT2MUX(per)))) { | ||
183 | |||
184 | /* | ||
185 | * Allow that the identical pin function can | ||
186 | * be requested from the same driver twice | ||
187 | */ | ||
188 | |||
189 | if (cmp_label(ident, label) == 0) | ||
190 | goto anyway; | ||
191 | |||
192 | printk(KERN_ERR | ||
193 | "%s: Peripheral %d function %d is already reserved by %s !\n", | ||
194 | __FUNCTION__, ident, P_FUNCT2MUX(per), get_label(ident)); | ||
195 | dump_stack(); | ||
196 | local_irq_restore(flags); | ||
197 | return -EBUSY; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | anyway: | ||
202 | reserved_peri_map[gpio_bank(ident)] |= gpio_bit(ident); | ||
203 | |||
204 | portmux_setup(ident, P_FUNCT2MUX(per)); | ||
205 | port_setup(ident, PERIPHERAL_USAGE); | ||
206 | |||
207 | local_irq_restore(flags); | ||
208 | set_label(ident, label); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | EXPORT_SYMBOL(peripheral_request); | ||
213 | |||
214 | int peripheral_request_list(unsigned short per[], const char *label) | ||
215 | { | ||
216 | u16 cnt; | ||
217 | int ret; | ||
218 | |||
219 | for (cnt = 0; per[cnt] != 0; cnt++) { | ||
220 | |||
221 | ret = peripheral_request(per[cnt], label); | ||
222 | |||
223 | if (ret < 0) { | ||
224 | for ( ; cnt > 0; cnt--) { | ||
225 | peripheral_free(per[cnt - 1]); | ||
226 | } | ||
227 | return ret; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | EXPORT_SYMBOL(peripheral_request_list); | ||
235 | |||
236 | void peripheral_free(unsigned short per) | ||
237 | { | ||
238 | unsigned long flags; | ||
239 | unsigned short ident = P_IDENT(per); | ||
240 | |||
241 | if (per & P_DONTCARE) | ||
242 | return; | ||
243 | |||
244 | if (!(per & P_DEFINED)) | ||
245 | return; | ||
246 | |||
247 | if (check_gpio(ident) < 0) | ||
248 | return; | ||
249 | |||
250 | local_irq_save(flags); | ||
251 | |||
252 | if (unlikely(!(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident)))) { | ||
253 | local_irq_restore(flags); | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | if (!(per & P_MAYSHARE)) { | ||
258 | port_setup(ident, GPIO_USAGE); | ||
259 | } | ||
260 | |||
261 | reserved_peri_map[gpio_bank(ident)] &= ~gpio_bit(ident); | ||
262 | |||
263 | local_irq_restore(flags); | ||
264 | } | ||
265 | EXPORT_SYMBOL(peripheral_free); | ||
266 | |||
267 | void peripheral_free_list(unsigned short per[]) | ||
268 | { | ||
269 | u16 cnt; | ||
270 | |||
271 | for (cnt = 0; per[cnt] != 0; cnt++) { | ||
272 | peripheral_free(per[cnt]); | ||
273 | } | ||
274 | |||
275 | } | ||
276 | EXPORT_SYMBOL(peripheral_free_list); | ||
277 | |||
278 | /*********************************************************** | ||
279 | * | ||
280 | * FUNCTIONS: Blackfin GPIO Driver | ||
281 | * | ||
282 | * INPUTS/OUTPUTS: | ||
283 | * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS | ||
284 | * | ||
285 | * | ||
286 | * DESCRIPTION: Blackfin GPIO Driver API | ||
287 | * | ||
288 | * CAUTION: | ||
289 | ************************************************************* | ||
290 | * MODIFICATION HISTORY : | ||
291 | **************************************************************/ | ||
292 | |||
293 | int gpio_request(unsigned short gpio, const char *label) | ||
294 | { | ||
295 | unsigned long flags; | ||
296 | |||
297 | if (check_gpio(gpio) < 0) | ||
298 | return -EINVAL; | ||
299 | |||
300 | local_irq_save(flags); | ||
301 | |||
302 | if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { | ||
303 | printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n", | ||
304 | gpio, get_label(gpio)); | ||
305 | dump_stack(); | ||
306 | local_irq_restore(flags); | ||
307 | return -EBUSY; | ||
308 | } | ||
309 | |||
310 | if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) { | ||
311 | printk(KERN_ERR | ||
312 | "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", | ||
313 | gpio, get_label(gpio)); | ||
314 | dump_stack(); | ||
315 | local_irq_restore(flags); | ||
316 | return -EBUSY; | ||
317 | } | ||
318 | |||
319 | reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); | ||
320 | |||
321 | local_irq_restore(flags); | ||
322 | |||
323 | port_setup(gpio, GPIO_USAGE); | ||
324 | set_label(gpio, label); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | EXPORT_SYMBOL(gpio_request); | ||
329 | |||
330 | void gpio_free(unsigned short gpio) | ||
331 | { | ||
332 | unsigned long flags; | ||
333 | |||
334 | if (check_gpio(gpio) < 0) | ||
335 | return; | ||
336 | |||
337 | local_irq_save(flags); | ||
338 | |||
339 | if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { | ||
340 | printk(KERN_ERR "bfin-gpio: GPIO %d wasn't reserved!\n", gpio); | ||
341 | dump_stack(); | ||
342 | local_irq_restore(flags); | ||
343 | return; | ||
344 | } | ||
345 | |||
346 | reserved_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio); | ||
347 | |||
348 | local_irq_restore(flags); | ||
349 | } | ||
350 | EXPORT_SYMBOL(gpio_free); | ||
351 | |||
352 | void gpio_direction_input(unsigned short gpio) | ||
353 | { | ||
354 | unsigned long flags; | ||
355 | |||
356 | BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
357 | |||
358 | local_irq_save(flags); | ||
359 | gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); | ||
360 | gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); | ||
361 | local_irq_restore(flags); | ||
362 | } | ||
363 | EXPORT_SYMBOL(gpio_direction_input); | ||
364 | |||
365 | void gpio_direction_output(unsigned short gpio) | ||
366 | { | ||
367 | unsigned long flags; | ||
368 | |||
369 | BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
370 | |||
371 | local_irq_save(flags); | ||
372 | gpio_array[gpio_bank(gpio)]->port_inen &= ~gpio_bit(gpio); | ||
373 | gpio_array[gpio_bank(gpio)]->port_dir_set = gpio_bit(gpio); | ||
374 | local_irq_restore(flags); | ||
375 | } | ||
376 | EXPORT_SYMBOL(gpio_direction_output); | ||
377 | |||
378 | void gpio_set_value(unsigned short gpio, unsigned short arg) | ||
379 | { | ||
380 | if (arg) | ||
381 | gpio_array[gpio_bank(gpio)]->port_set = gpio_bit(gpio); | ||
382 | else | ||
383 | gpio_array[gpio_bank(gpio)]->port_clear = gpio_bit(gpio); | ||
384 | |||
385 | } | ||
386 | EXPORT_SYMBOL(gpio_set_value); | ||
387 | |||
388 | unsigned short gpio_get_value(unsigned short gpio) | ||
389 | { | ||
390 | return (1 & (gpio_array[gpio_bank(gpio)]->port_data >> gpio_sub_n(gpio))); | ||
391 | } | ||
392 | EXPORT_SYMBOL(gpio_get_value); | ||