diff options
Diffstat (limited to 'arch/arm/mach-omap')
31 files changed, 9219 insertions, 0 deletions
diff --git a/arch/arm/mach-omap/Kconfig b/arch/arm/mach-omap/Kconfig new file mode 100644 index 000000000000..9e42efa66b2b --- /dev/null +++ b/arch/arm/mach-omap/Kconfig | |||
@@ -0,0 +1,221 @@ | |||
1 | if ARCH_OMAP | ||
2 | |||
3 | menu "TI OMAP Implementations" | ||
4 | |||
5 | comment "OMAP Core Type" | ||
6 | |||
7 | config ARCH_OMAP730 | ||
8 | depends on ARCH_OMAP | ||
9 | bool "OMAP730 Based System" | ||
10 | select ARCH_OMAP_OTG | ||
11 | |||
12 | config ARCH_OMAP1510 | ||
13 | depends on ARCH_OMAP | ||
14 | default y | ||
15 | bool "OMAP1510 Based System" | ||
16 | |||
17 | config ARCH_OMAP16XX | ||
18 | depends on ARCH_OMAP | ||
19 | bool "OMAP16XX Based System" | ||
20 | select ARCH_OMAP_OTG | ||
21 | |||
22 | config ARCH_OMAP_OTG | ||
23 | bool | ||
24 | |||
25 | comment "OMAP Board Type" | ||
26 | |||
27 | config MACH_OMAP_INNOVATOR | ||
28 | bool "TI Innovator" | ||
29 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX | ||
30 | help | ||
31 | TI OMAP 1510 or 1610 Innovator board support. Say Y here if you | ||
32 | have such a board. | ||
33 | |||
34 | config MACH_OMAP_H2 | ||
35 | bool "TI H2 Support" | ||
36 | depends on ARCH_OMAP16XX | ||
37 | help | ||
38 | TI OMAP 1610/1611B H2 board support. Say Y here if you have such | ||
39 | a board. | ||
40 | |||
41 | config MACH_OMAP_H3 | ||
42 | bool "TI H3 Support" | ||
43 | depends on ARCH_OMAP16XX | ||
44 | help | ||
45 | TI OMAP 1710 H3 board support. Say Y here if you have such | ||
46 | a board. | ||
47 | |||
48 | config MACH_OMAP_H4 | ||
49 | bool "TI H4 Support" | ||
50 | depends on ARCH_OMAP16XX | ||
51 | help | ||
52 | TI OMAP 1610 H4 board support. Say Y here if you have such | ||
53 | a board. | ||
54 | |||
55 | config MACH_OMAP_OSK | ||
56 | bool "TI OSK Support" | ||
57 | depends on ARCH_OMAP16XX | ||
58 | help | ||
59 | TI OMAP 5912 OSK (OMAP Starter Kit) board support. Say Y here | ||
60 | if you have such a board. | ||
61 | |||
62 | config MACH_OMAP_PERSEUS2 | ||
63 | bool "TI Perseus2" | ||
64 | depends on ARCH_OMAP730 | ||
65 | help | ||
66 | Support for TI OMAP 730 Perseus2 board. Say Y here if you have such | ||
67 | a board. | ||
68 | |||
69 | config MACH_VOICEBLUE | ||
70 | bool "Voiceblue" | ||
71 | depends on ARCH_OMAP1510 | ||
72 | help | ||
73 | Support for Voiceblue GSM/VoIP gateway. Say Y here if you have such | ||
74 | board. | ||
75 | |||
76 | config MACH_NETSTAR | ||
77 | bool "NetStar" | ||
78 | depends on ARCH_OMAP1510 | ||
79 | help | ||
80 | Support for NetStar PBX. Say Y here if you have such a board. | ||
81 | |||
82 | config MACH_OMAP_GENERIC | ||
83 | bool "Generic OMAP board" | ||
84 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX | ||
85 | help | ||
86 | Support for generic OMAP-1510, 1610 or 1710 board with | ||
87 | no FPGA. Can be used as template for porting Linux to | ||
88 | custom OMAP boards. Say Y here if you have a custom | ||
89 | board. | ||
90 | |||
91 | comment "OMAP Feature Selections" | ||
92 | |||
93 | #config OMAP_BOOT_TAG | ||
94 | # bool "OMAP bootloader information passing" | ||
95 | # depends on ARCH_OMAP | ||
96 | # default n | ||
97 | # help | ||
98 | # Say Y, if you have a bootloader which passes information | ||
99 | # about your board and its peripheral configuration. | ||
100 | |||
101 | config OMAP_MUX | ||
102 | bool "OMAP multiplexing support" | ||
103 | depends on ARCH_OMAP | ||
104 | default y | ||
105 | help | ||
106 | Pin multiplexing support for OMAP boards. If your bootloader | ||
107 | sets the multiplexing correctly, say N. Otherwise, or if unsure, | ||
108 | say Y. | ||
109 | |||
110 | config OMAP_MUX_DEBUG | ||
111 | bool "Multiplexing debug output" | ||
112 | depends on OMAP_MUX | ||
113 | default n | ||
114 | help | ||
115 | Makes the multiplexing functions print out a lot of debug info. | ||
116 | This is useful if you want to find out the correct values of the | ||
117 | multiplexing registers. | ||
118 | |||
119 | config OMAP_MUX_WARNINGS | ||
120 | bool "Warn about pins the bootloader didn't set up" | ||
121 | depends on OMAP_MUX | ||
122 | default y | ||
123 | help | ||
124 | Choose Y here to warn whenever driver initialization logic needs | ||
125 | to change the pin multiplexing setup. When there are no warnings | ||
126 | printed, it's safe to deselect OMAP_MUX for your product. | ||
127 | |||
128 | choice | ||
129 | prompt "System timer" | ||
130 | default OMAP_MPU_TIMER | ||
131 | |||
132 | config OMAP_MPU_TIMER | ||
133 | bool "Use mpu timer" | ||
134 | help | ||
135 | Select this option if you want to use the OMAP mpu timer. This | ||
136 | timer provides more intra-tick resolution than the 32KHz timer, | ||
137 | but consumes more power. | ||
138 | |||
139 | config OMAP_32K_TIMER | ||
140 | bool "Use 32KHz timer" | ||
141 | depends on ARCH_OMAP16XX | ||
142 | help | ||
143 | Select this option if you want to enable the OMAP 32KHz timer. | ||
144 | This timer saves power compared to the OMAP_MPU_TIMER, and has | ||
145 | support for no tick during idle. The 32KHz timer provides less | ||
146 | intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is | ||
147 | currently only available for OMAP-16xx. | ||
148 | |||
149 | endchoice | ||
150 | |||
151 | config OMAP_32K_TIMER_HZ | ||
152 | int "Kernel internal timer frequency for 32KHz timer" | ||
153 | range 32 1024 | ||
154 | depends on OMAP_32K_TIMER | ||
155 | default "128" | ||
156 | help | ||
157 | Kernel internal timer frequency should be a divisor of 32768, | ||
158 | such as 64 or 128. | ||
159 | |||
160 | choice | ||
161 | prompt "Low-level debug console UART" | ||
162 | depends on ARCH_OMAP | ||
163 | default OMAP_LL_DEBUG_UART1 | ||
164 | |||
165 | config OMAP_LL_DEBUG_UART1 | ||
166 | bool "UART1" | ||
167 | |||
168 | config OMAP_LL_DEBUG_UART2 | ||
169 | bool "UART2" | ||
170 | |||
171 | config OMAP_LL_DEBUG_UART3 | ||
172 | bool "UART3" | ||
173 | |||
174 | endchoice | ||
175 | |||
176 | config OMAP_ARM_195MHZ | ||
177 | bool "OMAP ARM 195 MHz CPU" | ||
178 | depends on ARCH_OMAP730 | ||
179 | help | ||
180 | Enable 195MHz clock for OMAP CPU. If unsure, say N. | ||
181 | |||
182 | config OMAP_ARM_192MHZ | ||
183 | bool "OMAP ARM 192 MHz CPU" | ||
184 | depends on ARCH_OMAP16XX | ||
185 | help | ||
186 | Enable 192MHz clock for OMAP CPU. If unsure, say N. | ||
187 | |||
188 | config OMAP_ARM_182MHZ | ||
189 | bool "OMAP ARM 182 MHz CPU" | ||
190 | depends on ARCH_OMAP730 | ||
191 | help | ||
192 | Enable 182MHz clock for OMAP CPU. If unsure, say N. | ||
193 | |||
194 | config OMAP_ARM_168MHZ | ||
195 | bool "OMAP ARM 168 MHz CPU" | ||
196 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 | ||
197 | help | ||
198 | Enable 168MHz clock for OMAP CPU. If unsure, say N. | ||
199 | |||
200 | config OMAP_ARM_120MHZ | ||
201 | bool "OMAP ARM 120 MHz CPU" | ||
202 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 | ||
203 | help | ||
204 | Enable 120MHz clock for OMAP CPU. If unsure, say N. | ||
205 | |||
206 | config OMAP_ARM_60MHZ | ||
207 | bool "OMAP ARM 60 MHz CPU" | ||
208 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 | ||
209 | default y | ||
210 | help | ||
211 | Enable 60MHz clock for OMAP CPU. If unsure, say Y. | ||
212 | |||
213 | config OMAP_ARM_30MHZ | ||
214 | bool "OMAP ARM 30 MHz CPU" | ||
215 | depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 | ||
216 | help | ||
217 | Enable 30MHz clock for OMAP CPU. If unsure, say N. | ||
218 | |||
219 | endmenu | ||
220 | |||
221 | endif | ||
diff --git a/arch/arm/mach-omap/Makefile b/arch/arm/mach-omap/Makefile new file mode 100644 index 000000000000..4cafb11d2c02 --- /dev/null +++ b/arch/arm/mach-omap/Makefile | |||
@@ -0,0 +1,40 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | |||
5 | # Common support | ||
6 | obj-y := common.o time.o irq.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o | ||
7 | obj-m := | ||
8 | obj-n := | ||
9 | obj- := | ||
10 | led-y := leds.o | ||
11 | |||
12 | # Specific board support | ||
13 | obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o | ||
14 | obj-$(CONFIG_MACH_OMAP_INNOVATOR) += board-innovator.o | ||
15 | obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o | ||
16 | obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o | ||
17 | obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o | ||
18 | obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o | ||
19 | obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o | ||
20 | obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o | ||
21 | |||
22 | # OCPI interconnect support for 1710, 1610 and 5912 | ||
23 | obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o | ||
24 | |||
25 | # LEDs support | ||
26 | led-$(CONFIG_MACH_OMAP_H2) += leds-h2p2-debug.o | ||
27 | led-$(CONFIG_MACH_OMAP_INNOVATOR) += leds-innovator.o | ||
28 | led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-h2p2-debug.o | ||
29 | obj-$(CONFIG_LEDS) += $(led-y) | ||
30 | |||
31 | # Power Management | ||
32 | obj-$(CONFIG_PM) += pm.o sleep.o | ||
33 | |||
34 | ifeq ($(CONFIG_ARCH_OMAP1510),y) | ||
35 | # Innovator-1510 FPGA | ||
36 | obj-$(CONFIG_MACH_OMAP_INNOVATOR) += fpga.o | ||
37 | endif | ||
38 | |||
39 | # kgdb support | ||
40 | obj-$(CONFIG_KGDB_SERIAL) += kgdb-serial.o | ||
diff --git a/arch/arm/mach-omap/Makefile.boot b/arch/arm/mach-omap/Makefile.boot new file mode 100644 index 000000000000..fee1a6a15b54 --- /dev/null +++ b/arch/arm/mach-omap/Makefile.boot | |||
@@ -0,0 +1,4 @@ | |||
1 | zreladdr-y := 0x10008000 | ||
2 | params_phys-y := 0x10000100 | ||
3 | initrd_phys-y := 0x10800000 | ||
4 | |||
diff --git a/arch/arm/mach-omap/board-generic.c b/arch/arm/mach-omap/board-generic.c new file mode 100644 index 000000000000..2102a2cd1013 --- /dev/null +++ b/arch/arm/mach-omap/board-generic.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-generic.c | ||
3 | * | ||
4 | * Modified from board-innovator1510.c | ||
5 | * | ||
6 | * Code for generic OMAP board. Should work on many OMAP systems where | ||
7 | * the device drivers take care of all the necessary hardware initialization. | ||
8 | * Do not put any board specific code to this file; create a new machine | ||
9 | * type if you need custom low-level initializations. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/device.h> | ||
19 | |||
20 | #include <asm/hardware.h> | ||
21 | #include <asm/mach-types.h> | ||
22 | #include <asm/mach/arch.h> | ||
23 | #include <asm/mach/map.h> | ||
24 | |||
25 | #include <asm/arch/gpio.h> | ||
26 | #include <asm/arch/mux.h> | ||
27 | #include <asm/arch/usb.h> | ||
28 | #include <asm/arch/board.h> | ||
29 | |||
30 | #include "common.h" | ||
31 | |||
32 | static int __initdata generic_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
33 | |||
34 | static void __init omap_generic_init_irq(void) | ||
35 | { | ||
36 | omap_init_irq(); | ||
37 | } | ||
38 | |||
39 | /* assume no Mini-AB port */ | ||
40 | |||
41 | #ifdef CONFIG_ARCH_OMAP1510 | ||
42 | static struct omap_usb_config generic1510_usb_config __initdata = { | ||
43 | .register_host = 1, | ||
44 | .register_dev = 1, | ||
45 | .hmc_mode = 16, | ||
46 | .pins[0] = 3, | ||
47 | }; | ||
48 | #endif | ||
49 | |||
50 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
51 | static struct omap_usb_config generic1610_usb_config __initdata = { | ||
52 | .register_host = 1, | ||
53 | .register_dev = 1, | ||
54 | .hmc_mode = 16, | ||
55 | .pins[0] = 6, | ||
56 | }; | ||
57 | #endif | ||
58 | |||
59 | static struct omap_board_config_kernel generic_config[] = { | ||
60 | { OMAP_TAG_USB, NULL }, | ||
61 | }; | ||
62 | |||
63 | static void __init omap_generic_init(void) | ||
64 | { | ||
65 | /* | ||
66 | * Make sure the serial ports are muxed on at this point. | ||
67 | * You have to mux them off in device drivers later on | ||
68 | * if not needed. | ||
69 | */ | ||
70 | #ifdef CONFIG_ARCH_OMAP1510 | ||
71 | if (cpu_is_omap1510()) { | ||
72 | generic_config[0].data = &generic1510_usb_config; | ||
73 | } | ||
74 | #endif | ||
75 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
76 | if (!cpu_is_omap1510()) { | ||
77 | generic_config[0].data = &generic1610_usb_config; | ||
78 | } | ||
79 | #endif | ||
80 | omap_board_config = generic_config; | ||
81 | omap_board_config_size = ARRAY_SIZE(generic_config); | ||
82 | omap_serial_init(generic_serial_ports); | ||
83 | } | ||
84 | |||
85 | static void __init omap_generic_map_io(void) | ||
86 | { | ||
87 | omap_map_io(); | ||
88 | } | ||
89 | |||
90 | MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710") | ||
91 | MAINTAINER("Tony Lindgren <tony@atomide.com>") | ||
92 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
93 | BOOT_PARAMS(0x10000100) | ||
94 | MAPIO(omap_generic_map_io) | ||
95 | INITIRQ(omap_generic_init_irq) | ||
96 | INIT_MACHINE(omap_generic_init) | ||
97 | .timer = &omap_timer, | ||
98 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-h2.c b/arch/arm/mach-omap/board-h2.c new file mode 100644 index 000000000000..1f067830d1fc --- /dev/null +++ b/arch/arm/mach-omap/board-h2.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-h2.c | ||
3 | * | ||
4 | * Board specific inits for OMAP-1610 H2 | ||
5 | * | ||
6 | * Copyright (C) 2001 RidgeRun, Inc. | ||
7 | * Author: Greg Lonnon <glonnon@ridgerun.com> | ||
8 | * | ||
9 | * Copyright (C) 2002 MontaVista Software, Inc. | ||
10 | * | ||
11 | * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 | ||
12 | * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> | ||
13 | * | ||
14 | * H2 specific changes and cleanup | ||
15 | * Copyright (C) 2004 Nokia Corporation by Imre Deak <imre.deak@nokia.com> | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or modify | ||
18 | * it under the terms of the GNU General Public License version 2 as | ||
19 | * published by the Free Software Foundation. | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/mtd/mtd.h> | ||
27 | #include <linux/mtd/partitions.h> | ||
28 | |||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/mach-types.h> | ||
31 | #include <asm/mach/arch.h> | ||
32 | #include <asm/mach/flash.h> | ||
33 | #include <asm/mach/map.h> | ||
34 | |||
35 | #include <asm/arch/gpio.h> | ||
36 | #include <asm/arch/tc.h> | ||
37 | #include <asm/arch/usb.h> | ||
38 | |||
39 | #include "common.h" | ||
40 | |||
41 | extern int omap_gpio_init(void); | ||
42 | |||
43 | static int __initdata h2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
44 | |||
45 | static struct mtd_partition h2_partitions[] = { | ||
46 | /* bootloader (U-Boot, etc) in first sector */ | ||
47 | { | ||
48 | .name = "bootloader", | ||
49 | .offset = 0, | ||
50 | .size = SZ_128K, | ||
51 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
52 | }, | ||
53 | /* bootloader params in the next sector */ | ||
54 | { | ||
55 | .name = "params", | ||
56 | .offset = MTDPART_OFS_APPEND, | ||
57 | .size = SZ_128K, | ||
58 | .mask_flags = 0, | ||
59 | }, | ||
60 | /* kernel */ | ||
61 | { | ||
62 | .name = "kernel", | ||
63 | .offset = MTDPART_OFS_APPEND, | ||
64 | .size = SZ_2M, | ||
65 | .mask_flags = 0 | ||
66 | }, | ||
67 | /* file system */ | ||
68 | { | ||
69 | .name = "filesystem", | ||
70 | .offset = MTDPART_OFS_APPEND, | ||
71 | .size = MTDPART_SIZ_FULL, | ||
72 | .mask_flags = 0 | ||
73 | } | ||
74 | }; | ||
75 | |||
76 | static struct flash_platform_data h2_flash_data = { | ||
77 | .map_name = "cfi_probe", | ||
78 | .width = 2, | ||
79 | .parts = h2_partitions, | ||
80 | .nr_parts = ARRAY_SIZE(h2_partitions), | ||
81 | }; | ||
82 | |||
83 | static struct resource h2_flash_resource = { | ||
84 | .start = OMAP_CS2B_PHYS, | ||
85 | .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, | ||
86 | .flags = IORESOURCE_MEM, | ||
87 | }; | ||
88 | |||
89 | static struct platform_device h2_flash_device = { | ||
90 | .name = "omapflash", | ||
91 | .id = 0, | ||
92 | .dev = { | ||
93 | .platform_data = &h2_flash_data, | ||
94 | }, | ||
95 | .num_resources = 1, | ||
96 | .resource = &h2_flash_resource, | ||
97 | }; | ||
98 | |||
99 | static struct resource h2_smc91x_resources[] = { | ||
100 | [0] = { | ||
101 | .start = OMAP1610_ETHR_START, /* Physical */ | ||
102 | .end = OMAP1610_ETHR_START + 0xf, | ||
103 | .flags = IORESOURCE_MEM, | ||
104 | }, | ||
105 | [1] = { | ||
106 | .start = OMAP_GPIO_IRQ(0), | ||
107 | .end = OMAP_GPIO_IRQ(0), | ||
108 | .flags = IORESOURCE_IRQ, | ||
109 | }, | ||
110 | }; | ||
111 | |||
112 | static struct platform_device h2_smc91x_device = { | ||
113 | .name = "smc91x", | ||
114 | .id = 0, | ||
115 | .num_resources = ARRAY_SIZE(h2_smc91x_resources), | ||
116 | .resource = h2_smc91x_resources, | ||
117 | }; | ||
118 | |||
119 | static struct platform_device *h2_devices[] __initdata = { | ||
120 | &h2_flash_device, | ||
121 | &h2_smc91x_device, | ||
122 | }; | ||
123 | |||
124 | static void __init h2_init_smc91x(void) | ||
125 | { | ||
126 | if ((omap_request_gpio(0)) < 0) { | ||
127 | printk("Error requesting gpio 0 for smc91x irq\n"); | ||
128 | return; | ||
129 | } | ||
130 | omap_set_gpio_edge_ctrl(0, OMAP_GPIO_FALLING_EDGE); | ||
131 | } | ||
132 | |||
133 | void h2_init_irq(void) | ||
134 | { | ||
135 | omap_init_irq(); | ||
136 | omap_gpio_init(); | ||
137 | h2_init_smc91x(); | ||
138 | } | ||
139 | |||
140 | static struct omap_usb_config h2_usb_config __initdata = { | ||
141 | /* usb1 has a Mini-AB port and external isp1301 transceiver */ | ||
142 | .otg = 2, | ||
143 | |||
144 | #ifdef CONFIG_USB_GADGET_OMAP | ||
145 | .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled | ||
146 | // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) | ||
147 | #elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
148 | /* needs OTG cable, or NONSTANDARD (B-to-MiniB) */ | ||
149 | .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled | ||
150 | #endif | ||
151 | |||
152 | .pins[1] = 3, | ||
153 | }; | ||
154 | |||
155 | static struct omap_mmc_config h2_mmc_config __initdata = { | ||
156 | .mmc_blocks = 1, | ||
157 | .mmc1_power_pin = -1, /* tps65010 gpio3 */ | ||
158 | .mmc1_switch_pin = OMAP_MPUIO(1), | ||
159 | }; | ||
160 | |||
161 | static struct omap_board_config_kernel h2_config[] = { | ||
162 | { OMAP_TAG_USB, &h2_usb_config }, | ||
163 | { OMAP_TAG_MMC, &h2_mmc_config }, | ||
164 | }; | ||
165 | |||
166 | static void __init h2_init(void) | ||
167 | { | ||
168 | platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); | ||
169 | omap_board_config = h2_config; | ||
170 | omap_board_config_size = ARRAY_SIZE(h2_config); | ||
171 | } | ||
172 | |||
173 | static void __init h2_map_io(void) | ||
174 | { | ||
175 | omap_map_io(); | ||
176 | omap_serial_init(h2_serial_ports); | ||
177 | } | ||
178 | |||
179 | MACHINE_START(OMAP_H2, "TI-H2") | ||
180 | MAINTAINER("Imre Deak <imre.deak@nokia.com>") | ||
181 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
182 | BOOT_PARAMS(0x10000100) | ||
183 | MAPIO(h2_map_io) | ||
184 | INITIRQ(h2_init_irq) | ||
185 | INIT_MACHINE(h2_init) | ||
186 | .timer = &omap_timer, | ||
187 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-h3.c b/arch/arm/mach-omap/board-h3.c new file mode 100644 index 000000000000..486a5a006c9a --- /dev/null +++ b/arch/arm/mach-omap/board-h3.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-h3.c | ||
3 | * | ||
4 | * This file contains OMAP1710 H3 specific code. | ||
5 | * | ||
6 | * Copyright (C) 2004 Texas Instruments, Inc. | ||
7 | * Copyright (C) 2002 MontaVista Software, Inc. | ||
8 | * Copyright (C) 2001 RidgeRun, Inc. | ||
9 | * Author: RidgeRun, Inc. | ||
10 | * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/major.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/device.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/mtd/mtd.h> | ||
25 | #include <linux/mtd/partitions.h> | ||
26 | |||
27 | #include <asm/setup.h> | ||
28 | #include <asm/page.h> | ||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/mach-types.h> | ||
31 | #include <asm/mach/arch.h> | ||
32 | #include <asm/mach/flash.h> | ||
33 | #include <asm/mach/map.h> | ||
34 | |||
35 | #include <asm/arch/gpio.h> | ||
36 | #include <asm/arch/irqs.h> | ||
37 | #include <asm/arch/mux.h> | ||
38 | #include <asm/arch/tc.h> | ||
39 | #include <asm/arch/usb.h> | ||
40 | |||
41 | #include "common.h" | ||
42 | |||
43 | extern int omap_gpio_init(void); | ||
44 | |||
45 | static int __initdata h3_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
46 | |||
47 | static struct mtd_partition h3_partitions[] = { | ||
48 | /* bootloader (U-Boot, etc) in first sector */ | ||
49 | { | ||
50 | .name = "bootloader", | ||
51 | .offset = 0, | ||
52 | .size = SZ_128K, | ||
53 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
54 | }, | ||
55 | /* bootloader params in the next sector */ | ||
56 | { | ||
57 | .name = "params", | ||
58 | .offset = MTDPART_OFS_APPEND, | ||
59 | .size = SZ_128K, | ||
60 | .mask_flags = 0, | ||
61 | }, | ||
62 | /* kernel */ | ||
63 | { | ||
64 | .name = "kernel", | ||
65 | .offset = MTDPART_OFS_APPEND, | ||
66 | .size = SZ_2M, | ||
67 | .mask_flags = 0 | ||
68 | }, | ||
69 | /* file system */ | ||
70 | { | ||
71 | .name = "filesystem", | ||
72 | .offset = MTDPART_OFS_APPEND, | ||
73 | .size = MTDPART_SIZ_FULL, | ||
74 | .mask_flags = 0 | ||
75 | } | ||
76 | }; | ||
77 | |||
78 | static struct flash_platform_data h3_flash_data = { | ||
79 | .map_name = "cfi_probe", | ||
80 | .width = 2, | ||
81 | .parts = h3_partitions, | ||
82 | .nr_parts = ARRAY_SIZE(h3_partitions), | ||
83 | }; | ||
84 | |||
85 | static struct resource h3_flash_resource = { | ||
86 | .start = OMAP_CS2B_PHYS, | ||
87 | .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, | ||
88 | .flags = IORESOURCE_MEM, | ||
89 | }; | ||
90 | |||
91 | static struct platform_device flash_device = { | ||
92 | .name = "omapflash", | ||
93 | .id = 0, | ||
94 | .dev = { | ||
95 | .platform_data = &h3_flash_data, | ||
96 | }, | ||
97 | .num_resources = 1, | ||
98 | .resource = &h3_flash_resource, | ||
99 | }; | ||
100 | |||
101 | static struct resource smc91x_resources[] = { | ||
102 | [0] = { | ||
103 | .start = OMAP1710_ETHR_START, /* Physical */ | ||
104 | .end = OMAP1710_ETHR_START + 0xf, | ||
105 | .flags = IORESOURCE_MEM, | ||
106 | }, | ||
107 | [1] = { | ||
108 | .start = OMAP_GPIO_IRQ(40), | ||
109 | .end = OMAP_GPIO_IRQ(40), | ||
110 | .flags = IORESOURCE_IRQ, | ||
111 | }, | ||
112 | }; | ||
113 | |||
114 | static struct platform_device smc91x_device = { | ||
115 | .name = "smc91x", | ||
116 | .id = 0, | ||
117 | .num_resources = ARRAY_SIZE(smc91x_resources), | ||
118 | .resource = smc91x_resources, | ||
119 | }; | ||
120 | |||
121 | #define GPTIMER_BASE 0xFFFB1400 | ||
122 | #define GPTIMER_REGS(x) (0xFFFB1400 + (x * 0x800)) | ||
123 | #define GPTIMER_REGS_SIZE 0x46 | ||
124 | |||
125 | static struct resource intlat_resources[] = { | ||
126 | [0] = { | ||
127 | .start = GPTIMER_REGS(0), /* Physical */ | ||
128 | .end = GPTIMER_REGS(0) + GPTIMER_REGS_SIZE, | ||
129 | .flags = IORESOURCE_MEM, | ||
130 | }, | ||
131 | [1] = { | ||
132 | .start = INT_1610_GPTIMER1, | ||
133 | .end = INT_1610_GPTIMER1, | ||
134 | .flags = IORESOURCE_IRQ, | ||
135 | }, | ||
136 | }; | ||
137 | |||
138 | static struct platform_device intlat_device = { | ||
139 | .name = "omap_intlat", | ||
140 | .id = 0, | ||
141 | .num_resources = ARRAY_SIZE(intlat_resources), | ||
142 | .resource = intlat_resources, | ||
143 | }; | ||
144 | |||
145 | static struct platform_device *devices[] __initdata = { | ||
146 | &flash_device, | ||
147 | &smc91x_device, | ||
148 | &intlat_device, | ||
149 | }; | ||
150 | |||
151 | static struct omap_usb_config h3_usb_config __initdata = { | ||
152 | /* usb1 has a Mini-AB port and external isp1301 transceiver */ | ||
153 | .otg = 2, | ||
154 | |||
155 | #ifdef CONFIG_USB_GADGET_OMAP | ||
156 | .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ | ||
157 | #elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
158 | /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ | ||
159 | .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ | ||
160 | #endif | ||
161 | |||
162 | .pins[1] = 3, | ||
163 | }; | ||
164 | |||
165 | static struct omap_board_config_kernel h3_config[] = { | ||
166 | { OMAP_TAG_USB, &h3_usb_config }, | ||
167 | }; | ||
168 | |||
169 | static void __init h3_init(void) | ||
170 | { | ||
171 | (void) platform_add_devices(devices, ARRAY_SIZE(devices)); | ||
172 | } | ||
173 | |||
174 | static void __init h3_init_smc91x(void) | ||
175 | { | ||
176 | omap_cfg_reg(W15_1710_GPIO40); | ||
177 | if (omap_request_gpio(40) < 0) { | ||
178 | printk("Error requesting gpio 40 for smc91x irq\n"); | ||
179 | return; | ||
180 | } | ||
181 | omap_set_gpio_edge_ctrl(40, OMAP_GPIO_FALLING_EDGE); | ||
182 | } | ||
183 | |||
184 | void h3_init_irq(void) | ||
185 | { | ||
186 | omap_init_irq(); | ||
187 | omap_gpio_init(); | ||
188 | h3_init_smc91x(); | ||
189 | } | ||
190 | |||
191 | static void __init h3_map_io(void) | ||
192 | { | ||
193 | omap_map_io(); | ||
194 | omap_serial_init(h3_serial_ports); | ||
195 | } | ||
196 | |||
197 | MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") | ||
198 | MAINTAINER("Texas Instruments, Inc.") | ||
199 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
200 | BOOT_PARAMS(0x10000100) | ||
201 | MAPIO(h3_map_io) | ||
202 | INITIRQ(h3_init_irq) | ||
203 | INIT_MACHINE(h3_init) | ||
204 | .timer = &omap_timer, | ||
205 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-innovator.c b/arch/arm/mach-omap/board-innovator.c new file mode 100644 index 000000000000..57cf4da88d55 --- /dev/null +++ b/arch/arm/mach-omap/board-innovator.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-innovator.c | ||
3 | * | ||
4 | * Board specific inits for OMAP-1510 and OMAP-1610 Innovator | ||
5 | * | ||
6 | * Copyright (C) 2001 RidgeRun, Inc. | ||
7 | * Author: Greg Lonnon <glonnon@ridgerun.com> | ||
8 | * | ||
9 | * Copyright (C) 2002 MontaVista Software, Inc. | ||
10 | * | ||
11 | * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 | ||
12 | * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> | ||
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 version 2 as | ||
16 | * published by the Free Software Foundation. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/mtd/mtd.h> | ||
24 | #include <linux/mtd/partitions.h> | ||
25 | |||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/mach-types.h> | ||
28 | #include <asm/mach/arch.h> | ||
29 | #include <asm/mach/flash.h> | ||
30 | #include <asm/mach/map.h> | ||
31 | |||
32 | #include <asm/arch/fpga.h> | ||
33 | #include <asm/arch/gpio.h> | ||
34 | #include <asm/arch/tc.h> | ||
35 | #include <asm/arch/usb.h> | ||
36 | |||
37 | #include "common.h" | ||
38 | |||
39 | static int __initdata innovator_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
40 | |||
41 | static struct mtd_partition innovator_partitions[] = { | ||
42 | /* bootloader (U-Boot, etc) in first sector */ | ||
43 | { | ||
44 | .name = "bootloader", | ||
45 | .offset = 0, | ||
46 | .size = SZ_128K, | ||
47 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
48 | }, | ||
49 | /* bootloader params in the next sector */ | ||
50 | { | ||
51 | .name = "params", | ||
52 | .offset = MTDPART_OFS_APPEND, | ||
53 | .size = SZ_128K, | ||
54 | .mask_flags = 0, | ||
55 | }, | ||
56 | /* kernel */ | ||
57 | { | ||
58 | .name = "kernel", | ||
59 | .offset = MTDPART_OFS_APPEND, | ||
60 | .size = SZ_2M, | ||
61 | .mask_flags = 0 | ||
62 | }, | ||
63 | /* rest of flash1 is a file system */ | ||
64 | { | ||
65 | .name = "rootfs", | ||
66 | .offset = MTDPART_OFS_APPEND, | ||
67 | .size = SZ_16M - SZ_2M - 2 * SZ_128K, | ||
68 | .mask_flags = 0 | ||
69 | }, | ||
70 | /* file system */ | ||
71 | { | ||
72 | .name = "filesystem", | ||
73 | .offset = MTDPART_OFS_APPEND, | ||
74 | .size = MTDPART_SIZ_FULL, | ||
75 | .mask_flags = 0 | ||
76 | } | ||
77 | }; | ||
78 | |||
79 | static struct flash_platform_data innovator_flash_data = { | ||
80 | .map_name = "cfi_probe", | ||
81 | .width = 2, | ||
82 | .parts = innovator_partitions, | ||
83 | .nr_parts = ARRAY_SIZE(innovator_partitions), | ||
84 | }; | ||
85 | |||
86 | static struct resource innovator_flash_resource = { | ||
87 | .start = OMAP_CS0_PHYS, | ||
88 | .end = OMAP_CS0_PHYS + SZ_32M - 1, | ||
89 | .flags = IORESOURCE_MEM, | ||
90 | }; | ||
91 | |||
92 | static struct platform_device innovator_flash_device = { | ||
93 | .name = "omapflash", | ||
94 | .id = 0, | ||
95 | .dev = { | ||
96 | .platform_data = &innovator_flash_data, | ||
97 | }, | ||
98 | .num_resources = 1, | ||
99 | .resource = &innovator_flash_resource, | ||
100 | }; | ||
101 | |||
102 | #ifdef CONFIG_ARCH_OMAP1510 | ||
103 | |||
104 | /* Only FPGA needs to be mapped here. All others are done with ioremap */ | ||
105 | static struct map_desc innovator1510_io_desc[] __initdata = { | ||
106 | { OMAP1510_FPGA_BASE, OMAP1510_FPGA_START, OMAP1510_FPGA_SIZE, | ||
107 | MT_DEVICE }, | ||
108 | }; | ||
109 | |||
110 | static struct resource innovator1510_smc91x_resources[] = { | ||
111 | [0] = { | ||
112 | .start = OMAP1510_FPGA_ETHR_START, /* Physical */ | ||
113 | .end = OMAP1510_FPGA_ETHR_START + 0xf, | ||
114 | .flags = IORESOURCE_MEM, | ||
115 | }, | ||
116 | [1] = { | ||
117 | .start = OMAP1510_INT_ETHER, | ||
118 | .end = OMAP1510_INT_ETHER, | ||
119 | .flags = IORESOURCE_IRQ, | ||
120 | }, | ||
121 | }; | ||
122 | |||
123 | static struct platform_device innovator1510_smc91x_device = { | ||
124 | .name = "smc91x", | ||
125 | .id = 0, | ||
126 | .num_resources = ARRAY_SIZE(innovator1510_smc91x_resources), | ||
127 | .resource = innovator1510_smc91x_resources, | ||
128 | }; | ||
129 | |||
130 | static struct platform_device *innovator1510_devices[] __initdata = { | ||
131 | &innovator_flash_device, | ||
132 | &innovator1510_smc91x_device, | ||
133 | }; | ||
134 | |||
135 | #endif /* CONFIG_ARCH_OMAP1510 */ | ||
136 | |||
137 | #ifdef CONFIG_ARCH_OMAP16XX | ||
138 | |||
139 | static struct resource innovator1610_smc91x_resources[] = { | ||
140 | [0] = { | ||
141 | .start = INNOVATOR1610_ETHR_START, /* Physical */ | ||
142 | .end = INNOVATOR1610_ETHR_START + 0xf, | ||
143 | .flags = IORESOURCE_MEM, | ||
144 | }, | ||
145 | [1] = { | ||
146 | .start = OMAP_GPIO_IRQ(0), | ||
147 | .end = OMAP_GPIO_IRQ(0), | ||
148 | .flags = IORESOURCE_IRQ, | ||
149 | }, | ||
150 | }; | ||
151 | |||
152 | static struct platform_device innovator1610_smc91x_device = { | ||
153 | .name = "smc91x", | ||
154 | .id = 0, | ||
155 | .num_resources = ARRAY_SIZE(innovator1610_smc91x_resources), | ||
156 | .resource = innovator1610_smc91x_resources, | ||
157 | }; | ||
158 | |||
159 | static struct platform_device *innovator1610_devices[] __initdata = { | ||
160 | &innovator_flash_device, | ||
161 | &innovator1610_smc91x_device, | ||
162 | }; | ||
163 | |||
164 | #endif /* CONFIG_ARCH_OMAP16XX */ | ||
165 | |||
166 | static void __init innovator_init_smc91x(void) | ||
167 | { | ||
168 | if (cpu_is_omap1510()) { | ||
169 | fpga_write(fpga_read(OMAP1510_FPGA_RST) & ~1, | ||
170 | OMAP1510_FPGA_RST); | ||
171 | udelay(750); | ||
172 | } else { | ||
173 | if ((omap_request_gpio(0)) < 0) { | ||
174 | printk("Error requesting gpio 0 for smc91x irq\n"); | ||
175 | return; | ||
176 | } | ||
177 | omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | void innovator_init_irq(void) | ||
182 | { | ||
183 | omap_init_irq(); | ||
184 | omap_gpio_init(); | ||
185 | #ifdef CONFIG_ARCH_OMAP1510 | ||
186 | if (cpu_is_omap1510()) { | ||
187 | omap1510_fpga_init_irq(); | ||
188 | } | ||
189 | #endif | ||
190 | innovator_init_smc91x(); | ||
191 | } | ||
192 | |||
193 | #ifdef CONFIG_ARCH_OMAP1510 | ||
194 | static struct omap_usb_config innovator1510_usb_config __initdata = { | ||
195 | /* for bundled non-standard host and peripheral cables */ | ||
196 | .hmc_mode = 4, | ||
197 | |||
198 | .register_host = 1, | ||
199 | .pins[1] = 6, | ||
200 | .pins[2] = 6, /* Conflicts with UART2 */ | ||
201 | |||
202 | .register_dev = 1, | ||
203 | .pins[0] = 2, | ||
204 | }; | ||
205 | #endif | ||
206 | |||
207 | #ifdef CONFIG_ARCH_OMAP16XX | ||
208 | static struct omap_usb_config h2_usb_config __initdata = { | ||
209 | /* usb1 has a Mini-AB port and external isp1301 transceiver */ | ||
210 | .otg = 2, | ||
211 | |||
212 | #ifdef CONFIG_USB_GADGET_OMAP | ||
213 | .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled | ||
214 | // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) | ||
215 | #elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
216 | /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ | ||
217 | .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled | ||
218 | #endif | ||
219 | |||
220 | .pins[1] = 3, | ||
221 | }; | ||
222 | #endif | ||
223 | |||
224 | static struct omap_board_config_kernel innovator_config[] = { | ||
225 | { OMAP_TAG_USB, NULL }, | ||
226 | }; | ||
227 | |||
228 | static void __init innovator_init(void) | ||
229 | { | ||
230 | #ifdef CONFIG_ARCH_OMAP1510 | ||
231 | if (cpu_is_omap1510()) { | ||
232 | platform_add_devices(innovator1510_devices, ARRAY_SIZE(innovator1510_devices)); | ||
233 | } | ||
234 | #endif | ||
235 | #ifdef CONFIG_ARCH_OMAP16XX | ||
236 | if (!cpu_is_omap1510()) { | ||
237 | platform_add_devices(innovator1610_devices, ARRAY_SIZE(innovator1610_devices)); | ||
238 | } | ||
239 | #endif | ||
240 | |||
241 | #ifdef CONFIG_ARCH_OMAP1510 | ||
242 | if (cpu_is_omap1510()) | ||
243 | innovator_config[0].data = &innovator1510_usb_config; | ||
244 | #endif | ||
245 | #ifdef CONFIG_ARCH_OMAP16XX | ||
246 | if (cpu_is_omap1610()) | ||
247 | innovator_config[0].data = &h2_usb_config; | ||
248 | #endif | ||
249 | omap_board_config = innovator_config; | ||
250 | omap_board_config_size = ARRAY_SIZE(innovator_config); | ||
251 | } | ||
252 | |||
253 | static void __init innovator_map_io(void) | ||
254 | { | ||
255 | omap_map_io(); | ||
256 | |||
257 | #ifdef CONFIG_ARCH_OMAP1510 | ||
258 | if (cpu_is_omap1510()) { | ||
259 | iotable_init(innovator1510_io_desc, ARRAY_SIZE(innovator1510_io_desc)); | ||
260 | udelay(10); /* Delay needed for FPGA */ | ||
261 | |||
262 | /* Dump the Innovator FPGA rev early - useful info for support. */ | ||
263 | printk("Innovator FPGA Rev %d.%d Board Rev %d\n", | ||
264 | fpga_read(OMAP1510_FPGA_REV_HIGH), | ||
265 | fpga_read(OMAP1510_FPGA_REV_LOW), | ||
266 | fpga_read(OMAP1510_FPGA_BOARD_REV)); | ||
267 | } | ||
268 | #endif | ||
269 | omap_serial_init(innovator_serial_ports); | ||
270 | } | ||
271 | |||
272 | MACHINE_START(OMAP_INNOVATOR, "TI-Innovator") | ||
273 | MAINTAINER("MontaVista Software, Inc.") | ||
274 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
275 | BOOT_PARAMS(0x10000100) | ||
276 | MAPIO(innovator_map_io) | ||
277 | INITIRQ(innovator_init_irq) | ||
278 | INIT_MACHINE(innovator_init) | ||
279 | .timer = &omap_timer, | ||
280 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-netstar.c b/arch/arm/mach-omap/board-netstar.c new file mode 100644 index 000000000000..54acbd215c4b --- /dev/null +++ b/arch/arm/mach-omap/board-netstar.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Modified from board-generic.c | ||
3 | * | ||
4 | * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz> | ||
5 | * | ||
6 | * Code for Netstar OMAP board. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/notifier.h> | ||
19 | #include <linux/reboot.h> | ||
20 | |||
21 | #include <asm/hardware.h> | ||
22 | #include <asm/mach-types.h> | ||
23 | #include <asm/mach/arch.h> | ||
24 | #include <asm/mach/map.h> | ||
25 | |||
26 | #include <asm/arch/gpio.h> | ||
27 | #include <asm/arch/mux.h> | ||
28 | #include <asm/arch/usb.h> | ||
29 | |||
30 | #include "common.h" | ||
31 | |||
32 | extern void __init omap_init_time(void); | ||
33 | extern int omap_gpio_init(void); | ||
34 | |||
35 | static struct resource netstar_smc91x_resources[] = { | ||
36 | [0] = { | ||
37 | .start = OMAP_CS1_PHYS + 0x300, | ||
38 | .end = OMAP_CS1_PHYS + 0x300 + 16, | ||
39 | .flags = IORESOURCE_MEM, | ||
40 | }, | ||
41 | [1] = { | ||
42 | .start = OMAP_GPIO_IRQ(8), | ||
43 | .end = OMAP_GPIO_IRQ(8), | ||
44 | .flags = IORESOURCE_IRQ, | ||
45 | }, | ||
46 | }; | ||
47 | |||
48 | static struct platform_device netstar_smc91x_device = { | ||
49 | .name = "smc91x", | ||
50 | .id = 0, | ||
51 | .num_resources = ARRAY_SIZE(netstar_smc91x_resources), | ||
52 | .resource = netstar_smc91x_resources, | ||
53 | }; | ||
54 | |||
55 | static struct platform_device *netstar_devices[] __initdata = { | ||
56 | &netstar_smc91x_device, | ||
57 | }; | ||
58 | |||
59 | static void __init netstar_init_irq(void) | ||
60 | { | ||
61 | omap_init_irq(); | ||
62 | omap_gpio_init(); | ||
63 | } | ||
64 | |||
65 | static void __init netstar_init(void) | ||
66 | { | ||
67 | /* green LED */ | ||
68 | omap_request_gpio(4); | ||
69 | omap_set_gpio_direction(4, 0); | ||
70 | /* smc91x reset */ | ||
71 | omap_request_gpio(7); | ||
72 | omap_set_gpio_direction(7, 0); | ||
73 | omap_set_gpio_dataout(7, 1); | ||
74 | udelay(2); /* wait at least 100ns */ | ||
75 | omap_set_gpio_dataout(7, 0); | ||
76 | mdelay(50); /* 50ms until PHY ready */ | ||
77 | /* smc91x interrupt pin */ | ||
78 | omap_request_gpio(8); | ||
79 | omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); | ||
80 | |||
81 | omap_request_gpio(12); | ||
82 | omap_request_gpio(13); | ||
83 | omap_request_gpio(14); | ||
84 | omap_request_gpio(15); | ||
85 | omap_set_gpio_edge_ctrl(12, OMAP_GPIO_FALLING_EDGE); | ||
86 | omap_set_gpio_edge_ctrl(13, OMAP_GPIO_FALLING_EDGE); | ||
87 | omap_set_gpio_edge_ctrl(14, OMAP_GPIO_FALLING_EDGE); | ||
88 | omap_set_gpio_edge_ctrl(15, OMAP_GPIO_FALLING_EDGE); | ||
89 | |||
90 | platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices)); | ||
91 | |||
92 | /* Switch on green LED */ | ||
93 | omap_set_gpio_dataout(4, 0); | ||
94 | /* Switch off red LED */ | ||
95 | omap_writeb(0x00, OMAP_LPG1_PMR); /* Disable clock */ | ||
96 | omap_writeb(0x80, OMAP_LPG1_LCR); | ||
97 | } | ||
98 | |||
99 | static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
100 | |||
101 | static void __init netstar_map_io(void) | ||
102 | { | ||
103 | omap_map_io(); | ||
104 | omap_serial_init(omap_serial_ports); | ||
105 | } | ||
106 | |||
107 | #define MACHINE_PANICED 1 | ||
108 | #define MACHINE_REBOOTING 2 | ||
109 | #define MACHINE_REBOOT 4 | ||
110 | static unsigned long machine_state; | ||
111 | |||
112 | static int panic_event(struct notifier_block *this, unsigned long event, | ||
113 | void *ptr) | ||
114 | { | ||
115 | if (test_and_set_bit(MACHINE_PANICED, &machine_state)) | ||
116 | return NOTIFY_DONE; | ||
117 | |||
118 | /* Switch off green LED */ | ||
119 | omap_set_gpio_dataout(4, 1); | ||
120 | /* Flash red LED */ | ||
121 | omap_writeb(0x78, OMAP_LPG1_LCR); | ||
122 | omap_writeb(0x01, OMAP_LPG1_PMR); /* Enable clock */ | ||
123 | |||
124 | return NOTIFY_DONE; | ||
125 | } | ||
126 | |||
127 | static struct notifier_block panic_block = { | ||
128 | .notifier_call = panic_event, | ||
129 | }; | ||
130 | |||
131 | static int __init netstar_late_init(void) | ||
132 | { | ||
133 | /* TODO: Setup front panel switch here */ | ||
134 | |||
135 | /* Setup panic notifier */ | ||
136 | notifier_chain_register(&panic_notifier_list, &panic_block); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | postcore_initcall(netstar_late_init); | ||
142 | |||
143 | MACHINE_START(NETSTAR, "NetStar OMAP5910") | ||
144 | MAINTAINER("Ladislav Michl <michl@2n.cz>") | ||
145 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
146 | BOOT_PARAMS(0x10000100) | ||
147 | MAPIO(netstar_map_io) | ||
148 | INITIRQ(netstar_init_irq) | ||
149 | INIT_MACHINE(netstar_init) | ||
150 | .timer = &omap_timer, | ||
151 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-osk.c b/arch/arm/mach-omap/board-osk.c new file mode 100644 index 000000000000..a951fc82459b --- /dev/null +++ b/arch/arm/mach-omap/board-osk.c | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-osk.c | ||
3 | * | ||
4 | * Board specific init for OMAP5912 OSK | ||
5 | * | ||
6 | * Written by Dirk Behme <dirk.behme@de.bosch.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License along | ||
25 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | */ | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/mach-types.h> | ||
35 | #include <asm/mach/arch.h> | ||
36 | #include <asm/mach/map.h> | ||
37 | |||
38 | #include <asm/arch/gpio.h> | ||
39 | #include <asm/arch/usb.h> | ||
40 | #include <asm/arch/mux.h> | ||
41 | #include <asm/arch/tc.h> | ||
42 | |||
43 | #include "common.h" | ||
44 | |||
45 | static struct map_desc osk5912_io_desc[] __initdata = { | ||
46 | { OMAP_OSK_NOR_FLASH_BASE, OMAP_OSK_NOR_FLASH_START, OMAP_OSK_NOR_FLASH_SIZE, | ||
47 | MT_DEVICE }, | ||
48 | }; | ||
49 | |||
50 | static int __initdata osk_serial_ports[OMAP_MAX_NR_PORTS] = {1, 0, 0}; | ||
51 | |||
52 | static struct resource osk5912_smc91x_resources[] = { | ||
53 | [0] = { | ||
54 | .start = OMAP_OSK_ETHR_START, /* Physical */ | ||
55 | .end = OMAP_OSK_ETHR_START + 0xf, | ||
56 | .flags = IORESOURCE_MEM, | ||
57 | }, | ||
58 | [1] = { | ||
59 | .start = OMAP_GPIO_IRQ(0), | ||
60 | .end = OMAP_GPIO_IRQ(0), | ||
61 | .flags = IORESOURCE_IRQ, | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | static struct platform_device osk5912_smc91x_device = { | ||
66 | .name = "smc91x", | ||
67 | .id = -1, | ||
68 | .num_resources = ARRAY_SIZE(osk5912_smc91x_resources), | ||
69 | .resource = osk5912_smc91x_resources, | ||
70 | }; | ||
71 | |||
72 | static struct resource osk5912_cf_resources[] = { | ||
73 | [0] = { | ||
74 | .start = OMAP_GPIO_IRQ(62), | ||
75 | .end = OMAP_GPIO_IRQ(62), | ||
76 | .flags = IORESOURCE_IRQ, | ||
77 | }, | ||
78 | }; | ||
79 | |||
80 | static struct platform_device osk5912_cf_device = { | ||
81 | .name = "omap_cf", | ||
82 | .id = -1, | ||
83 | .dev = { | ||
84 | .platform_data = (void *) 2 /* CS2 */, | ||
85 | }, | ||
86 | .num_resources = ARRAY_SIZE(osk5912_cf_resources), | ||
87 | .resource = osk5912_cf_resources, | ||
88 | }; | ||
89 | |||
90 | static struct platform_device *osk5912_devices[] __initdata = { | ||
91 | &osk5912_smc91x_device, | ||
92 | &osk5912_cf_device, | ||
93 | }; | ||
94 | |||
95 | static void __init osk_init_smc91x(void) | ||
96 | { | ||
97 | if ((omap_request_gpio(0)) < 0) { | ||
98 | printk("Error requesting gpio 0 for smc91x irq\n"); | ||
99 | return; | ||
100 | } | ||
101 | omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); | ||
102 | |||
103 | /* Check EMIFS wait states to fix errors with SMC_GET_PKT_HDR */ | ||
104 | EMIFS_CCS(1) |= 0x2; | ||
105 | } | ||
106 | |||
107 | static void __init osk_init_cf(void) | ||
108 | { | ||
109 | omap_cfg_reg(M7_1610_GPIO62); | ||
110 | if ((omap_request_gpio(62)) < 0) { | ||
111 | printk("Error requesting gpio 62 for CF irq\n"); | ||
112 | return; | ||
113 | } | ||
114 | /* it's really active-low */ | ||
115 | omap_set_gpio_edge_ctrl(62, OMAP_GPIO_FALLING_EDGE); | ||
116 | } | ||
117 | |||
118 | void osk_init_irq(void) | ||
119 | { | ||
120 | omap_init_irq(); | ||
121 | omap_gpio_init(); | ||
122 | osk_init_smc91x(); | ||
123 | osk_init_cf(); | ||
124 | } | ||
125 | |||
126 | static struct omap_usb_config osk_usb_config __initdata = { | ||
127 | /* has usb host connector (A) ... for development it can also | ||
128 | * be used, with a NONSTANDARD gender-bending cable/dongle, as | ||
129 | * a peripheral. | ||
130 | */ | ||
131 | #ifdef CONFIG_USB_GADGET_OMAP | ||
132 | .register_dev = 1, | ||
133 | .hmc_mode = 0, | ||
134 | #else | ||
135 | .register_host = 1, | ||
136 | .hmc_mode = 16, | ||
137 | .rwc = 1, | ||
138 | #endif | ||
139 | .pins[0] = 2, | ||
140 | }; | ||
141 | |||
142 | static struct omap_board_config_kernel osk_config[] = { | ||
143 | { OMAP_TAG_USB, &osk_usb_config }, | ||
144 | }; | ||
145 | |||
146 | static void __init osk_init(void) | ||
147 | { | ||
148 | platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); | ||
149 | omap_board_config = osk_config; | ||
150 | omap_board_config_size = ARRAY_SIZE(osk_config); | ||
151 | USB_TRANSCEIVER_CTRL_REG |= (3 << 1); | ||
152 | } | ||
153 | |||
154 | static void __init osk_map_io(void) | ||
155 | { | ||
156 | omap_map_io(); | ||
157 | iotable_init(osk5912_io_desc, ARRAY_SIZE(osk5912_io_desc)); | ||
158 | omap_serial_init(osk_serial_ports); | ||
159 | } | ||
160 | |||
161 | MACHINE_START(OMAP_OSK, "TI-OSK") | ||
162 | MAINTAINER("Dirk Behme <dirk.behme@de.bosch.com>") | ||
163 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
164 | BOOT_PARAMS(0x10000100) | ||
165 | MAPIO(osk_map_io) | ||
166 | INITIRQ(osk_init_irq) | ||
167 | INIT_MACHINE(osk_init) | ||
168 | .timer = &omap_timer, | ||
169 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-perseus2.c b/arch/arm/mach-omap/board-perseus2.c new file mode 100644 index 000000000000..64515aeb49cf --- /dev/null +++ b/arch/arm/mach-omap/board-perseus2.c | |||
@@ -0,0 +1,189 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-perseus2.c | ||
3 | * | ||
4 | * Modified from board-generic.c | ||
5 | * | ||
6 | * Original OMAP730 support by Jean Pihet <j-pihet@ti.com> | ||
7 | * Updated for 2.6 by Kevin Hilman <kjh@hilman.org> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/mtd/mtd.h> | ||
19 | #include <linux/mtd/partitions.h> | ||
20 | |||
21 | #include <asm/hardware.h> | ||
22 | #include <asm/mach-types.h> | ||
23 | #include <asm/mach/arch.h> | ||
24 | #include <asm/mach/flash.h> | ||
25 | #include <asm/mach/map.h> | ||
26 | |||
27 | #include <asm/arch/gpio.h> | ||
28 | #include <asm/arch/mux.h> | ||
29 | #include <asm/arch/fpga.h> | ||
30 | |||
31 | #include "common.h" | ||
32 | |||
33 | static struct resource smc91x_resources[] = { | ||
34 | [0] = { | ||
35 | .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ | ||
36 | .end = H2P2_DBG_FPGA_ETHR_START + 0xf, | ||
37 | .flags = IORESOURCE_MEM, | ||
38 | }, | ||
39 | [1] = { | ||
40 | .start = INT_730_MPU_EXT_NIRQ, | ||
41 | .end = 0, | ||
42 | .flags = IORESOURCE_IRQ, | ||
43 | }, | ||
44 | }; | ||
45 | |||
46 | static int __initdata p2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 0}; | ||
47 | |||
48 | static struct mtd_partition p2_partitions[] = { | ||
49 | /* bootloader (U-Boot, etc) in first sector */ | ||
50 | { | ||
51 | .name = "bootloader", | ||
52 | .offset = 0, | ||
53 | .size = SZ_128K, | ||
54 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
55 | }, | ||
56 | /* bootloader params in the next sector */ | ||
57 | { | ||
58 | .name = "params", | ||
59 | .offset = MTDPART_OFS_APPEND, | ||
60 | .size = SZ_128K, | ||
61 | .mask_flags = 0, | ||
62 | }, | ||
63 | /* kernel */ | ||
64 | { | ||
65 | .name = "kernel", | ||
66 | .offset = MTDPART_OFS_APPEND, | ||
67 | .size = SZ_2M, | ||
68 | .mask_flags = 0 | ||
69 | }, | ||
70 | /* rest of flash is a file system */ | ||
71 | { | ||
72 | .name = "rootfs", | ||
73 | .offset = MTDPART_OFS_APPEND, | ||
74 | .size = MTDPART_SIZ_FULL, | ||
75 | .mask_flags = 0 | ||
76 | }, | ||
77 | }; | ||
78 | |||
79 | static struct flash_platform_data p2_flash_data = { | ||
80 | .map_name = "cfi_probe", | ||
81 | .width = 2, | ||
82 | .parts = p2_partitions, | ||
83 | .nr_parts = ARRAY_SIZE(p2_partitions), | ||
84 | }; | ||
85 | |||
86 | static struct resource p2_flash_resource = { | ||
87 | .start = OMAP_FLASH_0_START, | ||
88 | .end = OMAP_FLASH_0_START + OMAP_FLASH_0_SIZE - 1, | ||
89 | .flags = IORESOURCE_MEM, | ||
90 | }; | ||
91 | |||
92 | static struct platform_device p2_flash_device = { | ||
93 | .name = "omapflash", | ||
94 | .id = 0, | ||
95 | .dev = { | ||
96 | .platform_data = &p2_flash_data, | ||
97 | }, | ||
98 | .num_resources = 1, | ||
99 | .resource = &p2_flash_resource, | ||
100 | }; | ||
101 | |||
102 | static struct platform_device smc91x_device = { | ||
103 | .name = "smc91x", | ||
104 | .id = 0, | ||
105 | .num_resources = ARRAY_SIZE(smc91x_resources), | ||
106 | .resource = smc91x_resources, | ||
107 | }; | ||
108 | |||
109 | static struct platform_device *devices[] __initdata = { | ||
110 | &p2_flash_device, | ||
111 | &smc91x_device, | ||
112 | }; | ||
113 | |||
114 | static void __init omap_perseus2_init(void) | ||
115 | { | ||
116 | (void) platform_add_devices(devices, ARRAY_SIZE(devices)); | ||
117 | } | ||
118 | |||
119 | static void __init perseus2_init_smc91x(void) | ||
120 | { | ||
121 | fpga_write(1, H2P2_DBG_FPGA_LAN_RESET); | ||
122 | mdelay(50); | ||
123 | fpga_write(fpga_read(H2P2_DBG_FPGA_LAN_RESET) & ~1, | ||
124 | H2P2_DBG_FPGA_LAN_RESET); | ||
125 | mdelay(50); | ||
126 | } | ||
127 | |||
128 | void omap_perseus2_init_irq(void) | ||
129 | { | ||
130 | omap_init_irq(); | ||
131 | omap_gpio_init(); | ||
132 | perseus2_init_smc91x(); | ||
133 | } | ||
134 | |||
135 | /* Only FPGA needs to be mapped here. All others are done with ioremap */ | ||
136 | static struct map_desc omap_perseus2_io_desc[] __initdata = { | ||
137 | {H2P2_DBG_FPGA_BASE, H2P2_DBG_FPGA_START, H2P2_DBG_FPGA_SIZE, | ||
138 | MT_DEVICE}, | ||
139 | }; | ||
140 | |||
141 | static void __init omap_perseus2_map_io(void) | ||
142 | { | ||
143 | omap_map_io(); | ||
144 | iotable_init(omap_perseus2_io_desc, | ||
145 | ARRAY_SIZE(omap_perseus2_io_desc)); | ||
146 | |||
147 | /* Early, board-dependent init */ | ||
148 | |||
149 | /* | ||
150 | * Hold GSM Reset until needed | ||
151 | */ | ||
152 | omap_writew(omap_readw(OMAP730_DSP_M_CTL) & ~1, OMAP730_DSP_M_CTL); | ||
153 | |||
154 | /* | ||
155 | * UARTs -> done automagically by 8250 driver | ||
156 | */ | ||
157 | |||
158 | /* | ||
159 | * CSx timings, GPIO Mux ... setup | ||
160 | */ | ||
161 | |||
162 | /* Flash: CS0 timings setup */ | ||
163 | omap_writel(0x0000fff3, OMAP730_FLASH_CFG_0); | ||
164 | omap_writel(0x00000088, OMAP730_FLASH_ACFG_0); | ||
165 | |||
166 | /* | ||
167 | * Ethernet support trough the debug board | ||
168 | * CS1 timings setup | ||
169 | */ | ||
170 | omap_writel(0x0000fff3, OMAP730_FLASH_CFG_1); | ||
171 | omap_writel(0x00000000, OMAP730_FLASH_ACFG_1); | ||
172 | |||
173 | /* | ||
174 | * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, | ||
175 | * It is used as the Ethernet controller interrupt | ||
176 | */ | ||
177 | omap_writel(omap_readl(OMAP730_IO_CONF_9) & 0x1FFFFFFF, OMAP730_IO_CONF_9); | ||
178 | omap_serial_init(p2_serial_ports); | ||
179 | } | ||
180 | |||
181 | MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2") | ||
182 | MAINTAINER("Kevin Hilman <kjh@hilman.org>") | ||
183 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
184 | BOOT_PARAMS(0x10000100) | ||
185 | MAPIO(omap_perseus2_map_io) | ||
186 | INITIRQ(omap_perseus2_init_irq) | ||
187 | INIT_MACHINE(omap_perseus2_init) | ||
188 | .timer = &omap_timer, | ||
189 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/board-voiceblue.c b/arch/arm/mach-omap/board-voiceblue.c new file mode 100644 index 000000000000..f1a5bffac666 --- /dev/null +++ b/arch/arm/mach-omap/board-voiceblue.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/board-voiceblue.c | ||
3 | * | ||
4 | * Modified from board-generic.c | ||
5 | * | ||
6 | * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz> | ||
7 | * | ||
8 | * Code for OMAP5910 based VoiceBlue board (VoIP to GSM gateway). | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/delay.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/notifier.h> | ||
21 | #include <linux/reboot.h> | ||
22 | #include <linux/serial_8250.h> | ||
23 | #include <linux/serial_reg.h> | ||
24 | |||
25 | #include <asm/hardware.h> | ||
26 | #include <asm/mach-types.h> | ||
27 | #include <asm/mach/arch.h> | ||
28 | #include <asm/mach/map.h> | ||
29 | |||
30 | #include <asm/arch/gpio.h> | ||
31 | #include <asm/arch/tc.h> | ||
32 | #include <asm/arch/mux.h> | ||
33 | #include <asm/arch/usb.h> | ||
34 | |||
35 | #include "common.h" | ||
36 | |||
37 | extern void omap_init_time(void); | ||
38 | extern int omap_gpio_init(void); | ||
39 | |||
40 | static struct plat_serial8250_port voiceblue_ports[] = { | ||
41 | { | ||
42 | .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x40000), | ||
43 | .irq = OMAP_GPIO_IRQ(12), | ||
44 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
45 | .iotype = UPIO_MEM, | ||
46 | .regshift = 1, | ||
47 | .uartclk = 3686400, | ||
48 | }, | ||
49 | { | ||
50 | .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x50000), | ||
51 | .irq = OMAP_GPIO_IRQ(13), | ||
52 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
53 | .iotype = UPIO_MEM, | ||
54 | .regshift = 1, | ||
55 | .uartclk = 3686400, | ||
56 | }, | ||
57 | { | ||
58 | .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x60000), | ||
59 | .irq = OMAP_GPIO_IRQ(14), | ||
60 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
61 | .iotype = UPIO_MEM, | ||
62 | .regshift = 1, | ||
63 | .uartclk = 3686400, | ||
64 | }, | ||
65 | { | ||
66 | .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x70000), | ||
67 | .irq = OMAP_GPIO_IRQ(15), | ||
68 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
69 | .iotype = UPIO_MEM, | ||
70 | .regshift = 1, | ||
71 | .uartclk = 3686400, | ||
72 | }, | ||
73 | { }, | ||
74 | }; | ||
75 | |||
76 | static struct platform_device serial_device = { | ||
77 | .name = "serial8250", | ||
78 | .id = 1, | ||
79 | .dev = { | ||
80 | .platform_data = voiceblue_ports, | ||
81 | }, | ||
82 | }; | ||
83 | |||
84 | static int __init ext_uart_init(void) | ||
85 | { | ||
86 | return platform_device_register(&serial_device); | ||
87 | } | ||
88 | arch_initcall(ext_uart_init); | ||
89 | |||
90 | static struct resource voiceblue_smc91x_resources[] = { | ||
91 | [0] = { | ||
92 | .start = OMAP_CS2_PHYS + 0x300, | ||
93 | .end = OMAP_CS2_PHYS + 0x300 + 16, | ||
94 | .flags = IORESOURCE_MEM, | ||
95 | }, | ||
96 | [1] = { | ||
97 | .start = OMAP_GPIO_IRQ(8), | ||
98 | .end = OMAP_GPIO_IRQ(8), | ||
99 | .flags = IORESOURCE_IRQ, | ||
100 | }, | ||
101 | }; | ||
102 | |||
103 | static struct platform_device voiceblue_smc91x_device = { | ||
104 | .name = "smc91x", | ||
105 | .id = 0, | ||
106 | .num_resources = ARRAY_SIZE(voiceblue_smc91x_resources), | ||
107 | .resource = voiceblue_smc91x_resources, | ||
108 | }; | ||
109 | |||
110 | static struct platform_device *voiceblue_devices[] __initdata = { | ||
111 | &voiceblue_smc91x_device, | ||
112 | }; | ||
113 | |||
114 | static struct omap_usb_config voiceblue_usb_config __initdata = { | ||
115 | .hmc_mode = 3, | ||
116 | .register_host = 1, | ||
117 | .register_dev = 1, | ||
118 | .pins[0] = 2, | ||
119 | .pins[1] = 6, | ||
120 | .pins[2] = 6, | ||
121 | }; | ||
122 | |||
123 | static struct omap_board_config_kernel voiceblue_config[] = { | ||
124 | { OMAP_TAG_USB, &voiceblue_usb_config }, | ||
125 | }; | ||
126 | |||
127 | static void __init voiceblue_init_irq(void) | ||
128 | { | ||
129 | omap_init_irq(); | ||
130 | omap_gpio_init(); | ||
131 | } | ||
132 | |||
133 | static void __init voiceblue_init(void) | ||
134 | { | ||
135 | /* There is a good chance board is going up, so enable Power LED | ||
136 | * (it is connected through invertor) */ | ||
137 | omap_writeb(0x00, OMAP_LPG1_LCR); | ||
138 | /* Watchdog */ | ||
139 | omap_request_gpio(0); | ||
140 | /* smc91x reset */ | ||
141 | omap_request_gpio(7); | ||
142 | omap_set_gpio_direction(7, 0); | ||
143 | omap_set_gpio_dataout(7, 1); | ||
144 | udelay(2); /* wait at least 100ns */ | ||
145 | omap_set_gpio_dataout(7, 0); | ||
146 | mdelay(50); /* 50ms until PHY ready */ | ||
147 | /* smc91x interrupt pin */ | ||
148 | omap_request_gpio(8); | ||
149 | omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); | ||
150 | /* 16C554 reset*/ | ||
151 | omap_request_gpio(6); | ||
152 | omap_set_gpio_direction(6, 0); | ||
153 | omap_set_gpio_dataout(6, 0); | ||
154 | /* 16C554 interrupt pins */ | ||
155 | omap_request_gpio(12); | ||
156 | omap_request_gpio(13); | ||
157 | omap_request_gpio(14); | ||
158 | omap_request_gpio(15); | ||
159 | omap_set_gpio_edge_ctrl(12, OMAP_GPIO_RISING_EDGE); | ||
160 | omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); | ||
161 | omap_set_gpio_edge_ctrl(14, OMAP_GPIO_RISING_EDGE); | ||
162 | omap_set_gpio_edge_ctrl(15, OMAP_GPIO_RISING_EDGE); | ||
163 | |||
164 | platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices)); | ||
165 | omap_board_config = voiceblue_config; | ||
166 | omap_board_config_size = ARRAY_SIZE(voiceblue_config); | ||
167 | } | ||
168 | |||
169 | static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; | ||
170 | |||
171 | static void __init voiceblue_map_io(void) | ||
172 | { | ||
173 | omap_map_io(); | ||
174 | omap_serial_init(omap_serial_ports); | ||
175 | } | ||
176 | |||
177 | #define MACHINE_PANICED 1 | ||
178 | #define MACHINE_REBOOTING 2 | ||
179 | #define MACHINE_REBOOT 4 | ||
180 | static unsigned long machine_state; | ||
181 | |||
182 | static int panic_event(struct notifier_block *this, unsigned long event, | ||
183 | void *ptr) | ||
184 | { | ||
185 | if (test_and_set_bit(MACHINE_PANICED, &machine_state)) | ||
186 | return NOTIFY_DONE; | ||
187 | |||
188 | /* Flash Power LED | ||
189 | * (TODO: Enable clock right way (enabled in bootloader already)) */ | ||
190 | omap_writeb(0x78, OMAP_LPG1_LCR); | ||
191 | |||
192 | return NOTIFY_DONE; | ||
193 | } | ||
194 | |||
195 | static struct notifier_block panic_block = { | ||
196 | .notifier_call = panic_event, | ||
197 | }; | ||
198 | |||
199 | static int __init setup_notifier(void) | ||
200 | { | ||
201 | /* Setup panic notifier */ | ||
202 | notifier_chain_register(&panic_notifier_list, &panic_block); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | postcore_initcall(setup_notifier); | ||
208 | |||
209 | static int wdt_gpio_state; | ||
210 | |||
211 | void voiceblue_wdt_enable(void) | ||
212 | { | ||
213 | omap_set_gpio_direction(0, 0); | ||
214 | omap_set_gpio_dataout(0, 0); | ||
215 | omap_set_gpio_dataout(0, 1); | ||
216 | omap_set_gpio_dataout(0, 0); | ||
217 | wdt_gpio_state = 0; | ||
218 | } | ||
219 | |||
220 | void voiceblue_wdt_disable(void) | ||
221 | { | ||
222 | omap_set_gpio_dataout(0, 0); | ||
223 | omap_set_gpio_dataout(0, 1); | ||
224 | omap_set_gpio_dataout(0, 0); | ||
225 | omap_set_gpio_direction(0, 1); | ||
226 | } | ||
227 | |||
228 | void voiceblue_wdt_ping(void) | ||
229 | { | ||
230 | if (test_bit(MACHINE_REBOOT, &machine_state)) | ||
231 | return; | ||
232 | |||
233 | wdt_gpio_state = !wdt_gpio_state; | ||
234 | omap_set_gpio_dataout(0, wdt_gpio_state); | ||
235 | } | ||
236 | |||
237 | void voiceblue_reset(void) | ||
238 | { | ||
239 | set_bit(MACHINE_REBOOT, &machine_state); | ||
240 | voiceblue_wdt_enable(); | ||
241 | while (1) ; | ||
242 | } | ||
243 | |||
244 | EXPORT_SYMBOL(voiceblue_wdt_enable); | ||
245 | EXPORT_SYMBOL(voiceblue_wdt_disable); | ||
246 | EXPORT_SYMBOL(voiceblue_wdt_ping); | ||
247 | |||
248 | MACHINE_START(VOICEBLUE, "VoiceBlue OMAP5910") | ||
249 | MAINTAINER("Ladislav Michl <michl@2n.cz>") | ||
250 | BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) | ||
251 | BOOT_PARAMS(0x10000100) | ||
252 | MAPIO(voiceblue_map_io) | ||
253 | INITIRQ(voiceblue_init_irq) | ||
254 | INIT_MACHINE(voiceblue_init) | ||
255 | .timer = &omap_timer, | ||
256 | MACHINE_END | ||
diff --git a/arch/arm/mach-omap/clock.c b/arch/arm/mach-omap/clock.c new file mode 100644 index 000000000000..e91186b5341a --- /dev/null +++ b/arch/arm/mach-omap/clock.c | |||
@@ -0,0 +1,1076 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/clock.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Nokia corporation | ||
5 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/err.h> | ||
16 | |||
17 | #include <asm/semaphore.h> | ||
18 | #include <asm/hardware/clock.h> | ||
19 | #include <asm/arch/board.h> | ||
20 | #include <asm/arch/usb.h> | ||
21 | |||
22 | #include "clock.h" | ||
23 | |||
24 | static LIST_HEAD(clocks); | ||
25 | static DECLARE_MUTEX(clocks_sem); | ||
26 | static DEFINE_SPINLOCK(clockfw_lock); | ||
27 | static void propagate_rate(struct clk * clk); | ||
28 | /* External clock (MCLK & BCLK) functions */ | ||
29 | static int set_ext_clk_rate(struct clk * clk, unsigned long rate); | ||
30 | static long round_ext_clk_rate(struct clk * clk, unsigned long rate); | ||
31 | static void init_ext_clk(struct clk * clk); | ||
32 | /* MPU virtual clock functions */ | ||
33 | static int select_table_rate(struct clk * clk, unsigned long rate); | ||
34 | static long round_to_table_rate(struct clk * clk, unsigned long rate); | ||
35 | void clk_setdpll(__u16, __u16); | ||
36 | |||
37 | struct mpu_rate rate_table[] = { | ||
38 | /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL | ||
39 | * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv | ||
40 | */ | ||
41 | #if defined(CONFIG_OMAP_ARM_216MHZ) | ||
42 | { 216000000, 12000000, 216000000, 0x050d, 0x2910 }, /* 1/1/2/2/2/8 */ | ||
43 | #endif | ||
44 | #if defined(CONFIG_OMAP_ARM_195MHZ) | ||
45 | { 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */ | ||
46 | #endif | ||
47 | #if defined(CONFIG_OMAP_ARM_192MHZ) | ||
48 | { 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */ | ||
49 | { 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */ | ||
50 | { 96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */ | ||
51 | { 48000000, 12000000, 192000000, 0x0ccf, 0x2810 }, /* 4/4/4/4/8/8 */ | ||
52 | { 24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */ | ||
53 | #endif | ||
54 | #if defined(CONFIG_OMAP_ARM_182MHZ) | ||
55 | { 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */ | ||
56 | #endif | ||
57 | #if defined(CONFIG_OMAP_ARM_168MHZ) | ||
58 | { 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */ | ||
59 | #endif | ||
60 | #if defined(CONFIG_OMAP_ARM_150MHZ) | ||
61 | { 150000000, 12000000, 150000000, 0x150a, 0x2cb0 }, /* 0/0/1/1/2/2 */ | ||
62 | #endif | ||
63 | #if defined(CONFIG_OMAP_ARM_120MHZ) | ||
64 | { 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */ | ||
65 | #endif | ||
66 | #if defined(CONFIG_OMAP_ARM_96MHZ) | ||
67 | { 96000000, 12000000, 96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */ | ||
68 | #endif | ||
69 | #if defined(CONFIG_OMAP_ARM_60MHZ) | ||
70 | { 60000000, 12000000, 60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */ | ||
71 | #endif | ||
72 | #if defined(CONFIG_OMAP_ARM_30MHZ) | ||
73 | { 30000000, 12000000, 60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */ | ||
74 | #endif | ||
75 | { 0, 0, 0, 0, 0 }, | ||
76 | }; | ||
77 | |||
78 | |||
79 | static void ckctl_recalc(struct clk * clk) | ||
80 | { | ||
81 | int dsor; | ||
82 | |||
83 | /* Calculate divisor encoded as 2-bit exponent */ | ||
84 | dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); | ||
85 | if (unlikely(clk->rate == clk->parent->rate / dsor)) | ||
86 | return; /* No change, quick exit */ | ||
87 | clk->rate = clk->parent->rate / dsor; | ||
88 | |||
89 | if (unlikely(clk->flags & RATE_PROPAGATES)) | ||
90 | propagate_rate(clk); | ||
91 | } | ||
92 | |||
93 | |||
94 | static void followparent_recalc(struct clk * clk) | ||
95 | { | ||
96 | clk->rate = clk->parent->rate; | ||
97 | } | ||
98 | |||
99 | |||
100 | static void watchdog_recalc(struct clk * clk) | ||
101 | { | ||
102 | clk->rate = clk->parent->rate / 14; | ||
103 | } | ||
104 | |||
105 | |||
106 | static struct clk ck_ref = { | ||
107 | .name = "ck_ref", | ||
108 | .rate = 12000000, | ||
109 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
110 | ALWAYS_ENABLED, | ||
111 | }; | ||
112 | |||
113 | static struct clk ck_dpll1 = { | ||
114 | .name = "ck_dpll1", | ||
115 | .parent = &ck_ref, | ||
116 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
117 | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
118 | }; | ||
119 | |||
120 | static struct clk ck_dpll1out = { | ||
121 | .name = "ck_dpll1out", | ||
122 | .parent = &ck_dpll1, | ||
123 | .flags = CLOCK_IN_OMAP16XX, | ||
124 | .enable_reg = ARM_IDLECT2, | ||
125 | .enable_bit = EN_CKOUT_ARM, | ||
126 | .recalc = &followparent_recalc, | ||
127 | }; | ||
128 | |||
129 | static struct clk arm_ck = { | ||
130 | .name = "arm_ck", | ||
131 | .parent = &ck_dpll1, | ||
132 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
133 | RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
134 | .rate_offset = CKCTL_ARMDIV_OFFSET, | ||
135 | .recalc = &ckctl_recalc, | ||
136 | }; | ||
137 | |||
138 | static struct clk armper_ck = { | ||
139 | .name = "armper_ck", | ||
140 | .parent = &ck_dpll1, | ||
141 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
142 | RATE_CKCTL, | ||
143 | .enable_reg = ARM_IDLECT2, | ||
144 | .enable_bit = EN_PERCK, | ||
145 | .rate_offset = CKCTL_PERDIV_OFFSET, | ||
146 | .recalc = &ckctl_recalc, | ||
147 | }; | ||
148 | |||
149 | static struct clk arm_gpio_ck = { | ||
150 | .name = "arm_gpio_ck", | ||
151 | .parent = &ck_dpll1, | ||
152 | .flags = CLOCK_IN_OMAP1510, | ||
153 | .enable_reg = ARM_IDLECT2, | ||
154 | .enable_bit = EN_GPIOCK, | ||
155 | .recalc = &followparent_recalc, | ||
156 | }; | ||
157 | |||
158 | static struct clk armxor_ck = { | ||
159 | .name = "armxor_ck", | ||
160 | .parent = &ck_ref, | ||
161 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
162 | .enable_reg = ARM_IDLECT2, | ||
163 | .enable_bit = EN_XORPCK, | ||
164 | .recalc = &followparent_recalc, | ||
165 | }; | ||
166 | |||
167 | static struct clk armtim_ck = { | ||
168 | .name = "armtim_ck", | ||
169 | .parent = &ck_ref, | ||
170 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
171 | .enable_reg = ARM_IDLECT2, | ||
172 | .enable_bit = EN_TIMCK, | ||
173 | .recalc = &followparent_recalc, | ||
174 | }; | ||
175 | |||
176 | static struct clk armwdt_ck = { | ||
177 | .name = "armwdt_ck", | ||
178 | .parent = &ck_ref, | ||
179 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
180 | .enable_reg = ARM_IDLECT2, | ||
181 | .enable_bit = EN_WDTCK, | ||
182 | .recalc = &watchdog_recalc, | ||
183 | }; | ||
184 | |||
185 | static struct clk arminth_ck16xx = { | ||
186 | .name = "arminth_ck", | ||
187 | .parent = &arm_ck, | ||
188 | .flags = CLOCK_IN_OMAP16XX, | ||
189 | .recalc = &followparent_recalc, | ||
190 | /* Note: On 16xx the frequency can be divided by 2 by programming | ||
191 | * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 | ||
192 | * | ||
193 | * 1510 version is in TC clocks. | ||
194 | */ | ||
195 | }; | ||
196 | |||
197 | static struct clk dsp_ck = { | ||
198 | .name = "dsp_ck", | ||
199 | .parent = &ck_dpll1, | ||
200 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
201 | RATE_CKCTL, | ||
202 | .enable_reg = ARM_CKCTL, | ||
203 | .enable_bit = EN_DSPCK, | ||
204 | .rate_offset = CKCTL_DSPDIV_OFFSET, | ||
205 | .recalc = &ckctl_recalc, | ||
206 | }; | ||
207 | |||
208 | static struct clk dspmmu_ck = { | ||
209 | .name = "dspmmu_ck", | ||
210 | .parent = &ck_dpll1, | ||
211 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
212 | RATE_CKCTL | ALWAYS_ENABLED, | ||
213 | .rate_offset = CKCTL_DSPMMUDIV_OFFSET, | ||
214 | .recalc = &ckctl_recalc, | ||
215 | }; | ||
216 | |||
217 | static struct clk tc_ck = { | ||
218 | .name = "tc_ck", | ||
219 | .parent = &ck_dpll1, | ||
220 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | | ||
221 | RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
222 | .rate_offset = CKCTL_TCDIV_OFFSET, | ||
223 | .recalc = &ckctl_recalc, | ||
224 | }; | ||
225 | |||
226 | static struct clk arminth_ck1510 = { | ||
227 | .name = "arminth_ck", | ||
228 | .parent = &tc_ck, | ||
229 | .flags = CLOCK_IN_OMAP1510, | ||
230 | .recalc = &followparent_recalc, | ||
231 | /* Note: On 1510 the frequency follows TC_CK | ||
232 | * | ||
233 | * 16xx version is in MPU clocks. | ||
234 | */ | ||
235 | }; | ||
236 | |||
237 | static struct clk tipb_ck = { | ||
238 | .name = "tibp_ck", | ||
239 | .parent = &tc_ck, | ||
240 | .flags = CLOCK_IN_OMAP1510, | ||
241 | .recalc = &followparent_recalc, | ||
242 | }; | ||
243 | |||
244 | static struct clk l3_ocpi_ck = { | ||
245 | .name = "l3_ocpi_ck", | ||
246 | .parent = &tc_ck, | ||
247 | .flags = CLOCK_IN_OMAP16XX, | ||
248 | .enable_reg = ARM_IDLECT3, | ||
249 | .enable_bit = EN_OCPI_CK, | ||
250 | .recalc = &followparent_recalc, | ||
251 | }; | ||
252 | |||
253 | static struct clk tc1_ck = { | ||
254 | .name = "tc1_ck", | ||
255 | .parent = &tc_ck, | ||
256 | .flags = CLOCK_IN_OMAP16XX, | ||
257 | .enable_reg = ARM_IDLECT3, | ||
258 | .enable_bit = EN_TC1_CK, | ||
259 | .recalc = &followparent_recalc, | ||
260 | }; | ||
261 | |||
262 | static struct clk tc2_ck = { | ||
263 | .name = "tc2_ck", | ||
264 | .parent = &tc_ck, | ||
265 | .flags = CLOCK_IN_OMAP16XX, | ||
266 | .enable_reg = ARM_IDLECT3, | ||
267 | .enable_bit = EN_TC2_CK, | ||
268 | .recalc = &followparent_recalc, | ||
269 | }; | ||
270 | |||
271 | static struct clk dma_ck = { | ||
272 | .name = "dma_ck", | ||
273 | .parent = &tc_ck, | ||
274 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
275 | .recalc = &followparent_recalc, | ||
276 | }; | ||
277 | |||
278 | static struct clk dma_lcdfree_ck = { | ||
279 | .name = "dma_lcdfree_ck", | ||
280 | .parent = &tc_ck, | ||
281 | .flags = CLOCK_IN_OMAP16XX, | ||
282 | .recalc = &followparent_recalc, | ||
283 | }; | ||
284 | |||
285 | static struct clk api_ck = { | ||
286 | .name = "api_ck", | ||
287 | .parent = &tc_ck, | ||
288 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
289 | .enable_reg = ARM_IDLECT2, | ||
290 | .enable_bit = EN_APICK, | ||
291 | .recalc = &followparent_recalc, | ||
292 | }; | ||
293 | |||
294 | static struct clk lb_ck = { | ||
295 | .name = "lb_ck", | ||
296 | .parent = &tc_ck, | ||
297 | .flags = CLOCK_IN_OMAP1510, | ||
298 | .enable_reg = ARM_IDLECT2, | ||
299 | .enable_bit = EN_LBCK, | ||
300 | .recalc = &followparent_recalc, | ||
301 | }; | ||
302 | |||
303 | static struct clk rhea1_ck = { | ||
304 | .name = "rhea1_ck", | ||
305 | .parent = &tc_ck, | ||
306 | .flags = CLOCK_IN_OMAP16XX, | ||
307 | .recalc = &followparent_recalc, | ||
308 | }; | ||
309 | |||
310 | static struct clk rhea2_ck = { | ||
311 | .name = "rhea2_ck", | ||
312 | .parent = &tc_ck, | ||
313 | .flags = CLOCK_IN_OMAP16XX, | ||
314 | .recalc = &followparent_recalc, | ||
315 | }; | ||
316 | |||
317 | static struct clk lcd_ck = { | ||
318 | .name = "lcd_ck", | ||
319 | .parent = &ck_dpll1, | ||
320 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | | ||
321 | RATE_CKCTL, | ||
322 | .enable_reg = ARM_IDLECT2, | ||
323 | .enable_bit = EN_LCDCK, | ||
324 | .rate_offset = CKCTL_LCDDIV_OFFSET, | ||
325 | .recalc = &ckctl_recalc, | ||
326 | }; | ||
327 | |||
328 | static struct clk uart1_ck = { | ||
329 | .name = "uart1_ck", | ||
330 | /* Direct from ULPD, no parent */ | ||
331 | .rate = 48000000, | ||
332 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
333 | RATE_FIXED | ENABLE_REG_32BIT, | ||
334 | .enable_reg = MOD_CONF_CTRL_0, | ||
335 | .enable_bit = 29, | ||
336 | /* (Only on 1510) | ||
337 | * The "enable bit" actually chooses between 48MHz and 12MHz. | ||
338 | */ | ||
339 | }; | ||
340 | |||
341 | static struct clk uart2_ck = { | ||
342 | .name = "uart2_ck", | ||
343 | /* Direct from ULPD, no parent */ | ||
344 | .rate = 48000000, | ||
345 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
346 | RATE_FIXED | ENABLE_REG_32BIT, | ||
347 | .enable_reg = MOD_CONF_CTRL_0, | ||
348 | .enable_bit = 30, | ||
349 | /* (for both 1510 and 16xx) | ||
350 | * The "enable bit" actually chooses between 48MHz and 12MHz/32kHz. | ||
351 | */ | ||
352 | }; | ||
353 | |||
354 | static struct clk uart3_ck = { | ||
355 | .name = "uart3_ck", | ||
356 | /* Direct from ULPD, no parent */ | ||
357 | .rate = 48000000, | ||
358 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
359 | RATE_FIXED | ENABLE_REG_32BIT, | ||
360 | .enable_reg = MOD_CONF_CTRL_0, | ||
361 | .enable_bit = 31, | ||
362 | /* (Only on 1510) | ||
363 | * The "enable bit" actually chooses between 48MHz and 12MHz. | ||
364 | */ | ||
365 | }; | ||
366 | |||
367 | static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ | ||
368 | .name = "usb_clko", | ||
369 | /* Direct from ULPD, no parent */ | ||
370 | .rate = 6000000, | ||
371 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
372 | RATE_FIXED | ENABLE_REG_32BIT, | ||
373 | .enable_reg = ULPD_CLOCK_CTRL, | ||
374 | .enable_bit = USB_MCLK_EN_BIT, | ||
375 | }; | ||
376 | |||
377 | static struct clk usb_hhc_ck1510 = { | ||
378 | .name = "usb_hhc_ck", | ||
379 | /* Direct from ULPD, no parent */ | ||
380 | .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ | ||
381 | .flags = CLOCK_IN_OMAP1510 | | ||
382 | RATE_FIXED | ENABLE_REG_32BIT, | ||
383 | .enable_reg = MOD_CONF_CTRL_0, | ||
384 | .enable_bit = USB_HOST_HHC_UHOST_EN, | ||
385 | }; | ||
386 | |||
387 | static struct clk usb_hhc_ck16xx = { | ||
388 | .name = "usb_hhc_ck", | ||
389 | /* Direct from ULPD, no parent */ | ||
390 | .rate = 48000000, | ||
391 | /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ | ||
392 | .flags = CLOCK_IN_OMAP16XX | | ||
393 | RATE_FIXED | ENABLE_REG_32BIT, | ||
394 | .enable_reg = OTG_BASE + 0x08 /* OTG_SYSCON_2 */, | ||
395 | .enable_bit = 8 /* UHOST_EN */, | ||
396 | }; | ||
397 | |||
398 | static struct clk mclk_1510 = { | ||
399 | .name = "mclk", | ||
400 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
401 | .rate = 12000000, | ||
402 | .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, | ||
403 | }; | ||
404 | |||
405 | static struct clk mclk_16xx = { | ||
406 | .name = "mclk", | ||
407 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
408 | .flags = CLOCK_IN_OMAP16XX, | ||
409 | .enable_reg = COM_CLK_DIV_CTRL_SEL, | ||
410 | .enable_bit = COM_ULPD_PLL_CLK_REQ, | ||
411 | .set_rate = &set_ext_clk_rate, | ||
412 | .round_rate = &round_ext_clk_rate, | ||
413 | .init = &init_ext_clk, | ||
414 | }; | ||
415 | |||
416 | static struct clk bclk_1510 = { | ||
417 | .name = "bclk", | ||
418 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
419 | .rate = 12000000, | ||
420 | .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, | ||
421 | }; | ||
422 | |||
423 | static struct clk bclk_16xx = { | ||
424 | .name = "bclk", | ||
425 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
426 | .flags = CLOCK_IN_OMAP16XX, | ||
427 | .enable_reg = SWD_CLK_DIV_CTRL_SEL, | ||
428 | .enable_bit = SWD_ULPD_PLL_CLK_REQ, | ||
429 | .set_rate = &set_ext_clk_rate, | ||
430 | .round_rate = &round_ext_clk_rate, | ||
431 | .init = &init_ext_clk, | ||
432 | }; | ||
433 | |||
434 | static struct clk mmc1_ck = { | ||
435 | .name = "mmc1_ck", | ||
436 | /* Functional clock is direct from ULPD, interface clock is ARMPER */ | ||
437 | .parent = &armper_ck, | ||
438 | .rate = 48000000, | ||
439 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
440 | RATE_FIXED | ENABLE_REG_32BIT, | ||
441 | .enable_reg = MOD_CONF_CTRL_0, | ||
442 | .enable_bit = 23, | ||
443 | }; | ||
444 | |||
445 | static struct clk mmc2_ck = { | ||
446 | .name = "mmc2_ck", | ||
447 | /* Functional clock is direct from ULPD, interface clock is ARMPER */ | ||
448 | .parent = &armper_ck, | ||
449 | .rate = 48000000, | ||
450 | .flags = CLOCK_IN_OMAP16XX | | ||
451 | RATE_FIXED | ENABLE_REG_32BIT, | ||
452 | .enable_reg = MOD_CONF_CTRL_0, | ||
453 | .enable_bit = 20, | ||
454 | }; | ||
455 | |||
456 | static struct clk virtual_ck_mpu = { | ||
457 | .name = "mpu", | ||
458 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
459 | VIRTUAL_CLOCK | ALWAYS_ENABLED, | ||
460 | .parent = &arm_ck, /* Is smarter alias for */ | ||
461 | .recalc = &followparent_recalc, | ||
462 | .set_rate = &select_table_rate, | ||
463 | .round_rate = &round_to_table_rate, | ||
464 | }; | ||
465 | |||
466 | |||
467 | static struct clk * onchip_clks[] = { | ||
468 | /* non-ULPD clocks */ | ||
469 | &ck_ref, | ||
470 | &ck_dpll1, | ||
471 | /* CK_GEN1 clocks */ | ||
472 | &ck_dpll1out, | ||
473 | &arm_ck, | ||
474 | &armper_ck, | ||
475 | &arm_gpio_ck, | ||
476 | &armxor_ck, | ||
477 | &armtim_ck, | ||
478 | &armwdt_ck, | ||
479 | &arminth_ck1510, &arminth_ck16xx, | ||
480 | /* CK_GEN2 clocks */ | ||
481 | &dsp_ck, | ||
482 | &dspmmu_ck, | ||
483 | /* CK_GEN3 clocks */ | ||
484 | &tc_ck, | ||
485 | &tipb_ck, | ||
486 | &l3_ocpi_ck, | ||
487 | &tc1_ck, | ||
488 | &tc2_ck, | ||
489 | &dma_ck, | ||
490 | &dma_lcdfree_ck, | ||
491 | &api_ck, | ||
492 | &lb_ck, | ||
493 | &rhea1_ck, | ||
494 | &rhea2_ck, | ||
495 | &lcd_ck, | ||
496 | /* ULPD clocks */ | ||
497 | &uart1_ck, | ||
498 | &uart2_ck, | ||
499 | &uart3_ck, | ||
500 | &usb_clko, | ||
501 | &usb_hhc_ck1510, &usb_hhc_ck16xx, | ||
502 | &mclk_1510, &mclk_16xx, | ||
503 | &bclk_1510, &bclk_16xx, | ||
504 | &mmc1_ck, | ||
505 | &mmc2_ck, | ||
506 | /* Virtual clocks */ | ||
507 | &virtual_ck_mpu, | ||
508 | }; | ||
509 | |||
510 | struct clk *clk_get(struct device *dev, const char *id) | ||
511 | { | ||
512 | struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
513 | |||
514 | down(&clocks_sem); | ||
515 | list_for_each_entry(p, &clocks, node) { | ||
516 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
517 | clk = p; | ||
518 | break; | ||
519 | } | ||
520 | } | ||
521 | up(&clocks_sem); | ||
522 | |||
523 | return clk; | ||
524 | } | ||
525 | EXPORT_SYMBOL(clk_get); | ||
526 | |||
527 | |||
528 | void clk_put(struct clk *clk) | ||
529 | { | ||
530 | if (clk && !IS_ERR(clk)) | ||
531 | module_put(clk->owner); | ||
532 | } | ||
533 | EXPORT_SYMBOL(clk_put); | ||
534 | |||
535 | |||
536 | int __clk_enable(struct clk *clk) | ||
537 | { | ||
538 | __u16 regval16; | ||
539 | __u32 regval32; | ||
540 | |||
541 | if (clk->flags & ALWAYS_ENABLED) | ||
542 | return 0; | ||
543 | |||
544 | if (unlikely(clk->enable_reg == 0)) { | ||
545 | printk(KERN_ERR "clock.c: Enable for %s without enable code\n", | ||
546 | clk->name); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | if (clk->flags & ENABLE_REG_32BIT) { | ||
551 | regval32 = omap_readl(clk->enable_reg); | ||
552 | regval32 |= (1 << clk->enable_bit); | ||
553 | omap_writel(regval32, clk->enable_reg); | ||
554 | } else { | ||
555 | regval16 = omap_readw(clk->enable_reg); | ||
556 | regval16 |= (1 << clk->enable_bit); | ||
557 | omap_writew(regval16, clk->enable_reg); | ||
558 | } | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | |||
564 | void __clk_disable(struct clk *clk) | ||
565 | { | ||
566 | __u16 regval16; | ||
567 | __u32 regval32; | ||
568 | |||
569 | if (clk->enable_reg == 0) | ||
570 | return; | ||
571 | |||
572 | if (clk->flags & ENABLE_REG_32BIT) { | ||
573 | regval32 = omap_readl(clk->enable_reg); | ||
574 | regval32 &= ~(1 << clk->enable_bit); | ||
575 | omap_writel(regval32, clk->enable_reg); | ||
576 | } else { | ||
577 | regval16 = omap_readw(clk->enable_reg); | ||
578 | regval16 &= ~(1 << clk->enable_bit); | ||
579 | omap_writew(regval16, clk->enable_reg); | ||
580 | } | ||
581 | } | ||
582 | |||
583 | |||
584 | void __clk_unuse(struct clk *clk) | ||
585 | { | ||
586 | if (clk->usecount > 0 && !(--clk->usecount)) { | ||
587 | __clk_disable(clk); | ||
588 | if (likely(clk->parent)) | ||
589 | __clk_unuse(clk->parent); | ||
590 | } | ||
591 | } | ||
592 | |||
593 | |||
594 | int __clk_use(struct clk *clk) | ||
595 | { | ||
596 | int ret = 0; | ||
597 | if (clk->usecount++ == 0) { | ||
598 | if (likely(clk->parent)) | ||
599 | ret = __clk_use(clk->parent); | ||
600 | |||
601 | if (unlikely(ret != 0)) { | ||
602 | clk->usecount--; | ||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | ret = __clk_enable(clk); | ||
607 | |||
608 | if (unlikely(ret != 0) && clk->parent) { | ||
609 | __clk_unuse(clk->parent); | ||
610 | clk->usecount--; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | return ret; | ||
615 | } | ||
616 | |||
617 | |||
618 | int clk_enable(struct clk *clk) | ||
619 | { | ||
620 | unsigned long flags; | ||
621 | int ret; | ||
622 | |||
623 | spin_lock_irqsave(&clockfw_lock, flags); | ||
624 | ret = __clk_enable(clk); | ||
625 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
626 | return ret; | ||
627 | } | ||
628 | EXPORT_SYMBOL(clk_enable); | ||
629 | |||
630 | |||
631 | void clk_disable(struct clk *clk) | ||
632 | { | ||
633 | unsigned long flags; | ||
634 | |||
635 | spin_lock_irqsave(&clockfw_lock, flags); | ||
636 | __clk_disable(clk); | ||
637 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
638 | } | ||
639 | EXPORT_SYMBOL(clk_disable); | ||
640 | |||
641 | |||
642 | int clk_use(struct clk *clk) | ||
643 | { | ||
644 | unsigned long flags; | ||
645 | int ret = 0; | ||
646 | |||
647 | spin_lock_irqsave(&clockfw_lock, flags); | ||
648 | ret = __clk_use(clk); | ||
649 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
650 | return ret; | ||
651 | } | ||
652 | EXPORT_SYMBOL(clk_use); | ||
653 | |||
654 | |||
655 | void clk_unuse(struct clk *clk) | ||
656 | { | ||
657 | unsigned long flags; | ||
658 | |||
659 | spin_lock_irqsave(&clockfw_lock, flags); | ||
660 | __clk_unuse(clk); | ||
661 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
662 | } | ||
663 | EXPORT_SYMBOL(clk_unuse); | ||
664 | |||
665 | |||
666 | int clk_get_usecount(struct clk *clk) | ||
667 | { | ||
668 | return clk->usecount; | ||
669 | } | ||
670 | EXPORT_SYMBOL(clk_get_usecount); | ||
671 | |||
672 | |||
673 | unsigned long clk_get_rate(struct clk *clk) | ||
674 | { | ||
675 | return clk->rate; | ||
676 | } | ||
677 | EXPORT_SYMBOL(clk_get_rate); | ||
678 | |||
679 | |||
680 | static __u16 verify_ckctl_value(__u16 newval) | ||
681 | { | ||
682 | /* This function checks for following limitations set | ||
683 | * by the hardware (all conditions must be true): | ||
684 | * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 | ||
685 | * ARM_CK >= TC_CK | ||
686 | * DSP_CK >= TC_CK | ||
687 | * DSPMMU_CK >= TC_CK | ||
688 | * | ||
689 | * In addition following rules are enforced: | ||
690 | * LCD_CK <= TC_CK | ||
691 | * ARMPER_CK <= TC_CK | ||
692 | * | ||
693 | * However, maximum frequencies are not checked for! | ||
694 | */ | ||
695 | __u8 per_exp; | ||
696 | __u8 lcd_exp; | ||
697 | __u8 arm_exp; | ||
698 | __u8 dsp_exp; | ||
699 | __u8 tc_exp; | ||
700 | __u8 dspmmu_exp; | ||
701 | |||
702 | per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; | ||
703 | lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; | ||
704 | arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; | ||
705 | dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; | ||
706 | tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; | ||
707 | dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; | ||
708 | |||
709 | if (dspmmu_exp < dsp_exp) | ||
710 | dspmmu_exp = dsp_exp; | ||
711 | if (dspmmu_exp > dsp_exp+1) | ||
712 | dspmmu_exp = dsp_exp+1; | ||
713 | if (tc_exp < arm_exp) | ||
714 | tc_exp = arm_exp; | ||
715 | if (tc_exp < dspmmu_exp) | ||
716 | tc_exp = dspmmu_exp; | ||
717 | if (tc_exp > lcd_exp) | ||
718 | lcd_exp = tc_exp; | ||
719 | if (tc_exp > per_exp) | ||
720 | per_exp = tc_exp; | ||
721 | |||
722 | newval &= 0xf000; | ||
723 | newval |= per_exp << CKCTL_PERDIV_OFFSET; | ||
724 | newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; | ||
725 | newval |= arm_exp << CKCTL_ARMDIV_OFFSET; | ||
726 | newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; | ||
727 | newval |= tc_exp << CKCTL_TCDIV_OFFSET; | ||
728 | newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; | ||
729 | |||
730 | return newval; | ||
731 | } | ||
732 | |||
733 | |||
734 | static int calc_dsor_exp(struct clk *clk, unsigned long rate) | ||
735 | { | ||
736 | /* Note: If target frequency is too low, this function will return 4, | ||
737 | * which is invalid value. Caller must check for this value and act | ||
738 | * accordingly. | ||
739 | * | ||
740 | * Note: This function does not check for following limitations set | ||
741 | * by the hardware (all conditions must be true): | ||
742 | * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 | ||
743 | * ARM_CK >= TC_CK | ||
744 | * DSP_CK >= TC_CK | ||
745 | * DSPMMU_CK >= TC_CK | ||
746 | */ | ||
747 | unsigned long realrate; | ||
748 | struct clk * parent; | ||
749 | unsigned dsor_exp; | ||
750 | |||
751 | if (unlikely(!(clk->flags & RATE_CKCTL))) | ||
752 | return -EINVAL; | ||
753 | |||
754 | parent = clk->parent; | ||
755 | if (unlikely(parent == 0)) | ||
756 | return -EIO; | ||
757 | |||
758 | realrate = parent->rate; | ||
759 | for (dsor_exp=0; dsor_exp<4; dsor_exp++) { | ||
760 | if (realrate <= rate) | ||
761 | break; | ||
762 | |||
763 | realrate /= 2; | ||
764 | } | ||
765 | |||
766 | return dsor_exp; | ||
767 | } | ||
768 | |||
769 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
770 | { | ||
771 | int dsor_exp; | ||
772 | |||
773 | if (clk->flags & RATE_FIXED) | ||
774 | return clk->rate; | ||
775 | |||
776 | if (clk->flags & RATE_CKCTL) { | ||
777 | dsor_exp = calc_dsor_exp(clk, rate); | ||
778 | if (dsor_exp < 0) | ||
779 | return dsor_exp; | ||
780 | if (dsor_exp > 3) | ||
781 | dsor_exp = 3; | ||
782 | return clk->parent->rate / (1 << dsor_exp); | ||
783 | } | ||
784 | |||
785 | if(clk->round_rate != 0) | ||
786 | return clk->round_rate(clk, rate); | ||
787 | |||
788 | return clk->rate; | ||
789 | } | ||
790 | EXPORT_SYMBOL(clk_round_rate); | ||
791 | |||
792 | |||
793 | static void propagate_rate(struct clk * clk) | ||
794 | { | ||
795 | struct clk ** clkp; | ||
796 | |||
797 | for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { | ||
798 | if (likely((*clkp)->parent != clk)) continue; | ||
799 | if (likely((*clkp)->recalc)) | ||
800 | (*clkp)->recalc(*clkp); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | |||
805 | static int select_table_rate(struct clk * clk, unsigned long rate) | ||
806 | { | ||
807 | /* Find the highest supported frequency <= rate and switch to it */ | ||
808 | struct mpu_rate * ptr; | ||
809 | |||
810 | if (clk != &virtual_ck_mpu) | ||
811 | return -EINVAL; | ||
812 | |||
813 | for (ptr = rate_table; ptr->rate; ptr++) { | ||
814 | if (ptr->xtal != ck_ref.rate) | ||
815 | continue; | ||
816 | |||
817 | /* DPLL1 cannot be reprogrammed without risking system crash */ | ||
818 | if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate) | ||
819 | continue; | ||
820 | |||
821 | /* Can check only after xtal frequency check */ | ||
822 | if (ptr->rate <= rate) | ||
823 | break; | ||
824 | } | ||
825 | |||
826 | if (!ptr->rate) | ||
827 | return -EINVAL; | ||
828 | |||
829 | if (unlikely(ck_dpll1.rate == 0)) { | ||
830 | omap_writew(ptr->dpllctl_val, DPLL_CTL); | ||
831 | ck_dpll1.rate = ptr->pll_rate; | ||
832 | } | ||
833 | omap_writew(ptr->ckctl_val, ARM_CKCTL); | ||
834 | propagate_rate(&ck_dpll1); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | |||
839 | static long round_to_table_rate(struct clk * clk, unsigned long rate) | ||
840 | { | ||
841 | /* Find the highest supported frequency <= rate */ | ||
842 | struct mpu_rate * ptr; | ||
843 | long highest_rate; | ||
844 | |||
845 | if (clk != &virtual_ck_mpu) | ||
846 | return -EINVAL; | ||
847 | |||
848 | highest_rate = -EINVAL; | ||
849 | |||
850 | for (ptr = rate_table; ptr->rate; ptr++) { | ||
851 | if (ptr->xtal != ck_ref.rate) | ||
852 | continue; | ||
853 | |||
854 | highest_rate = ptr->rate; | ||
855 | |||
856 | /* Can check only after xtal frequency check */ | ||
857 | if (ptr->rate <= rate) | ||
858 | break; | ||
859 | } | ||
860 | |||
861 | return highest_rate; | ||
862 | } | ||
863 | |||
864 | |||
865 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
866 | { | ||
867 | int ret = -EINVAL; | ||
868 | int dsor_exp; | ||
869 | __u16 regval; | ||
870 | unsigned long flags; | ||
871 | |||
872 | if (clk->flags & RATE_CKCTL) { | ||
873 | dsor_exp = calc_dsor_exp(clk, rate); | ||
874 | if (dsor_exp > 3) | ||
875 | dsor_exp = -EINVAL; | ||
876 | if (dsor_exp < 0) | ||
877 | return dsor_exp; | ||
878 | |||
879 | spin_lock_irqsave(&clockfw_lock, flags); | ||
880 | regval = omap_readw(ARM_CKCTL); | ||
881 | regval &= ~(3 << clk->rate_offset); | ||
882 | regval |= dsor_exp << clk->rate_offset; | ||
883 | regval = verify_ckctl_value(regval); | ||
884 | omap_writew(regval, ARM_CKCTL); | ||
885 | clk->rate = clk->parent->rate / (1 << dsor_exp); | ||
886 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
887 | ret = 0; | ||
888 | } else if(clk->set_rate != 0) { | ||
889 | spin_lock_irqsave(&clockfw_lock, flags); | ||
890 | ret = clk->set_rate(clk, rate); | ||
891 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
892 | } | ||
893 | |||
894 | if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) | ||
895 | propagate_rate(clk); | ||
896 | |||
897 | return ret; | ||
898 | } | ||
899 | EXPORT_SYMBOL(clk_set_rate); | ||
900 | |||
901 | |||
902 | static unsigned calc_ext_dsor(unsigned long rate) | ||
903 | { | ||
904 | unsigned dsor; | ||
905 | |||
906 | /* MCLK and BCLK divisor selection is not linear: | ||
907 | * freq = 96MHz / dsor | ||
908 | * | ||
909 | * RATIO_SEL range: dsor <-> RATIO_SEL | ||
910 | * 0..6: (RATIO_SEL+2) <-> (dsor-2) | ||
911 | * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) | ||
912 | * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 | ||
913 | * can not be used. | ||
914 | */ | ||
915 | for (dsor = 2; dsor < 96; ++dsor) { | ||
916 | if ((dsor & 1) && dsor > 8) | ||
917 | continue; | ||
918 | if (rate >= 96000000 / dsor) | ||
919 | break; | ||
920 | } | ||
921 | return dsor; | ||
922 | } | ||
923 | |||
924 | |||
925 | static int set_ext_clk_rate(struct clk * clk, unsigned long rate) | ||
926 | { | ||
927 | unsigned dsor; | ||
928 | __u16 ratio_bits; | ||
929 | |||
930 | dsor = calc_ext_dsor(rate); | ||
931 | clk->rate = 96000000 / dsor; | ||
932 | if (dsor > 8) | ||
933 | ratio_bits = ((dsor - 8) / 2 + 6) << 2; | ||
934 | else | ||
935 | ratio_bits = (dsor - 2) << 2; | ||
936 | |||
937 | ratio_bits |= omap_readw(clk->enable_reg) & ~0xfd; | ||
938 | omap_writew(ratio_bits, clk->enable_reg); | ||
939 | |||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | |||
944 | static long round_ext_clk_rate(struct clk * clk, unsigned long rate) | ||
945 | { | ||
946 | return 96000000 / calc_ext_dsor(rate); | ||
947 | } | ||
948 | |||
949 | |||
950 | static void init_ext_clk(struct clk * clk) | ||
951 | { | ||
952 | unsigned dsor; | ||
953 | __u16 ratio_bits; | ||
954 | |||
955 | /* Determine current rate and ensure clock is based on 96MHz APLL */ | ||
956 | ratio_bits = omap_readw(clk->enable_reg) & ~1; | ||
957 | omap_writew(ratio_bits, clk->enable_reg); | ||
958 | |||
959 | ratio_bits = (ratio_bits & 0xfc) >> 2; | ||
960 | if (ratio_bits > 6) | ||
961 | dsor = (ratio_bits - 6) * 2 + 8; | ||
962 | else | ||
963 | dsor = ratio_bits + 2; | ||
964 | |||
965 | clk-> rate = 96000000 / dsor; | ||
966 | } | ||
967 | |||
968 | |||
969 | int clk_register(struct clk *clk) | ||
970 | { | ||
971 | down(&clocks_sem); | ||
972 | list_add(&clk->node, &clocks); | ||
973 | if (clk->init) | ||
974 | clk->init(clk); | ||
975 | up(&clocks_sem); | ||
976 | return 0; | ||
977 | } | ||
978 | EXPORT_SYMBOL(clk_register); | ||
979 | |||
980 | void clk_unregister(struct clk *clk) | ||
981 | { | ||
982 | down(&clocks_sem); | ||
983 | list_del(&clk->node); | ||
984 | up(&clocks_sem); | ||
985 | } | ||
986 | EXPORT_SYMBOL(clk_unregister); | ||
987 | |||
988 | |||
989 | |||
990 | int __init clk_init(void) | ||
991 | { | ||
992 | struct clk ** clkp; | ||
993 | const struct omap_clock_config *info; | ||
994 | int crystal_type = 0; /* Default 12 MHz */ | ||
995 | |||
996 | for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { | ||
997 | if (((*clkp)->flags &CLOCK_IN_OMAP1510) && cpu_is_omap1510()) { | ||
998 | clk_register(*clkp); | ||
999 | continue; | ||
1000 | } | ||
1001 | |||
1002 | if (((*clkp)->flags &CLOCK_IN_OMAP16XX) && cpu_is_omap16xx()) { | ||
1003 | clk_register(*clkp); | ||
1004 | continue; | ||
1005 | } | ||
1006 | |||
1007 | if (((*clkp)->flags &CLOCK_IN_OMAP730) && cpu_is_omap730()) { | ||
1008 | clk_register(*clkp); | ||
1009 | continue; | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); | ||
1014 | if (info != NULL) { | ||
1015 | if (!cpu_is_omap1510()) | ||
1016 | crystal_type = info->system_clock_type; | ||
1017 | } | ||
1018 | |||
1019 | #if defined(CONFIG_ARCH_OMAP730) | ||
1020 | ck_ref.rate = 13000000; | ||
1021 | #elif defined(CONFIG_ARCH_OMAP16XX) | ||
1022 | if (crystal_type == 2) | ||
1023 | ck_ref.rate = 19200000; | ||
1024 | #endif | ||
1025 | |||
1026 | /* We want to be in syncronous scalable mode */ | ||
1027 | omap_writew(0x1000, ARM_SYSST); | ||
1028 | |||
1029 | /* Find the highest supported frequency and enable it */ | ||
1030 | if (select_table_rate(&virtual_ck_mpu, ~0)) { | ||
1031 | printk(KERN_ERR "System frequencies not set. Check your config.\n"); | ||
1032 | /* Guess sane values (60MHz) */ | ||
1033 | omap_writew(0x2290, DPLL_CTL); | ||
1034 | omap_writew(0x1005, ARM_CKCTL); | ||
1035 | ck_dpll1.rate = 60000000; | ||
1036 | propagate_rate(&ck_dpll1); | ||
1037 | printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld/%ld/%ld\n", | ||
1038 | ck_ref.rate, ck_dpll1.rate, arm_ck.rate); | ||
1039 | } | ||
1040 | |||
1041 | /* Cache rates for clocks connected to ck_ref (not dpll1) */ | ||
1042 | propagate_rate(&ck_ref); | ||
1043 | |||
1044 | #ifdef CONFIG_MACH_OMAP_PERSEUS2 | ||
1045 | /* Select slicer output as OMAP input clock */ | ||
1046 | omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL); | ||
1047 | #endif | ||
1048 | |||
1049 | /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */ | ||
1050 | omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); | ||
1051 | |||
1052 | /* Put DSP/MPUI into reset until needed */ | ||
1053 | omap_writew(0, ARM_RSTCT1); | ||
1054 | omap_writew(1, ARM_RSTCT2); | ||
1055 | omap_writew(0x400, ARM_IDLECT1); | ||
1056 | |||
1057 | /* | ||
1058 | * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) | ||
1059 | * of the ARM_IDLECT2 register must be set to zero. The power-on | ||
1060 | * default value of this bit is one. | ||
1061 | */ | ||
1062 | omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ | ||
1063 | |||
1064 | /* | ||
1065 | * Only enable those clocks we will need, let the drivers | ||
1066 | * enable other clocks as necessary | ||
1067 | */ | ||
1068 | clk_use(&armper_ck); | ||
1069 | clk_use(&armxor_ck); | ||
1070 | clk_use(&armtim_ck); | ||
1071 | |||
1072 | if (cpu_is_omap1510()) | ||
1073 | clk_enable(&arm_gpio_ck); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
diff --git a/arch/arm/mach-omap/clock.h b/arch/arm/mach-omap/clock.h new file mode 100644 index 000000000000..08c0ddde1835 --- /dev/null +++ b/arch/arm/mach-omap/clock.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/clock.h | ||
3 | * | ||
4 | * Copyright (C) 2004 Nokia corporation | ||
5 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
6 | * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __ARCH_ARM_OMAP_CLOCK_H | ||
14 | #define __ARCH_ARM_OMAP_CLOCK_H | ||
15 | |||
16 | struct module; | ||
17 | |||
18 | struct clk { | ||
19 | struct list_head node; | ||
20 | struct module *owner; | ||
21 | const char *name; | ||
22 | struct clk *parent; | ||
23 | unsigned long rate; | ||
24 | __s8 usecount; | ||
25 | __u16 flags; | ||
26 | __u32 enable_reg; | ||
27 | __u8 enable_bit; | ||
28 | __u8 rate_offset; | ||
29 | void (*recalc)(struct clk *); | ||
30 | int (*set_rate)(struct clk *, unsigned long); | ||
31 | long (*round_rate)(struct clk *, unsigned long); | ||
32 | void (*init)(struct clk *); | ||
33 | }; | ||
34 | |||
35 | |||
36 | struct mpu_rate { | ||
37 | unsigned long rate; | ||
38 | unsigned long xtal; | ||
39 | unsigned long pll_rate; | ||
40 | __u16 ckctl_val; | ||
41 | __u16 dpllctl_val; | ||
42 | }; | ||
43 | |||
44 | |||
45 | /* Clock flags */ | ||
46 | #define RATE_CKCTL 1 | ||
47 | #define RATE_FIXED 2 | ||
48 | #define RATE_PROPAGATES 4 | ||
49 | #define VIRTUAL_CLOCK 8 | ||
50 | #define ALWAYS_ENABLED 16 | ||
51 | #define ENABLE_REG_32BIT 32 | ||
52 | #define CLOCK_IN_OMAP16XX 64 | ||
53 | #define CLOCK_IN_OMAP1510 128 | ||
54 | #define CLOCK_IN_OMAP730 256 | ||
55 | |||
56 | /* ARM_CKCTL bit shifts */ | ||
57 | #define CKCTL_PERDIV_OFFSET 0 | ||
58 | #define CKCTL_LCDDIV_OFFSET 2 | ||
59 | #define CKCTL_ARMDIV_OFFSET 4 | ||
60 | #define CKCTL_DSPDIV_OFFSET 6 | ||
61 | #define CKCTL_TCDIV_OFFSET 8 | ||
62 | #define CKCTL_DSPMMUDIV_OFFSET 10 | ||
63 | /*#define ARM_TIMXO 12*/ | ||
64 | #define EN_DSPCK 13 | ||
65 | /*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */ | ||
66 | |||
67 | /* ARM_IDLECT1 bit shifts */ | ||
68 | /*#define IDLWDT_ARM 0*/ | ||
69 | /*#define IDLXORP_ARM 1*/ | ||
70 | /*#define IDLPER_ARM 2*/ | ||
71 | /*#define IDLLCD_ARM 3*/ | ||
72 | /*#define IDLLB_ARM 4*/ | ||
73 | /*#define IDLHSAB_ARM 5*/ | ||
74 | /*#define IDLIF_ARM 6*/ | ||
75 | /*#define IDLDPLL_ARM 7*/ | ||
76 | /*#define IDLAPI_ARM 8*/ | ||
77 | /*#define IDLTIM_ARM 9*/ | ||
78 | /*#define SETARM_IDLE 11*/ | ||
79 | |||
80 | /* ARM_IDLECT2 bit shifts */ | ||
81 | #define EN_WDTCK 0 | ||
82 | #define EN_XORPCK 1 | ||
83 | #define EN_PERCK 2 | ||
84 | #define EN_LCDCK 3 | ||
85 | #define EN_LBCK 4 /* Not on 1610/1710 */ | ||
86 | /*#define EN_HSABCK 5*/ | ||
87 | #define EN_APICK 6 | ||
88 | #define EN_TIMCK 7 | ||
89 | #define DMACK_REQ 8 | ||
90 | #define EN_GPIOCK 9 /* Not on 1610/1710 */ | ||
91 | /*#define EN_LBFREECK 10*/ | ||
92 | #define EN_CKOUT_ARM 11 | ||
93 | |||
94 | /* ARM_IDLECT3 bit shifts */ | ||
95 | #define EN_OCPI_CK 0 | ||
96 | #define EN_TC1_CK 2 | ||
97 | #define EN_TC2_CK 4 | ||
98 | |||
99 | /* Various register defines for clock controls scattered around OMAP chip */ | ||
100 | #define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */ | ||
101 | #define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */ | ||
102 | #define SWD_ULPD_PLL_CLK_REQ 1 /* In SWD_CLK_DIV_CTRL_SEL */ | ||
103 | #define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */ | ||
104 | #define SWD_CLK_DIV_CTRL_SEL 0xfffe0874 | ||
105 | #define COM_CLK_DIV_CTRL_SEL 0xfffe0878 | ||
106 | |||
107 | |||
108 | int clk_register(struct clk *clk); | ||
109 | void clk_unregister(struct clk *clk); | ||
110 | int clk_init(void); | ||
111 | |||
112 | #endif | ||
diff --git a/arch/arm/mach-omap/common.c b/arch/arm/mach-omap/common.c new file mode 100644 index 000000000000..265cde48586f --- /dev/null +++ b/arch/arm/mach-omap/common.c | |||
@@ -0,0 +1,549 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/common.c | ||
3 | * | ||
4 | * Code common to all OMAP machines. | ||
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 version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/pm.h> | ||
16 | #include <linux/console.h> | ||
17 | #include <linux/serial.h> | ||
18 | #include <linux/tty.h> | ||
19 | #include <linux/serial_8250.h> | ||
20 | #include <linux/serial_reg.h> | ||
21 | |||
22 | #include <asm/hardware.h> | ||
23 | #include <asm/system.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/mach/map.h> | ||
26 | #include <asm/hardware/clock.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/mach-types.h> | ||
29 | |||
30 | #include <asm/arch/board.h> | ||
31 | #include <asm/arch/mux.h> | ||
32 | #include <asm/arch/fpga.h> | ||
33 | |||
34 | #include "clock.h" | ||
35 | |||
36 | #define DEBUG 1 | ||
37 | |||
38 | struct omap_id { | ||
39 | u16 jtag_id; /* Used to determine OMAP type */ | ||
40 | u8 die_rev; /* Processor revision */ | ||
41 | u32 omap_id; /* OMAP revision */ | ||
42 | u32 type; /* Cpu id bits [31:08], cpu class bits [07:00] */ | ||
43 | }; | ||
44 | |||
45 | /* Register values to detect the OMAP version */ | ||
46 | static struct omap_id omap_ids[] __initdata = { | ||
47 | { .jtag_id = 0x355f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300100}, | ||
48 | { .jtag_id = 0xb55f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300300}, | ||
49 | { .jtag_id = 0xb470, .die_rev = 0x0, .omap_id = 0x03310100, .type = 0x15100000}, | ||
50 | { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x16100000}, | ||
51 | { .jtag_id = 0xb576, .die_rev = 0x2, .omap_id = 0x03320100, .type = 0x16110000}, | ||
52 | { .jtag_id = 0xb576, .die_rev = 0x3, .omap_id = 0x03320100, .type = 0x16100c00}, | ||
53 | { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320200, .type = 0x16100d00}, | ||
54 | { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, | ||
55 | { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, | ||
56 | { .jtag_id = 0xb576, .die_rev = 0x1, .omap_id = 0x03320100, .type = 0x16110000}, | ||
57 | { .jtag_id = 0xb58c, .die_rev = 0x2, .omap_id = 0x03320200, .type = 0x16110b00}, | ||
58 | { .jtag_id = 0xb58c, .die_rev = 0x3, .omap_id = 0x03320200, .type = 0x16110c00}, | ||
59 | { .jtag_id = 0xb65f, .die_rev = 0x0, .omap_id = 0x03320400, .type = 0x16212300}, | ||
60 | { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320400, .type = 0x16212300}, | ||
61 | { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x16212300}, | ||
62 | { .jtag_id = 0xb5f7, .die_rev = 0x0, .omap_id = 0x03330000, .type = 0x17100000}, | ||
63 | { .jtag_id = 0xb5f7, .die_rev = 0x1, .omap_id = 0x03330100, .type = 0x17100000}, | ||
64 | { .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000}, | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * Get OMAP type from PROD_ID. | ||
69 | * 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM. | ||
70 | * 1510 PROD_ID is empty, and 1610 PROD_ID does not make sense. | ||
71 | * Undocumented register in TEST BLOCK is used as fallback; This seems to | ||
72 | * work on 1510, 1610 & 1710. The official way hopefully will work in future | ||
73 | * processors. | ||
74 | */ | ||
75 | static u16 __init omap_get_jtag_id(void) | ||
76 | { | ||
77 | u32 prod_id, omap_id; | ||
78 | |||
79 | prod_id = omap_readl(OMAP_PRODUCTION_ID_1); | ||
80 | omap_id = omap_readl(OMAP32_ID_1); | ||
81 | |||
82 | /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */ | ||
83 | if (((prod_id >> 20) == 0) || (prod_id == omap_id)) | ||
84 | prod_id = 0; | ||
85 | else | ||
86 | prod_id &= 0xffff; | ||
87 | |||
88 | if (prod_id) | ||
89 | return prod_id; | ||
90 | |||
91 | /* Use OMAP32_ID_1 as fallback */ | ||
92 | prod_id = ((omap_id >> 12) & 0xffff); | ||
93 | |||
94 | return prod_id; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Get OMAP revision from DIE_REV. | ||
99 | * Early 1710 processors may have broken OMAP_DIE_ID, it contains PROD_ID. | ||
100 | * Undocumented register in the TEST BLOCK is used as fallback. | ||
101 | * REVISIT: This does not seem to work on 1510 | ||
102 | */ | ||
103 | static u8 __init omap_get_die_rev(void) | ||
104 | { | ||
105 | u32 die_rev; | ||
106 | |||
107 | die_rev = omap_readl(OMAP_DIE_ID_1); | ||
108 | |||
109 | /* Check for broken OMAP_DIE_ID on early 1710 */ | ||
110 | if (((die_rev >> 12) & 0xffff) == omap_get_jtag_id()) | ||
111 | die_rev = 0; | ||
112 | |||
113 | die_rev = (die_rev >> 17) & 0xf; | ||
114 | if (die_rev) | ||
115 | return die_rev; | ||
116 | |||
117 | die_rev = (omap_readl(OMAP32_ID_1) >> 28) & 0xf; | ||
118 | |||
119 | return die_rev; | ||
120 | } | ||
121 | |||
122 | static void __init omap_check_revision(void) | ||
123 | { | ||
124 | int i; | ||
125 | u16 jtag_id; | ||
126 | u8 die_rev; | ||
127 | u32 omap_id; | ||
128 | u8 cpu_type; | ||
129 | |||
130 | jtag_id = omap_get_jtag_id(); | ||
131 | die_rev = omap_get_die_rev(); | ||
132 | omap_id = omap_readl(OMAP32_ID_0); | ||
133 | |||
134 | #ifdef DEBUG | ||
135 | printk("OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0)); | ||
136 | printk("OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n", | ||
137 | omap_readl(OMAP_DIE_ID_1), | ||
138 | (omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf); | ||
139 | printk("OMAP_PRODUCTION_ID_0: 0x%08x\n", omap_readl(OMAP_PRODUCTION_ID_0)); | ||
140 | printk("OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n", | ||
141 | omap_readl(OMAP_PRODUCTION_ID_1), | ||
142 | omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff); | ||
143 | printk("OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0)); | ||
144 | printk("OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1)); | ||
145 | printk("JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev); | ||
146 | #endif | ||
147 | |||
148 | system_serial_high = omap_readl(OMAP_DIE_ID_0); | ||
149 | system_serial_low = omap_readl(OMAP_DIE_ID_1); | ||
150 | |||
151 | /* First check only the major version in a safe way */ | ||
152 | for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { | ||
153 | if (jtag_id == (omap_ids[i].jtag_id)) { | ||
154 | system_rev = omap_ids[i].type; | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /* Check if we can find the die revision */ | ||
160 | for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { | ||
161 | if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) { | ||
162 | system_rev = omap_ids[i].type; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | /* Finally check also the omap_id */ | ||
168 | for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { | ||
169 | if (jtag_id == omap_ids[i].jtag_id | ||
170 | && die_rev == omap_ids[i].die_rev | ||
171 | && omap_id == omap_ids[i].omap_id) { | ||
172 | system_rev = omap_ids[i].type; | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */ | ||
178 | cpu_type = system_rev >> 24; | ||
179 | |||
180 | switch (cpu_type) { | ||
181 | case 0x07: | ||
182 | system_rev |= 0x07; | ||
183 | break; | ||
184 | case 0x15: | ||
185 | system_rev |= 0x15; | ||
186 | break; | ||
187 | case 0x16: | ||
188 | case 0x17: | ||
189 | system_rev |= 0x16; | ||
190 | break; | ||
191 | case 0x24: | ||
192 | system_rev |= 0x24; | ||
193 | break; | ||
194 | default: | ||
195 | printk("Unknown OMAP cpu type: 0x%02x\n", cpu_type); | ||
196 | } | ||
197 | |||
198 | printk("OMAP%04x", system_rev >> 16); | ||
199 | if ((system_rev >> 8) & 0xff) | ||
200 | printk("%x", (system_rev >> 8) & 0xff); | ||
201 | printk(" revision %i handled as %02xxx id: %08x%08x\n", | ||
202 | die_rev, system_rev & 0xff, system_serial_low, | ||
203 | system_serial_high); | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * ---------------------------------------------------------------------------- | ||
208 | * OMAP I/O mapping | ||
209 | * | ||
210 | * The machine specific code may provide the extra mapping besides the | ||
211 | * default mapping provided here. | ||
212 | * ---------------------------------------------------------------------------- | ||
213 | */ | ||
214 | |||
215 | static struct map_desc omap_io_desc[] __initdata = { | ||
216 | { IO_VIRT, IO_PHYS, IO_SIZE, MT_DEVICE }, | ||
217 | }; | ||
218 | |||
219 | #ifdef CONFIG_ARCH_OMAP730 | ||
220 | static struct map_desc omap730_io_desc[] __initdata = { | ||
221 | { OMAP730_DSP_BASE, OMAP730_DSP_START, OMAP730_DSP_SIZE, MT_DEVICE }, | ||
222 | { OMAP730_DSPREG_BASE, OMAP730_DSPREG_START, OMAP730_DSPREG_SIZE, MT_DEVICE }, | ||
223 | { OMAP730_SRAM_BASE, OMAP730_SRAM_START, OMAP730_SRAM_SIZE, MT_DEVICE } | ||
224 | }; | ||
225 | #endif | ||
226 | |||
227 | #ifdef CONFIG_ARCH_OMAP1510 | ||
228 | static struct map_desc omap1510_io_desc[] __initdata = { | ||
229 | { OMAP1510_DSP_BASE, OMAP1510_DSP_START, OMAP1510_DSP_SIZE, MT_DEVICE }, | ||
230 | { OMAP1510_DSPREG_BASE, OMAP1510_DSPREG_START, OMAP1510_DSPREG_SIZE, MT_DEVICE }, | ||
231 | { OMAP1510_SRAM_BASE, OMAP1510_SRAM_START, OMAP1510_SRAM_SIZE, MT_DEVICE } | ||
232 | }; | ||
233 | #endif | ||
234 | |||
235 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
236 | static struct map_desc omap1610_io_desc[] __initdata = { | ||
237 | { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, | ||
238 | { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, | ||
239 | { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP1610_SRAM_SIZE, MT_DEVICE } | ||
240 | }; | ||
241 | |||
242 | static struct map_desc omap5912_io_desc[] __initdata = { | ||
243 | { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, | ||
244 | { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, | ||
245 | /* | ||
246 | * The OMAP5912 has 250kByte internal SRAM. Because the mapping is baseed on page | ||
247 | * size (4kByte), it seems that the last 2kByte (=0x800) of the 250kByte are not mapped. | ||
248 | * Add additional 2kByte (0x800) so that the last page is mapped and the last 2kByte | ||
249 | * can be used. | ||
250 | */ | ||
251 | { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP5912_SRAM_SIZE + 0x800, MT_DEVICE } | ||
252 | }; | ||
253 | #endif | ||
254 | |||
255 | static int initialized = 0; | ||
256 | |||
257 | static void __init _omap_map_io(void) | ||
258 | { | ||
259 | initialized = 1; | ||
260 | |||
261 | /* We have to initialize the IO space mapping before we can run | ||
262 | * cpu_is_omapxxx() macros. */ | ||
263 | iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc)); | ||
264 | omap_check_revision(); | ||
265 | |||
266 | #ifdef CONFIG_ARCH_OMAP730 | ||
267 | if (cpu_is_omap730()) { | ||
268 | iotable_init(omap730_io_desc, ARRAY_SIZE(omap730_io_desc)); | ||
269 | } | ||
270 | #endif | ||
271 | #ifdef CONFIG_ARCH_OMAP1510 | ||
272 | if (cpu_is_omap1510()) { | ||
273 | iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); | ||
274 | } | ||
275 | #endif | ||
276 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
277 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
278 | iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc)); | ||
279 | } | ||
280 | if (cpu_is_omap5912()) { | ||
281 | iotable_init(omap5912_io_desc, ARRAY_SIZE(omap5912_io_desc)); | ||
282 | } | ||
283 | #endif | ||
284 | |||
285 | /* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort | ||
286 | * on a Posted Write in the TIPB Bridge". | ||
287 | */ | ||
288 | omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL); | ||
289 | omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL); | ||
290 | |||
291 | /* Must init clocks early to assure that timer interrupt works | ||
292 | */ | ||
293 | clk_init(); | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * This should only get called from board specific init | ||
298 | */ | ||
299 | void omap_map_io(void) | ||
300 | { | ||
301 | if (!initialized) | ||
302 | _omap_map_io(); | ||
303 | } | ||
304 | |||
305 | static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, | ||
306 | int offset) | ||
307 | { | ||
308 | offset <<= up->regshift; | ||
309 | return (unsigned int)__raw_readb(up->membase + offset); | ||
310 | } | ||
311 | |||
312 | static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset, | ||
313 | int value) | ||
314 | { | ||
315 | offset <<= p->regshift; | ||
316 | __raw_writeb(value, p->membase + offset); | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Internal UARTs need to be initialized for the 8250 autoconfig to work | ||
321 | * properly. Note that the TX watermark initialization may not be needed | ||
322 | * once the 8250.c watermark handling code is merged. | ||
323 | */ | ||
324 | static void __init omap_serial_reset(struct plat_serial8250_port *p) | ||
325 | { | ||
326 | omap_serial_outp(p, UART_OMAP_MDR1, 0x07); /* disable UART */ | ||
327 | omap_serial_outp(p, UART_OMAP_SCR, 0x08); /* TX watermark */ | ||
328 | omap_serial_outp(p, UART_OMAP_MDR1, 0x00); /* enable UART */ | ||
329 | |||
330 | if (!cpu_is_omap1510()) { | ||
331 | omap_serial_outp(p, UART_OMAP_SYSC, 0x01); | ||
332 | while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01)); | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static struct plat_serial8250_port serial_platform_data[] = { | ||
337 | { | ||
338 | .membase = (char*)IO_ADDRESS(OMAP_UART1_BASE), | ||
339 | .mapbase = (unsigned long)OMAP_UART1_BASE, | ||
340 | .irq = INT_UART1, | ||
341 | .flags = UPF_BOOT_AUTOCONF, | ||
342 | .iotype = UPIO_MEM, | ||
343 | .regshift = 2, | ||
344 | .uartclk = OMAP16XX_BASE_BAUD * 16, | ||
345 | }, | ||
346 | { | ||
347 | .membase = (char*)IO_ADDRESS(OMAP_UART2_BASE), | ||
348 | .mapbase = (unsigned long)OMAP_UART2_BASE, | ||
349 | .irq = INT_UART2, | ||
350 | .flags = UPF_BOOT_AUTOCONF, | ||
351 | .iotype = UPIO_MEM, | ||
352 | .regshift = 2, | ||
353 | .uartclk = OMAP16XX_BASE_BAUD * 16, | ||
354 | }, | ||
355 | { | ||
356 | .membase = (char*)IO_ADDRESS(OMAP_UART3_BASE), | ||
357 | .mapbase = (unsigned long)OMAP_UART3_BASE, | ||
358 | .irq = INT_UART3, | ||
359 | .flags = UPF_BOOT_AUTOCONF, | ||
360 | .iotype = UPIO_MEM, | ||
361 | .regshift = 2, | ||
362 | .uartclk = OMAP16XX_BASE_BAUD * 16, | ||
363 | }, | ||
364 | { }, | ||
365 | }; | ||
366 | |||
367 | static struct platform_device serial_device = { | ||
368 | .name = "serial8250", | ||
369 | .id = 0, | ||
370 | .dev = { | ||
371 | .platform_data = serial_platform_data, | ||
372 | }, | ||
373 | }; | ||
374 | |||
375 | /* | ||
376 | * Note that on Innovator-1510 UART2 pins conflict with USB2. | ||
377 | * By default UART2 does not work on Innovator-1510 if you have | ||
378 | * USB OHCI enabled. To use UART2, you must disable USB2 first. | ||
379 | */ | ||
380 | void __init omap_serial_init(int ports[OMAP_MAX_NR_PORTS]) | ||
381 | { | ||
382 | int i; | ||
383 | |||
384 | if (cpu_is_omap730()) { | ||
385 | serial_platform_data[0].regshift = 0; | ||
386 | serial_platform_data[1].regshift = 0; | ||
387 | serial_platform_data[0].irq = INT_730_UART_MODEM_1; | ||
388 | serial_platform_data[1].irq = INT_730_UART_MODEM_IRDA_2; | ||
389 | } | ||
390 | |||
391 | if (cpu_is_omap1510()) { | ||
392 | serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16; | ||
393 | serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16; | ||
394 | serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16; | ||
395 | } | ||
396 | |||
397 | for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { | ||
398 | unsigned char reg; | ||
399 | |||
400 | if (ports[i] == 0) { | ||
401 | serial_platform_data[i].membase = 0; | ||
402 | serial_platform_data[i].mapbase = 0; | ||
403 | continue; | ||
404 | } | ||
405 | |||
406 | switch (i) { | ||
407 | case 0: | ||
408 | if (cpu_is_omap1510()) { | ||
409 | omap_cfg_reg(UART1_TX); | ||
410 | omap_cfg_reg(UART1_RTS); | ||
411 | if (machine_is_omap_innovator()) { | ||
412 | reg = fpga_read(OMAP1510_FPGA_POWER); | ||
413 | reg |= OMAP1510_FPGA_PCR_COM1_EN; | ||
414 | fpga_write(reg, OMAP1510_FPGA_POWER); | ||
415 | udelay(10); | ||
416 | } | ||
417 | } | ||
418 | break; | ||
419 | case 1: | ||
420 | if (cpu_is_omap1510()) { | ||
421 | omap_cfg_reg(UART2_TX); | ||
422 | omap_cfg_reg(UART2_RTS); | ||
423 | if (machine_is_omap_innovator()) { | ||
424 | reg = fpga_read(OMAP1510_FPGA_POWER); | ||
425 | reg |= OMAP1510_FPGA_PCR_COM2_EN; | ||
426 | fpga_write(reg, OMAP1510_FPGA_POWER); | ||
427 | udelay(10); | ||
428 | } | ||
429 | } | ||
430 | break; | ||
431 | case 2: | ||
432 | if (cpu_is_omap1510()) { | ||
433 | omap_cfg_reg(UART3_TX); | ||
434 | omap_cfg_reg(UART3_RX); | ||
435 | } | ||
436 | if (cpu_is_omap1710()) { | ||
437 | clk_enable(clk_get(0, "uart3_ck")); | ||
438 | } | ||
439 | break; | ||
440 | } | ||
441 | omap_serial_reset(&serial_platform_data[i]); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | static int __init omap_init(void) | ||
446 | { | ||
447 | return platform_device_register(&serial_device); | ||
448 | } | ||
449 | arch_initcall(omap_init); | ||
450 | |||
451 | #define NO_LENGTH_CHECK 0xffffffff | ||
452 | |||
453 | extern int omap_bootloader_tag_len; | ||
454 | extern u8 omap_bootloader_tag[]; | ||
455 | |||
456 | struct omap_board_config_kernel *omap_board_config; | ||
457 | int omap_board_config_size = 0; | ||
458 | |||
459 | static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) | ||
460 | { | ||
461 | struct omap_board_config_kernel *kinfo = NULL; | ||
462 | int i; | ||
463 | |||
464 | #ifdef CONFIG_OMAP_BOOT_TAG | ||
465 | struct omap_board_config_entry *info = NULL; | ||
466 | |||
467 | if (omap_bootloader_tag_len > 4) | ||
468 | info = (struct omap_board_config_entry *) omap_bootloader_tag; | ||
469 | while (info != NULL) { | ||
470 | u8 *next; | ||
471 | |||
472 | if (info->tag == tag) { | ||
473 | if (skip == 0) | ||
474 | break; | ||
475 | skip--; | ||
476 | } | ||
477 | |||
478 | if ((info->len & 0x03) != 0) { | ||
479 | /* We bail out to avoid an alignment fault */ | ||
480 | printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", | ||
481 | info->len, info->tag); | ||
482 | return NULL; | ||
483 | } | ||
484 | next = (u8 *) info + sizeof(*info) + info->len; | ||
485 | if (next >= omap_bootloader_tag + omap_bootloader_tag_len) | ||
486 | info = NULL; | ||
487 | else | ||
488 | info = (struct omap_board_config_entry *) next; | ||
489 | } | ||
490 | if (info != NULL) { | ||
491 | /* Check the length as a lame attempt to check for | ||
492 | * binary inconsistancy. */ | ||
493 | if (len != NO_LENGTH_CHECK) { | ||
494 | /* Word-align len */ | ||
495 | if (len & 0x03) | ||
496 | len = (len + 3) & ~0x03; | ||
497 | if (info->len != len) { | ||
498 | printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", | ||
499 | tag, len, info->len); | ||
500 | return NULL; | ||
501 | } | ||
502 | } | ||
503 | if (len_out != NULL) | ||
504 | *len_out = info->len; | ||
505 | return info->data; | ||
506 | } | ||
507 | #endif | ||
508 | /* Try to find the config from the board-specific structures | ||
509 | * in the kernel. */ | ||
510 | for (i = 0; i < omap_board_config_size; i++) { | ||
511 | if (omap_board_config[i].tag == tag) { | ||
512 | kinfo = &omap_board_config[i]; | ||
513 | break; | ||
514 | } | ||
515 | } | ||
516 | if (kinfo == NULL) | ||
517 | return NULL; | ||
518 | return kinfo->data; | ||
519 | } | ||
520 | |||
521 | const void *__omap_get_config(u16 tag, size_t len, int nr) | ||
522 | { | ||
523 | return get_config(tag, len, nr, NULL); | ||
524 | } | ||
525 | EXPORT_SYMBOL(__omap_get_config); | ||
526 | |||
527 | const void *omap_get_var_config(u16 tag, size_t *len) | ||
528 | { | ||
529 | return get_config(tag, NO_LENGTH_CHECK, 0, len); | ||
530 | } | ||
531 | EXPORT_SYMBOL(omap_get_var_config); | ||
532 | |||
533 | static int __init omap_add_serial_console(void) | ||
534 | { | ||
535 | const struct omap_uart_config *info; | ||
536 | |||
537 | info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config); | ||
538 | if (info != NULL && info->console_uart) { | ||
539 | static char speed[11], *opt = NULL; | ||
540 | |||
541 | if (info->console_speed) { | ||
542 | snprintf(speed, sizeof(speed), "%u", info->console_speed); | ||
543 | opt = speed; | ||
544 | } | ||
545 | return add_preferred_console("ttyS", info->console_uart - 1, opt); | ||
546 | } | ||
547 | return 0; | ||
548 | } | ||
549 | console_initcall(omap_add_serial_console); | ||
diff --git a/arch/arm/mach-omap/common.h b/arch/arm/mach-omap/common.h new file mode 100644 index 000000000000..9f62858c0df4 --- /dev/null +++ b/arch/arm/mach-omap/common.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/common.h | ||
3 | * | ||
4 | * Header for code common to all OMAP machines. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
12 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
13 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
14 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
15 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
16 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
17 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
18 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
19 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
20 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License along | ||
23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
24 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | */ | ||
26 | |||
27 | #ifndef __ARCH_ARM_MACH_OMAP_COMMON_H | ||
28 | #define __ARCH_ARM_MACH_OMAP_COMMON_H | ||
29 | |||
30 | struct sys_timer; | ||
31 | |||
32 | extern void omap_map_io(void); | ||
33 | extern struct sys_timer omap_timer; | ||
34 | extern void omap_serial_init(int ports[]); | ||
35 | |||
36 | #endif /* __ARCH_ARM_MACH_OMAP_COMMON_H */ | ||
diff --git a/arch/arm/mach-omap/dma.c b/arch/arm/mach-omap/dma.c new file mode 100644 index 000000000000..7a9ebe80d6f8 --- /dev/null +++ b/arch/arm/mach-omap/dma.c | |||
@@ -0,0 +1,1086 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/omap/dma.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Nokia Corporation | ||
5 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> | ||
6 | * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> | ||
7 | * Graphics DMA and LCD DMA graphics tranformations | ||
8 | * by Imre Deak <imre.deak@nokia.com> | ||
9 | * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. | ||
10 | * | ||
11 | * Support functions for the OMAP internal DMA channels. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | |||
26 | #include <asm/system.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/hardware.h> | ||
29 | #include <asm/dma.h> | ||
30 | #include <asm/io.h> | ||
31 | |||
32 | #include <asm/arch/tc.h> | ||
33 | |||
34 | #define OMAP_DMA_ACTIVE 0x01 | ||
35 | |||
36 | #define OMAP_DMA_CCR_EN (1 << 7) | ||
37 | |||
38 | #define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) | ||
39 | |||
40 | static int enable_1510_mode = 0; | ||
41 | |||
42 | struct omap_dma_lch { | ||
43 | int next_lch; | ||
44 | int dev_id; | ||
45 | u16 saved_csr; | ||
46 | u16 enabled_irqs; | ||
47 | const char *dev_name; | ||
48 | void (* callback)(int lch, u16 ch_status, void *data); | ||
49 | void *data; | ||
50 | long flags; | ||
51 | }; | ||
52 | |||
53 | static int dma_chan_count; | ||
54 | |||
55 | static spinlock_t dma_chan_lock; | ||
56 | static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
57 | |||
58 | const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = { | ||
59 | INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3, | ||
60 | INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7, | ||
61 | INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10, | ||
62 | INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13, | ||
63 | INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD | ||
64 | }; | ||
65 | |||
66 | static inline int get_gdma_dev(int req) | ||
67 | { | ||
68 | u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; | ||
69 | int shift = ((req - 1) % 5) * 6; | ||
70 | |||
71 | return ((omap_readl(reg) >> shift) & 0x3f) + 1; | ||
72 | } | ||
73 | |||
74 | static inline void set_gdma_dev(int req, int dev) | ||
75 | { | ||
76 | u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; | ||
77 | int shift = ((req - 1) % 5) * 6; | ||
78 | u32 l; | ||
79 | |||
80 | l = omap_readl(reg); | ||
81 | l &= ~(0x3f << shift); | ||
82 | l |= (dev - 1) << shift; | ||
83 | omap_writel(l, reg); | ||
84 | } | ||
85 | |||
86 | static void clear_lch_regs(int lch) | ||
87 | { | ||
88 | int i; | ||
89 | u32 lch_base = OMAP_DMA_BASE + lch * 0x40; | ||
90 | |||
91 | for (i = 0; i < 0x2c; i += 2) | ||
92 | omap_writew(0, lch_base + i); | ||
93 | } | ||
94 | |||
95 | void omap_set_dma_priority(int dst_port, int priority) | ||
96 | { | ||
97 | unsigned long reg; | ||
98 | u32 l; | ||
99 | |||
100 | switch (dst_port) { | ||
101 | case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ | ||
102 | reg = OMAP_TC_OCPT1_PRIOR; | ||
103 | break; | ||
104 | case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ | ||
105 | reg = OMAP_TC_OCPT2_PRIOR; | ||
106 | break; | ||
107 | case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ | ||
108 | reg = OMAP_TC_EMIFF_PRIOR; | ||
109 | break; | ||
110 | case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ | ||
111 | reg = OMAP_TC_EMIFS_PRIOR; | ||
112 | break; | ||
113 | default: | ||
114 | BUG(); | ||
115 | return; | ||
116 | } | ||
117 | l = omap_readl(reg); | ||
118 | l &= ~(0xf << 8); | ||
119 | l |= (priority & 0xf) << 8; | ||
120 | omap_writel(l, reg); | ||
121 | } | ||
122 | |||
123 | void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, | ||
124 | int frame_count, int sync_mode) | ||
125 | { | ||
126 | u16 w; | ||
127 | |||
128 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
129 | w &= ~0x03; | ||
130 | w |= data_type; | ||
131 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
132 | |||
133 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
134 | w &= ~(1 << 5); | ||
135 | if (sync_mode == OMAP_DMA_SYNC_FRAME) | ||
136 | w |= 1 << 5; | ||
137 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
138 | |||
139 | w = omap_readw(OMAP_DMA_CCR2(lch)); | ||
140 | w &= ~(1 << 2); | ||
141 | if (sync_mode == OMAP_DMA_SYNC_BLOCK) | ||
142 | w |= 1 << 2; | ||
143 | omap_writew(w, OMAP_DMA_CCR2(lch)); | ||
144 | |||
145 | omap_writew(elem_count, OMAP_DMA_CEN(lch)); | ||
146 | omap_writew(frame_count, OMAP_DMA_CFN(lch)); | ||
147 | |||
148 | } | ||
149 | void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) | ||
150 | { | ||
151 | u16 w; | ||
152 | |||
153 | BUG_ON(omap_dma_in_1510_mode()); | ||
154 | |||
155 | w = omap_readw(OMAP_DMA_CCR2(lch)) & ~0x03; | ||
156 | switch (mode) { | ||
157 | case OMAP_DMA_CONSTANT_FILL: | ||
158 | w |= 0x01; | ||
159 | break; | ||
160 | case OMAP_DMA_TRANSPARENT_COPY: | ||
161 | w |= 0x02; | ||
162 | break; | ||
163 | case OMAP_DMA_COLOR_DIS: | ||
164 | break; | ||
165 | default: | ||
166 | BUG(); | ||
167 | } | ||
168 | omap_writew(w, OMAP_DMA_CCR2(lch)); | ||
169 | |||
170 | w = omap_readw(OMAP_DMA_LCH_CTRL(lch)) & ~0x0f; | ||
171 | /* Default is channel type 2D */ | ||
172 | if (mode) { | ||
173 | omap_writew((u16)color, OMAP_DMA_COLOR_L(lch)); | ||
174 | omap_writew((u16)(color >> 16), OMAP_DMA_COLOR_U(lch)); | ||
175 | w |= 1; /* Channel type G */ | ||
176 | } | ||
177 | omap_writew(w, OMAP_DMA_LCH_CTRL(lch)); | ||
178 | } | ||
179 | |||
180 | |||
181 | void omap_set_dma_src_params(int lch, int src_port, int src_amode, | ||
182 | unsigned long src_start) | ||
183 | { | ||
184 | u16 w; | ||
185 | |||
186 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
187 | w &= ~(0x1f << 2); | ||
188 | w |= src_port << 2; | ||
189 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
190 | |||
191 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
192 | w &= ~(0x03 << 12); | ||
193 | w |= src_amode << 12; | ||
194 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
195 | |||
196 | omap_writew(src_start >> 16, OMAP_DMA_CSSA_U(lch)); | ||
197 | omap_writew(src_start, OMAP_DMA_CSSA_L(lch)); | ||
198 | } | ||
199 | |||
200 | void omap_set_dma_src_index(int lch, int eidx, int fidx) | ||
201 | { | ||
202 | omap_writew(eidx, OMAP_DMA_CSEI(lch)); | ||
203 | omap_writew(fidx, OMAP_DMA_CSFI(lch)); | ||
204 | } | ||
205 | |||
206 | void omap_set_dma_src_data_pack(int lch, int enable) | ||
207 | { | ||
208 | u16 w; | ||
209 | |||
210 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 6); | ||
211 | w |= enable ? (1 << 6) : 0; | ||
212 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
213 | } | ||
214 | |||
215 | void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) | ||
216 | { | ||
217 | u16 w; | ||
218 | |||
219 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 7); | ||
220 | switch (burst_mode) { | ||
221 | case OMAP_DMA_DATA_BURST_DIS: | ||
222 | break; | ||
223 | case OMAP_DMA_DATA_BURST_4: | ||
224 | w |= (0x01 << 7); | ||
225 | break; | ||
226 | case OMAP_DMA_DATA_BURST_8: | ||
227 | /* not supported by current hardware | ||
228 | * w |= (0x03 << 7); | ||
229 | * fall through | ||
230 | */ | ||
231 | default: | ||
232 | BUG(); | ||
233 | } | ||
234 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
235 | } | ||
236 | |||
237 | void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, | ||
238 | unsigned long dest_start) | ||
239 | { | ||
240 | u16 w; | ||
241 | |||
242 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
243 | w &= ~(0x1f << 9); | ||
244 | w |= dest_port << 9; | ||
245 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
246 | |||
247 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
248 | w &= ~(0x03 << 14); | ||
249 | w |= dest_amode << 14; | ||
250 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
251 | |||
252 | omap_writew(dest_start >> 16, OMAP_DMA_CDSA_U(lch)); | ||
253 | omap_writew(dest_start, OMAP_DMA_CDSA_L(lch)); | ||
254 | } | ||
255 | |||
256 | void omap_set_dma_dest_index(int lch, int eidx, int fidx) | ||
257 | { | ||
258 | omap_writew(eidx, OMAP_DMA_CDEI(lch)); | ||
259 | omap_writew(fidx, OMAP_DMA_CDFI(lch)); | ||
260 | } | ||
261 | |||
262 | void omap_set_dma_dest_data_pack(int lch, int enable) | ||
263 | { | ||
264 | u16 w; | ||
265 | |||
266 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 13); | ||
267 | w |= enable ? (1 << 13) : 0; | ||
268 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
269 | } | ||
270 | |||
271 | void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) | ||
272 | { | ||
273 | u16 w; | ||
274 | |||
275 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 14); | ||
276 | switch (burst_mode) { | ||
277 | case OMAP_DMA_DATA_BURST_DIS: | ||
278 | break; | ||
279 | case OMAP_DMA_DATA_BURST_4: | ||
280 | w |= (0x01 << 14); | ||
281 | break; | ||
282 | case OMAP_DMA_DATA_BURST_8: | ||
283 | w |= (0x03 << 14); | ||
284 | break; | ||
285 | default: | ||
286 | printk(KERN_ERR "Invalid DMA burst mode\n"); | ||
287 | BUG(); | ||
288 | return; | ||
289 | } | ||
290 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
291 | } | ||
292 | |||
293 | static inline void init_intr(int lch) | ||
294 | { | ||
295 | u16 w; | ||
296 | |||
297 | /* Read CSR to make sure it's cleared. */ | ||
298 | w = omap_readw(OMAP_DMA_CSR(lch)); | ||
299 | /* Enable some nice interrupts. */ | ||
300 | omap_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR(lch)); | ||
301 | dma_chan[lch].flags |= OMAP_DMA_ACTIVE; | ||
302 | } | ||
303 | |||
304 | static inline void enable_lnk(int lch) | ||
305 | { | ||
306 | u16 w; | ||
307 | |||
308 | /* Clear the STOP_LNK bits */ | ||
309 | w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); | ||
310 | w &= ~(1 << 14); | ||
311 | omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); | ||
312 | |||
313 | /* And set the ENABLE_LNK bits */ | ||
314 | if (dma_chan[lch].next_lch != -1) | ||
315 | omap_writew(dma_chan[lch].next_lch | (1 << 15), | ||
316 | OMAP_DMA_CLNK_CTRL(lch)); | ||
317 | } | ||
318 | |||
319 | static inline void disable_lnk(int lch) | ||
320 | { | ||
321 | u16 w; | ||
322 | |||
323 | /* Disable interrupts */ | ||
324 | omap_writew(0, OMAP_DMA_CICR(lch)); | ||
325 | |||
326 | /* Set the STOP_LNK bit */ | ||
327 | w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); | ||
328 | w |= (1 << 14); | ||
329 | w = omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); | ||
330 | |||
331 | dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; | ||
332 | } | ||
333 | |||
334 | void omap_start_dma(int lch) | ||
335 | { | ||
336 | u16 w; | ||
337 | |||
338 | if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { | ||
339 | int next_lch, cur_lch; | ||
340 | char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
341 | |||
342 | dma_chan_link_map[lch] = 1; | ||
343 | /* Set the link register of the first channel */ | ||
344 | enable_lnk(lch); | ||
345 | |||
346 | memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); | ||
347 | cur_lch = dma_chan[lch].next_lch; | ||
348 | do { | ||
349 | next_lch = dma_chan[cur_lch].next_lch; | ||
350 | |||
351 | /* The loop case: we've been here already */ | ||
352 | if (dma_chan_link_map[cur_lch]) | ||
353 | break; | ||
354 | /* Mark the current channel */ | ||
355 | dma_chan_link_map[cur_lch] = 1; | ||
356 | |||
357 | enable_lnk(cur_lch); | ||
358 | init_intr(cur_lch); | ||
359 | |||
360 | cur_lch = next_lch; | ||
361 | } while (next_lch != -1); | ||
362 | } | ||
363 | |||
364 | init_intr(lch); | ||
365 | |||
366 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
367 | w |= OMAP_DMA_CCR_EN; | ||
368 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
369 | dma_chan[lch].flags |= OMAP_DMA_ACTIVE; | ||
370 | } | ||
371 | |||
372 | void omap_stop_dma(int lch) | ||
373 | { | ||
374 | u16 w; | ||
375 | |||
376 | if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { | ||
377 | int next_lch, cur_lch = lch; | ||
378 | char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
379 | |||
380 | memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); | ||
381 | do { | ||
382 | /* The loop case: we've been here already */ | ||
383 | if (dma_chan_link_map[cur_lch]) | ||
384 | break; | ||
385 | /* Mark the current channel */ | ||
386 | dma_chan_link_map[cur_lch] = 1; | ||
387 | |||
388 | disable_lnk(cur_lch); | ||
389 | |||
390 | next_lch = dma_chan[cur_lch].next_lch; | ||
391 | cur_lch = next_lch; | ||
392 | } while (next_lch != -1); | ||
393 | |||
394 | return; | ||
395 | } | ||
396 | /* Disable all interrupts on the channel */ | ||
397 | omap_writew(0, OMAP_DMA_CICR(lch)); | ||
398 | |||
399 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
400 | w &= ~OMAP_DMA_CCR_EN; | ||
401 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
402 | dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; | ||
403 | } | ||
404 | |||
405 | void omap_enable_dma_irq(int lch, u16 bits) | ||
406 | { | ||
407 | dma_chan[lch].enabled_irqs |= bits; | ||
408 | } | ||
409 | |||
410 | void omap_disable_dma_irq(int lch, u16 bits) | ||
411 | { | ||
412 | dma_chan[lch].enabled_irqs &= ~bits; | ||
413 | } | ||
414 | |||
415 | static int dma_handle_ch(int ch) | ||
416 | { | ||
417 | u16 csr; | ||
418 | |||
419 | if (enable_1510_mode && ch >= 6) { | ||
420 | csr = dma_chan[ch].saved_csr; | ||
421 | dma_chan[ch].saved_csr = 0; | ||
422 | } else | ||
423 | csr = omap_readw(OMAP_DMA_CSR(ch)); | ||
424 | if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { | ||
425 | dma_chan[ch + 6].saved_csr = csr >> 7; | ||
426 | csr &= 0x7f; | ||
427 | } | ||
428 | if (!csr) | ||
429 | return 0; | ||
430 | if (unlikely(dma_chan[ch].dev_id == -1)) { | ||
431 | printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n", | ||
432 | ch, csr); | ||
433 | return 0; | ||
434 | } | ||
435 | if (unlikely(csr & OMAP_DMA_TOUT_IRQ)) | ||
436 | printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id); | ||
437 | if (unlikely(csr & OMAP_DMA_DROP_IRQ)) | ||
438 | printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n", | ||
439 | dma_chan[ch].dev_id); | ||
440 | if (likely(csr & OMAP_DMA_BLOCK_IRQ)) | ||
441 | dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; | ||
442 | if (likely(dma_chan[ch].callback != NULL)) | ||
443 | dma_chan[ch].callback(ch, csr, dma_chan[ch].data); | ||
444 | return 1; | ||
445 | } | ||
446 | |||
447 | static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
448 | { | ||
449 | int ch = ((int) dev_id) - 1; | ||
450 | int handled = 0; | ||
451 | |||
452 | for (;;) { | ||
453 | int handled_now = 0; | ||
454 | |||
455 | handled_now += dma_handle_ch(ch); | ||
456 | if (enable_1510_mode && dma_chan[ch + 6].saved_csr) | ||
457 | handled_now += dma_handle_ch(ch + 6); | ||
458 | if (!handled_now) | ||
459 | break; | ||
460 | handled += handled_now; | ||
461 | } | ||
462 | |||
463 | return handled ? IRQ_HANDLED : IRQ_NONE; | ||
464 | } | ||
465 | |||
466 | int omap_request_dma(int dev_id, const char *dev_name, | ||
467 | void (* callback)(int lch, u16 ch_status, void *data), | ||
468 | void *data, int *dma_ch_out) | ||
469 | { | ||
470 | int ch, free_ch = -1; | ||
471 | unsigned long flags; | ||
472 | struct omap_dma_lch *chan; | ||
473 | |||
474 | spin_lock_irqsave(&dma_chan_lock, flags); | ||
475 | for (ch = 0; ch < dma_chan_count; ch++) { | ||
476 | if (free_ch == -1 && dma_chan[ch].dev_id == -1) { | ||
477 | free_ch = ch; | ||
478 | if (dev_id == 0) | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | if (free_ch == -1) { | ||
483 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
484 | return -EBUSY; | ||
485 | } | ||
486 | chan = dma_chan + free_ch; | ||
487 | chan->dev_id = dev_id; | ||
488 | clear_lch_regs(free_ch); | ||
489 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
490 | |||
491 | chan->dev_id = dev_id; | ||
492 | chan->dev_name = dev_name; | ||
493 | chan->callback = callback; | ||
494 | chan->data = data; | ||
495 | chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; | ||
496 | |||
497 | if (cpu_is_omap16xx()) { | ||
498 | /* If the sync device is set, configure it dynamically. */ | ||
499 | if (dev_id != 0) { | ||
500 | set_gdma_dev(free_ch + 1, dev_id); | ||
501 | dev_id = free_ch + 1; | ||
502 | } | ||
503 | /* Disable the 1510 compatibility mode and set the sync device | ||
504 | * id. */ | ||
505 | omap_writew(dev_id | (1 << 10), OMAP_DMA_CCR(free_ch)); | ||
506 | } else { | ||
507 | omap_writew(dev_id, OMAP_DMA_CCR(free_ch)); | ||
508 | } | ||
509 | *dma_ch_out = free_ch; | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | void omap_free_dma(int ch) | ||
515 | { | ||
516 | unsigned long flags; | ||
517 | |||
518 | spin_lock_irqsave(&dma_chan_lock, flags); | ||
519 | if (dma_chan[ch].dev_id == -1) { | ||
520 | printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch); | ||
521 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
522 | return; | ||
523 | } | ||
524 | dma_chan[ch].dev_id = -1; | ||
525 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
526 | |||
527 | /* Disable all DMA interrupts for the channel. */ | ||
528 | omap_writew(0, OMAP_DMA_CICR(ch)); | ||
529 | /* Make sure the DMA transfer is stopped. */ | ||
530 | omap_writew(0, OMAP_DMA_CCR(ch)); | ||
531 | } | ||
532 | |||
533 | int omap_dma_in_1510_mode(void) | ||
534 | { | ||
535 | return enable_1510_mode; | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * lch_queue DMA will start right after lch_head one is finished. | ||
540 | * For this DMA link to start, you still need to start (see omap_start_dma) | ||
541 | * the first one. That will fire up the entire queue. | ||
542 | */ | ||
543 | void omap_dma_link_lch (int lch_head, int lch_queue) | ||
544 | { | ||
545 | if (omap_dma_in_1510_mode()) { | ||
546 | printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||
547 | BUG(); | ||
548 | return; | ||
549 | } | ||
550 | |||
551 | if ((dma_chan[lch_head].dev_id == -1) || | ||
552 | (dma_chan[lch_queue].dev_id == -1)) { | ||
553 | printk(KERN_ERR "omap_dma: trying to link non requested channels\n"); | ||
554 | dump_stack(); | ||
555 | } | ||
556 | |||
557 | dma_chan[lch_head].next_lch = lch_queue; | ||
558 | } | ||
559 | |||
560 | /* | ||
561 | * Once the DMA queue is stopped, we can destroy it. | ||
562 | */ | ||
563 | void omap_dma_unlink_lch (int lch_head, int lch_queue) | ||
564 | { | ||
565 | if (omap_dma_in_1510_mode()) { | ||
566 | printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||
567 | BUG(); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | if (dma_chan[lch_head].next_lch != lch_queue || | ||
572 | dma_chan[lch_head].next_lch == -1) { | ||
573 | printk(KERN_ERR "omap_dma: trying to unlink non linked channels\n"); | ||
574 | dump_stack(); | ||
575 | } | ||
576 | |||
577 | |||
578 | if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || | ||
579 | (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) { | ||
580 | printk(KERN_ERR "omap_dma: You need to stop the DMA channels before unlinking\n"); | ||
581 | dump_stack(); | ||
582 | } | ||
583 | |||
584 | dma_chan[lch_head].next_lch = -1; | ||
585 | } | ||
586 | |||
587 | |||
588 | static struct lcd_dma_info { | ||
589 | spinlock_t lock; | ||
590 | int reserved; | ||
591 | void (* callback)(u16 status, void *data); | ||
592 | void *cb_data; | ||
593 | |||
594 | int active; | ||
595 | unsigned long addr, size; | ||
596 | int rotate, data_type, xres, yres; | ||
597 | int vxres; | ||
598 | int mirror; | ||
599 | int xscale, yscale; | ||
600 | int ext_ctrl; | ||
601 | int src_port; | ||
602 | int single_transfer; | ||
603 | } lcd_dma; | ||
604 | |||
605 | void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, | ||
606 | int data_type) | ||
607 | { | ||
608 | lcd_dma.addr = addr; | ||
609 | lcd_dma.data_type = data_type; | ||
610 | lcd_dma.xres = fb_xres; | ||
611 | lcd_dma.yres = fb_yres; | ||
612 | } | ||
613 | |||
614 | void omap_set_lcd_dma_src_port(int port) | ||
615 | { | ||
616 | lcd_dma.src_port = port; | ||
617 | } | ||
618 | |||
619 | void omap_set_lcd_dma_ext_controller(int external) | ||
620 | { | ||
621 | lcd_dma.ext_ctrl = external; | ||
622 | } | ||
623 | |||
624 | void omap_set_lcd_dma_single_transfer(int single) | ||
625 | { | ||
626 | lcd_dma.single_transfer = single; | ||
627 | } | ||
628 | |||
629 | |||
630 | void omap_set_lcd_dma_b1_rotation(int rotate) | ||
631 | { | ||
632 | if (omap_dma_in_1510_mode()) { | ||
633 | printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); | ||
634 | BUG(); | ||
635 | return; | ||
636 | } | ||
637 | lcd_dma.rotate = rotate; | ||
638 | } | ||
639 | |||
640 | void omap_set_lcd_dma_b1_mirror(int mirror) | ||
641 | { | ||
642 | if (omap_dma_in_1510_mode()) { | ||
643 | printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); | ||
644 | BUG(); | ||
645 | } | ||
646 | lcd_dma.mirror = mirror; | ||
647 | } | ||
648 | |||
649 | void omap_set_lcd_dma_b1_vxres(unsigned long vxres) | ||
650 | { | ||
651 | if (omap_dma_in_1510_mode()) { | ||
652 | printk(KERN_ERR "DMA virtual resulotion is not supported " | ||
653 | "in 1510 mode\n"); | ||
654 | BUG(); | ||
655 | } | ||
656 | lcd_dma.vxres = vxres; | ||
657 | } | ||
658 | |||
659 | void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) | ||
660 | { | ||
661 | if (omap_dma_in_1510_mode()) { | ||
662 | printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); | ||
663 | BUG(); | ||
664 | } | ||
665 | lcd_dma.xscale = xscale; | ||
666 | lcd_dma.yscale = yscale; | ||
667 | } | ||
668 | |||
669 | static void set_b1_regs(void) | ||
670 | { | ||
671 | unsigned long top, bottom; | ||
672 | int es; | ||
673 | u16 w; | ||
674 | unsigned long en, fn; | ||
675 | long ei, fi; | ||
676 | unsigned long vxres; | ||
677 | unsigned int xscale, yscale; | ||
678 | |||
679 | switch (lcd_dma.data_type) { | ||
680 | case OMAP_DMA_DATA_TYPE_S8: | ||
681 | es = 1; | ||
682 | break; | ||
683 | case OMAP_DMA_DATA_TYPE_S16: | ||
684 | es = 2; | ||
685 | break; | ||
686 | case OMAP_DMA_DATA_TYPE_S32: | ||
687 | es = 4; | ||
688 | break; | ||
689 | default: | ||
690 | BUG(); | ||
691 | return; | ||
692 | } | ||
693 | |||
694 | vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres; | ||
695 | xscale = lcd_dma.xscale ? lcd_dma.xscale : 1; | ||
696 | yscale = lcd_dma.yscale ? lcd_dma.yscale : 1; | ||
697 | BUG_ON(vxres < lcd_dma.xres); | ||
698 | #define PIXADDR(x,y) (lcd_dma.addr + ((y) * vxres * yscale + (x) * xscale) * es) | ||
699 | #define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1) | ||
700 | switch (lcd_dma.rotate) { | ||
701 | case 0: | ||
702 | if (!lcd_dma.mirror) { | ||
703 | top = PIXADDR(0, 0); | ||
704 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
705 | /* 1510 DMA requires the bottom address to be 2 more | ||
706 | * than the actual last memory access location. */ | ||
707 | if (omap_dma_in_1510_mode() && | ||
708 | lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) | ||
709 | bottom += 2; | ||
710 | ei = PIXSTEP(0, 0, 1, 0); | ||
711 | fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1); | ||
712 | } else { | ||
713 | top = PIXADDR(lcd_dma.xres - 1, 0); | ||
714 | bottom = PIXADDR(0, lcd_dma.yres - 1); | ||
715 | ei = PIXSTEP(1, 0, 0, 0); | ||
716 | fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1); | ||
717 | } | ||
718 | en = lcd_dma.xres; | ||
719 | fn = lcd_dma.yres; | ||
720 | break; | ||
721 | case 90: | ||
722 | if (!lcd_dma.mirror) { | ||
723 | top = PIXADDR(0, lcd_dma.yres - 1); | ||
724 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | ||
725 | ei = PIXSTEP(0, 1, 0, 0); | ||
726 | fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1); | ||
727 | } else { | ||
728 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
729 | bottom = PIXADDR(0, 0); | ||
730 | ei = PIXSTEP(0, 1, 0, 0); | ||
731 | fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1); | ||
732 | } | ||
733 | en = lcd_dma.yres; | ||
734 | fn = lcd_dma.xres; | ||
735 | break; | ||
736 | case 180: | ||
737 | if (!lcd_dma.mirror) { | ||
738 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
739 | bottom = PIXADDR(0, 0); | ||
740 | ei = PIXSTEP(1, 0, 0, 0); | ||
741 | fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0); | ||
742 | } else { | ||
743 | top = PIXADDR(0, lcd_dma.yres - 1); | ||
744 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | ||
745 | ei = PIXSTEP(0, 0, 1, 0); | ||
746 | fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0); | ||
747 | } | ||
748 | en = lcd_dma.xres; | ||
749 | fn = lcd_dma.yres; | ||
750 | break; | ||
751 | case 270: | ||
752 | if (!lcd_dma.mirror) { | ||
753 | top = PIXADDR(lcd_dma.xres - 1, 0); | ||
754 | bottom = PIXADDR(0, lcd_dma.yres - 1); | ||
755 | ei = PIXSTEP(0, 0, 0, 1); | ||
756 | fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0); | ||
757 | } else { | ||
758 | top = PIXADDR(0, 0); | ||
759 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
760 | ei = PIXSTEP(0, 0, 0, 1); | ||
761 | fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0); | ||
762 | } | ||
763 | en = lcd_dma.yres; | ||
764 | fn = lcd_dma.xres; | ||
765 | break; | ||
766 | default: | ||
767 | BUG(); | ||
768 | return; /* Supress warning about uninitialized vars */ | ||
769 | } | ||
770 | |||
771 | if (omap_dma_in_1510_mode()) { | ||
772 | omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); | ||
773 | omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); | ||
774 | omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); | ||
775 | omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L); | ||
776 | |||
777 | return; | ||
778 | } | ||
779 | |||
780 | /* 1610 regs */ | ||
781 | omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U); | ||
782 | omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L); | ||
783 | omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U); | ||
784 | omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L); | ||
785 | |||
786 | omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1); | ||
787 | omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1); | ||
788 | |||
789 | w = omap_readw(OMAP1610_DMA_LCD_CSDP); | ||
790 | w &= ~0x03; | ||
791 | w |= lcd_dma.data_type; | ||
792 | omap_writew(w, OMAP1610_DMA_LCD_CSDP); | ||
793 | |||
794 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
795 | /* Always set the source port as SDRAM for now*/ | ||
796 | w &= ~(0x03 << 6); | ||
797 | if (lcd_dma.ext_ctrl) | ||
798 | w |= 1 << 8; | ||
799 | else | ||
800 | w &= ~(1 << 8); | ||
801 | if (lcd_dma.callback != NULL) | ||
802 | w |= 1 << 1; /* Block interrupt enable */ | ||
803 | else | ||
804 | w &= ~(1 << 1); | ||
805 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
806 | |||
807 | if (!(lcd_dma.rotate || lcd_dma.mirror || | ||
808 | lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) | ||
809 | return; | ||
810 | |||
811 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
812 | /* Set the double-indexed addressing mode */ | ||
813 | w |= (0x03 << 12); | ||
814 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
815 | |||
816 | omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); | ||
817 | omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); | ||
818 | omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); | ||
819 | } | ||
820 | |||
821 | static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
822 | { | ||
823 | u16 w; | ||
824 | |||
825 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
826 | if (unlikely(!(w & (1 << 3)))) { | ||
827 | printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); | ||
828 | return IRQ_NONE; | ||
829 | } | ||
830 | /* Ack the IRQ */ | ||
831 | w |= (1 << 3); | ||
832 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
833 | lcd_dma.active = 0; | ||
834 | if (lcd_dma.callback != NULL) | ||
835 | lcd_dma.callback(w, lcd_dma.cb_data); | ||
836 | |||
837 | return IRQ_HANDLED; | ||
838 | } | ||
839 | |||
840 | int omap_request_lcd_dma(void (* callback)(u16 status, void *data), | ||
841 | void *data) | ||
842 | { | ||
843 | spin_lock_irq(&lcd_dma.lock); | ||
844 | if (lcd_dma.reserved) { | ||
845 | spin_unlock_irq(&lcd_dma.lock); | ||
846 | printk(KERN_ERR "LCD DMA channel already reserved\n"); | ||
847 | BUG(); | ||
848 | return -EBUSY; | ||
849 | } | ||
850 | lcd_dma.reserved = 1; | ||
851 | spin_unlock_irq(&lcd_dma.lock); | ||
852 | lcd_dma.callback = callback; | ||
853 | lcd_dma.cb_data = data; | ||
854 | lcd_dma.active = 0; | ||
855 | lcd_dma.single_transfer = 0; | ||
856 | lcd_dma.rotate = 0; | ||
857 | lcd_dma.vxres = 0; | ||
858 | lcd_dma.mirror = 0; | ||
859 | lcd_dma.xscale = 0; | ||
860 | lcd_dma.yscale = 0; | ||
861 | lcd_dma.ext_ctrl = 0; | ||
862 | lcd_dma.src_port = 0; | ||
863 | |||
864 | return 0; | ||
865 | } | ||
866 | |||
867 | void omap_free_lcd_dma(void) | ||
868 | { | ||
869 | spin_lock(&lcd_dma.lock); | ||
870 | if (!lcd_dma.reserved) { | ||
871 | spin_unlock(&lcd_dma.lock); | ||
872 | printk(KERN_ERR "LCD DMA is not reserved\n"); | ||
873 | BUG(); | ||
874 | return; | ||
875 | } | ||
876 | if (!enable_1510_mode) | ||
877 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); | ||
878 | lcd_dma.reserved = 0; | ||
879 | spin_unlock(&lcd_dma.lock); | ||
880 | } | ||
881 | |||
882 | void omap_enable_lcd_dma(void) | ||
883 | { | ||
884 | u16 w; | ||
885 | |||
886 | /* Set the Enable bit only if an external controller is | ||
887 | * connected. Otherwise the OMAP internal controller will | ||
888 | * start the transfer when it gets enabled. | ||
889 | */ | ||
890 | if (enable_1510_mode || !lcd_dma.ext_ctrl) | ||
891 | return; | ||
892 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
893 | w |= 1 << 7; | ||
894 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
895 | lcd_dma.active = 1; | ||
896 | } | ||
897 | |||
898 | void omap_setup_lcd_dma(void) | ||
899 | { | ||
900 | BUG_ON(lcd_dma.active); | ||
901 | if (!enable_1510_mode) { | ||
902 | /* Set some reasonable defaults */ | ||
903 | omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); | ||
904 | omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); | ||
905 | omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); | ||
906 | } | ||
907 | set_b1_regs(); | ||
908 | if (!enable_1510_mode) { | ||
909 | u16 w; | ||
910 | |||
911 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
912 | /* If DMA was already active set the end_prog bit to have | ||
913 | * the programmed register set loaded into the active | ||
914 | * register set. | ||
915 | */ | ||
916 | w |= 1 << 11; /* End_prog */ | ||
917 | if (!lcd_dma.single_transfer) | ||
918 | w |= (3 << 8); /* Auto_init, repeat */ | ||
919 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
920 | } | ||
921 | } | ||
922 | |||
923 | void omap_stop_lcd_dma(void) | ||
924 | { | ||
925 | lcd_dma.active = 0; | ||
926 | if (!enable_1510_mode && lcd_dma.ext_ctrl) | ||
927 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~(1 << 7), | ||
928 | OMAP1610_DMA_LCD_CCR); | ||
929 | } | ||
930 | |||
931 | /* | ||
932 | * Clears any DMA state so the DMA engine is ready to restart with new buffers | ||
933 | * through omap_start_dma(). Any buffers in flight are discarded. | ||
934 | */ | ||
935 | void omap_clear_dma(int lch) | ||
936 | { | ||
937 | unsigned long flags; | ||
938 | int status; | ||
939 | |||
940 | local_irq_save(flags); | ||
941 | omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN, | ||
942 | OMAP_DMA_CCR(lch)); | ||
943 | status = OMAP_DMA_CSR(lch); /* clear pending interrupts */ | ||
944 | local_irq_restore(flags); | ||
945 | } | ||
946 | |||
947 | /* | ||
948 | * Returns current physical source address for the given DMA channel. | ||
949 | * If the channel is running the caller must disable interrupts prior calling | ||
950 | * this function and process the returned value before re-enabling interrupt to | ||
951 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
952 | * is a chance for CSSA_L register overflow inbetween the two reads resulting | ||
953 | * in incorrect return value. | ||
954 | */ | ||
955 | dma_addr_t omap_get_dma_src_pos(int lch) | ||
956 | { | ||
957 | return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) | | ||
958 | (OMAP_DMA_CSSA_U(lch) << 16)); | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * Returns current physical destination address for the given DMA channel. | ||
963 | * If the channel is running the caller must disable interrupts prior calling | ||
964 | * this function and process the returned value before re-enabling interrupt to | ||
965 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
966 | * is a chance for CDSA_L register overflow inbetween the two reads resulting | ||
967 | * in incorrect return value. | ||
968 | */ | ||
969 | dma_addr_t omap_get_dma_dst_pos(int lch) | ||
970 | { | ||
971 | return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) | | ||
972 | (OMAP_DMA_CDSA_U(lch) << 16)); | ||
973 | } | ||
974 | |||
975 | static int __init omap_init_dma(void) | ||
976 | { | ||
977 | int ch, r; | ||
978 | |||
979 | if (cpu_is_omap1510()) { | ||
980 | printk(KERN_INFO "DMA support for OMAP1510 initialized\n"); | ||
981 | dma_chan_count = 9; | ||
982 | enable_1510_mode = 1; | ||
983 | } else if (cpu_is_omap16xx() || cpu_is_omap730()) { | ||
984 | printk(KERN_INFO "OMAP DMA hardware version %d\n", | ||
985 | omap_readw(OMAP_DMA_HW_ID)); | ||
986 | printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", | ||
987 | (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L), | ||
988 | (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L), | ||
989 | omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3), | ||
990 | omap_readw(OMAP_DMA_CAPS_4)); | ||
991 | if (!enable_1510_mode) { | ||
992 | u16 w; | ||
993 | |||
994 | /* Disable OMAP 3.0/3.1 compatibility mode. */ | ||
995 | w = omap_readw(OMAP_DMA_GSCR); | ||
996 | w |= 1 << 3; | ||
997 | omap_writew(w, OMAP_DMA_GSCR); | ||
998 | dma_chan_count = 16; | ||
999 | } else | ||
1000 | dma_chan_count = 9; | ||
1001 | } else { | ||
1002 | dma_chan_count = 0; | ||
1003 | return 0; | ||
1004 | } | ||
1005 | |||
1006 | memset(&lcd_dma, 0, sizeof(lcd_dma)); | ||
1007 | spin_lock_init(&lcd_dma.lock); | ||
1008 | spin_lock_init(&dma_chan_lock); | ||
1009 | memset(&dma_chan, 0, sizeof(dma_chan)); | ||
1010 | |||
1011 | for (ch = 0; ch < dma_chan_count; ch++) { | ||
1012 | dma_chan[ch].dev_id = -1; | ||
1013 | dma_chan[ch].next_lch = -1; | ||
1014 | |||
1015 | if (ch >= 6 && enable_1510_mode) | ||
1016 | continue; | ||
1017 | |||
1018 | /* request_irq() doesn't like dev_id (ie. ch) being zero, | ||
1019 | * so we have to kludge around this. */ | ||
1020 | r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA", | ||
1021 | (void *) (ch + 1)); | ||
1022 | if (r != 0) { | ||
1023 | int i; | ||
1024 | |||
1025 | printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n", | ||
1026 | dma_irq[ch], r); | ||
1027 | for (i = 0; i < ch; i++) | ||
1028 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
1029 | return r; | ||
1030 | } | ||
1031 | } | ||
1032 | r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL); | ||
1033 | if (r != 0) { | ||
1034 | int i; | ||
1035 | |||
1036 | printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r); | ||
1037 | for (i = 0; i < dma_chan_count; i++) | ||
1038 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
1039 | return r; | ||
1040 | } | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | arch_initcall(omap_init_dma); | ||
1045 | |||
1046 | |||
1047 | EXPORT_SYMBOL(omap_get_dma_src_pos); | ||
1048 | EXPORT_SYMBOL(omap_get_dma_dst_pos); | ||
1049 | EXPORT_SYMBOL(omap_clear_dma); | ||
1050 | EXPORT_SYMBOL(omap_set_dma_priority); | ||
1051 | EXPORT_SYMBOL(omap_request_dma); | ||
1052 | EXPORT_SYMBOL(omap_free_dma); | ||
1053 | EXPORT_SYMBOL(omap_start_dma); | ||
1054 | EXPORT_SYMBOL(omap_stop_dma); | ||
1055 | EXPORT_SYMBOL(omap_enable_dma_irq); | ||
1056 | EXPORT_SYMBOL(omap_disable_dma_irq); | ||
1057 | |||
1058 | EXPORT_SYMBOL(omap_set_dma_transfer_params); | ||
1059 | EXPORT_SYMBOL(omap_set_dma_color_mode); | ||
1060 | |||
1061 | EXPORT_SYMBOL(omap_set_dma_src_params); | ||
1062 | EXPORT_SYMBOL(omap_set_dma_src_index); | ||
1063 | EXPORT_SYMBOL(omap_set_dma_src_data_pack); | ||
1064 | EXPORT_SYMBOL(omap_set_dma_src_burst_mode); | ||
1065 | |||
1066 | EXPORT_SYMBOL(omap_set_dma_dest_params); | ||
1067 | EXPORT_SYMBOL(omap_set_dma_dest_index); | ||
1068 | EXPORT_SYMBOL(omap_set_dma_dest_data_pack); | ||
1069 | EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); | ||
1070 | |||
1071 | EXPORT_SYMBOL(omap_dma_link_lch); | ||
1072 | EXPORT_SYMBOL(omap_dma_unlink_lch); | ||
1073 | |||
1074 | EXPORT_SYMBOL(omap_request_lcd_dma); | ||
1075 | EXPORT_SYMBOL(omap_free_lcd_dma); | ||
1076 | EXPORT_SYMBOL(omap_enable_lcd_dma); | ||
1077 | EXPORT_SYMBOL(omap_setup_lcd_dma); | ||
1078 | EXPORT_SYMBOL(omap_stop_lcd_dma); | ||
1079 | EXPORT_SYMBOL(omap_set_lcd_dma_b1); | ||
1080 | EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); | ||
1081 | EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); | ||
1082 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); | ||
1083 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); | ||
1084 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); | ||
1085 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); | ||
1086 | |||
diff --git a/arch/arm/mach-omap/fpga.c b/arch/arm/mach-omap/fpga.c new file mode 100644 index 000000000000..7c08f6c2e1d0 --- /dev/null +++ b/arch/arm/mach-omap/fpga.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/fpga.c | ||
3 | * | ||
4 | * Interrupt handler for OMAP-1510 Innovator FPGA | ||
5 | * | ||
6 | * Copyright (C) 2001 RidgeRun, Inc. | ||
7 | * Author: Greg Lonnon <glonnon@ridgerun.com> | ||
8 | * | ||
9 | * Copyright (C) 2002 MontaVista Software, Inc. | ||
10 | * | ||
11 | * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 | ||
12 | * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> | ||
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 version 2 as | ||
16 | * published by the Free Software Foundation. | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/errno.h> | ||
25 | |||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/irq.h> | ||
29 | #include <asm/mach/irq.h> | ||
30 | |||
31 | #include <asm/arch/fpga.h> | ||
32 | #include <asm/arch/gpio.h> | ||
33 | |||
34 | static void fpga_mask_irq(unsigned int irq) | ||
35 | { | ||
36 | irq -= OMAP1510_IH_FPGA_BASE; | ||
37 | |||
38 | if (irq < 8) | ||
39 | __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | ||
40 | & ~(1 << irq)), OMAP1510_FPGA_IMR_LO); | ||
41 | else if (irq < 16) | ||
42 | __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) | ||
43 | & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); | ||
44 | else | ||
45 | __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) | ||
46 | & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2); | ||
47 | } | ||
48 | |||
49 | |||
50 | static inline u32 get_fpga_unmasked_irqs(void) | ||
51 | { | ||
52 | return | ||
53 | ((__raw_readb(OMAP1510_FPGA_ISR_LO) & | ||
54 | __raw_readb(OMAP1510_FPGA_IMR_LO))) | | ||
55 | ((__raw_readb(OMAP1510_FPGA_ISR_HI) & | ||
56 | __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) | | ||
57 | ((__raw_readb(INNOVATOR_FPGA_ISR2) & | ||
58 | __raw_readb(INNOVATOR_FPGA_IMR2)) << 16); | ||
59 | } | ||
60 | |||
61 | |||
62 | static void fpga_ack_irq(unsigned int irq) | ||
63 | { | ||
64 | /* Don't need to explicitly ACK FPGA interrupts */ | ||
65 | } | ||
66 | |||
67 | static void fpga_unmask_irq(unsigned int irq) | ||
68 | { | ||
69 | irq -= OMAP1510_IH_FPGA_BASE; | ||
70 | |||
71 | if (irq < 8) | ||
72 | __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), | ||
73 | OMAP1510_FPGA_IMR_LO); | ||
74 | else if (irq < 16) | ||
75 | __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) | ||
76 | | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); | ||
77 | else | ||
78 | __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) | ||
79 | | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2); | ||
80 | } | ||
81 | |||
82 | static void fpga_mask_ack_irq(unsigned int irq) | ||
83 | { | ||
84 | fpga_mask_irq(irq); | ||
85 | fpga_ack_irq(irq); | ||
86 | } | ||
87 | |||
88 | void innovator_fpga_IRQ_demux(unsigned int irq, struct irqdesc *desc, | ||
89 | struct pt_regs *regs) | ||
90 | { | ||
91 | struct irqdesc *d; | ||
92 | u32 stat; | ||
93 | int fpga_irq; | ||
94 | |||
95 | stat = get_fpga_unmasked_irqs(); | ||
96 | |||
97 | if (!stat) | ||
98 | return; | ||
99 | |||
100 | for (fpga_irq = OMAP1510_IH_FPGA_BASE; | ||
101 | (fpga_irq < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; | ||
102 | fpga_irq++, stat >>= 1) { | ||
103 | if (stat & 1) { | ||
104 | d = irq_desc + fpga_irq; | ||
105 | d->handle(fpga_irq, d, regs); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static struct irqchip omap_fpga_irq_ack = { | ||
111 | .ack = fpga_mask_ack_irq, | ||
112 | .mask = fpga_mask_irq, | ||
113 | .unmask = fpga_unmask_irq, | ||
114 | }; | ||
115 | |||
116 | |||
117 | static struct irqchip omap_fpga_irq = { | ||
118 | .ack = fpga_ack_irq, | ||
119 | .mask = fpga_mask_irq, | ||
120 | .unmask = fpga_unmask_irq, | ||
121 | }; | ||
122 | |||
123 | /* | ||
124 | * All of the FPGA interrupt request inputs except for the touchscreen are | ||
125 | * edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive | ||
126 | * interrupts are acknowledged as a side-effect of reading the interrupt | ||
127 | * status register from the FPGA. The edge-sensitive interrupt inputs | ||
128 | * cause a problem with level interrupt requests, such as Ethernet. The | ||
129 | * problem occurs when a level interrupt request is asserted while its | ||
130 | * interrupt input is masked in the FPGA, which results in a missed | ||
131 | * interrupt. | ||
132 | * | ||
133 | * In an attempt to workaround the problem with missed interrupts, the | ||
134 | * mask_ack routine for all of the FPGA interrupts has been changed from | ||
135 | * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt | ||
136 | * being serviced is left unmasked. We can do this because the FPGA cascade | ||
137 | * interrupt is installed with the SA_INTERRUPT flag, which leaves all | ||
138 | * interrupts masked at the CPU while an FPGA interrupt handler executes. | ||
139 | * | ||
140 | * Limited testing indicates that this workaround appears to be effective | ||
141 | * for the smc9194 Ethernet driver used on the Innovator. It should work | ||
142 | * on other FPGA interrupts as well, but any drivers that explicitly mask | ||
143 | * interrupts at the interrupt controller via disable_irq/enable_irq | ||
144 | * could pose a problem. | ||
145 | */ | ||
146 | void omap1510_fpga_init_irq(void) | ||
147 | { | ||
148 | int i; | ||
149 | |||
150 | __raw_writeb(0, OMAP1510_FPGA_IMR_LO); | ||
151 | __raw_writeb(0, OMAP1510_FPGA_IMR_HI); | ||
152 | __raw_writeb(0, INNOVATOR_FPGA_IMR2); | ||
153 | |||
154 | for (i = OMAP1510_IH_FPGA_BASE; i < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS); i++) { | ||
155 | |||
156 | if (i == OMAP1510_INT_FPGA_TS) { | ||
157 | /* | ||
158 | * The touchscreen interrupt is level-sensitive, so | ||
159 | * we'll use the regular mask_ack routine for it. | ||
160 | */ | ||
161 | set_irq_chip(i, &omap_fpga_irq_ack); | ||
162 | } | ||
163 | else { | ||
164 | /* | ||
165 | * All FPGA interrupts except the touchscreen are | ||
166 | * edge-sensitive, so we won't mask them. | ||
167 | */ | ||
168 | set_irq_chip(i, &omap_fpga_irq); | ||
169 | } | ||
170 | |||
171 | set_irq_handler(i, do_edge_IRQ); | ||
172 | set_irq_flags(i, IRQF_VALID); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * The FPGA interrupt line is connected to GPIO13. Claim this pin for | ||
177 | * the ARM. | ||
178 | * | ||
179 | * NOTE: For general GPIO/MPUIO access and interrupts, please see | ||
180 | * gpio.[ch] | ||
181 | */ | ||
182 | omap_request_gpio(13); | ||
183 | omap_set_gpio_direction(13, 1); | ||
184 | omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); | ||
185 | set_irq_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); | ||
186 | } | ||
187 | |||
188 | EXPORT_SYMBOL(omap1510_fpga_init_irq); | ||
diff --git a/arch/arm/mach-omap/gpio.c b/arch/arm/mach-omap/gpio.c new file mode 100644 index 000000000000..9045dfd469ad --- /dev/null +++ b/arch/arm/mach-omap/gpio.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/gpio.c | ||
3 | * | ||
4 | * Support functions for OMAP GPIO | ||
5 | * | ||
6 | * Copyright (C) 2003 Nokia Corporation | ||
7 | * Written by Juha Yrjölä <juha.yrjola@nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | |||
21 | #include <asm/hardware.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/arch/irqs.h> | ||
24 | #include <asm/arch/gpio.h> | ||
25 | #include <asm/mach/irq.h> | ||
26 | |||
27 | #include <asm/io.h> | ||
28 | |||
29 | /* | ||
30 | * OMAP1510 GPIO registers | ||
31 | */ | ||
32 | #define OMAP1510_GPIO_BASE 0xfffce000 | ||
33 | #define OMAP1510_GPIO_DATA_INPUT 0x00 | ||
34 | #define OMAP1510_GPIO_DATA_OUTPUT 0x04 | ||
35 | #define OMAP1510_GPIO_DIR_CONTROL 0x08 | ||
36 | #define OMAP1510_GPIO_INT_CONTROL 0x0c | ||
37 | #define OMAP1510_GPIO_INT_MASK 0x10 | ||
38 | #define OMAP1510_GPIO_INT_STATUS 0x14 | ||
39 | #define OMAP1510_GPIO_PIN_CONTROL 0x18 | ||
40 | |||
41 | #define OMAP1510_IH_GPIO_BASE 64 | ||
42 | |||
43 | /* | ||
44 | * OMAP1610 specific GPIO registers | ||
45 | */ | ||
46 | #define OMAP1610_GPIO1_BASE 0xfffbe400 | ||
47 | #define OMAP1610_GPIO2_BASE 0xfffbec00 | ||
48 | #define OMAP1610_GPIO3_BASE 0xfffbb400 | ||
49 | #define OMAP1610_GPIO4_BASE 0xfffbbc00 | ||
50 | #define OMAP1610_GPIO_REVISION 0x0000 | ||
51 | #define OMAP1610_GPIO_SYSCONFIG 0x0010 | ||
52 | #define OMAP1610_GPIO_SYSSTATUS 0x0014 | ||
53 | #define OMAP1610_GPIO_IRQSTATUS1 0x0018 | ||
54 | #define OMAP1610_GPIO_IRQENABLE1 0x001c | ||
55 | #define OMAP1610_GPIO_DATAIN 0x002c | ||
56 | #define OMAP1610_GPIO_DATAOUT 0x0030 | ||
57 | #define OMAP1610_GPIO_DIRECTION 0x0034 | ||
58 | #define OMAP1610_GPIO_EDGE_CTRL1 0x0038 | ||
59 | #define OMAP1610_GPIO_EDGE_CTRL2 0x003c | ||
60 | #define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c | ||
61 | #define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 | ||
62 | #define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc | ||
63 | #define OMAP1610_GPIO_SET_DATAOUT 0x00f0 | ||
64 | |||
65 | /* | ||
66 | * OMAP730 specific GPIO registers | ||
67 | */ | ||
68 | #define OMAP730_GPIO1_BASE 0xfffbc000 | ||
69 | #define OMAP730_GPIO2_BASE 0xfffbc800 | ||
70 | #define OMAP730_GPIO3_BASE 0xfffbd000 | ||
71 | #define OMAP730_GPIO4_BASE 0xfffbd800 | ||
72 | #define OMAP730_GPIO5_BASE 0xfffbe000 | ||
73 | #define OMAP730_GPIO6_BASE 0xfffbe800 | ||
74 | #define OMAP730_GPIO_DATA_INPUT 0x00 | ||
75 | #define OMAP730_GPIO_DATA_OUTPUT 0x04 | ||
76 | #define OMAP730_GPIO_DIR_CONTROL 0x08 | ||
77 | #define OMAP730_GPIO_INT_CONTROL 0x0c | ||
78 | #define OMAP730_GPIO_INT_MASK 0x10 | ||
79 | #define OMAP730_GPIO_INT_STATUS 0x14 | ||
80 | |||
81 | #define OMAP_MPUIO_MASK (~OMAP_MAX_GPIO_LINES & 0xff) | ||
82 | |||
83 | struct gpio_bank { | ||
84 | u32 base; | ||
85 | u16 irq; | ||
86 | u16 virtual_irq_start; | ||
87 | u8 method; | ||
88 | u32 reserved_map; | ||
89 | spinlock_t lock; | ||
90 | }; | ||
91 | |||
92 | #define METHOD_MPUIO 0 | ||
93 | #define METHOD_GPIO_1510 1 | ||
94 | #define METHOD_GPIO_1610 2 | ||
95 | #define METHOD_GPIO_730 3 | ||
96 | |||
97 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
98 | static struct gpio_bank gpio_bank_1610[5] = { | ||
99 | { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO}, | ||
100 | { OMAP1610_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1610 }, | ||
101 | { OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 }, | ||
102 | { OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 }, | ||
103 | { OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 }, | ||
104 | }; | ||
105 | #endif | ||
106 | |||
107 | #ifdef CONFIG_ARCH_OMAP1510 | ||
108 | static struct gpio_bank gpio_bank_1510[2] = { | ||
109 | { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, | ||
110 | { OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1510 } | ||
111 | }; | ||
112 | #endif | ||
113 | |||
114 | #ifdef CONFIG_ARCH_OMAP730 | ||
115 | static struct gpio_bank gpio_bank_730[7] = { | ||
116 | { OMAP_MPUIO_BASE, INT_730_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, | ||
117 | { OMAP730_GPIO1_BASE, INT_730_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_730 }, | ||
118 | { OMAP730_GPIO2_BASE, INT_730_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_730 }, | ||
119 | { OMAP730_GPIO3_BASE, INT_730_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_730 }, | ||
120 | { OMAP730_GPIO4_BASE, INT_730_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_730 }, | ||
121 | { OMAP730_GPIO5_BASE, INT_730_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_730 }, | ||
122 | { OMAP730_GPIO6_BASE, INT_730_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_730 }, | ||
123 | }; | ||
124 | #endif | ||
125 | |||
126 | static struct gpio_bank *gpio_bank; | ||
127 | static int gpio_bank_count; | ||
128 | |||
129 | static inline struct gpio_bank *get_gpio_bank(int gpio) | ||
130 | { | ||
131 | #ifdef CONFIG_ARCH_OMAP1510 | ||
132 | if (cpu_is_omap1510()) { | ||
133 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
134 | return &gpio_bank[0]; | ||
135 | return &gpio_bank[1]; | ||
136 | } | ||
137 | #endif | ||
138 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
139 | if (cpu_is_omap16xx()) { | ||
140 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
141 | return &gpio_bank[0]; | ||
142 | return &gpio_bank[1 + (gpio >> 4)]; | ||
143 | } | ||
144 | #endif | ||
145 | #ifdef CONFIG_ARCH_OMAP730 | ||
146 | if (cpu_is_omap730()) { | ||
147 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
148 | return &gpio_bank[0]; | ||
149 | return &gpio_bank[1 + (gpio >> 5)]; | ||
150 | } | ||
151 | #endif | ||
152 | } | ||
153 | |||
154 | static inline int get_gpio_index(int gpio) | ||
155 | { | ||
156 | if (cpu_is_omap730()) | ||
157 | return gpio & 0x1f; | ||
158 | else | ||
159 | return gpio & 0x0f; | ||
160 | } | ||
161 | |||
162 | static inline int gpio_valid(int gpio) | ||
163 | { | ||
164 | if (gpio < 0) | ||
165 | return -1; | ||
166 | if (OMAP_GPIO_IS_MPUIO(gpio)) { | ||
167 | if ((gpio & OMAP_MPUIO_MASK) > 16) | ||
168 | return -1; | ||
169 | return 0; | ||
170 | } | ||
171 | #ifdef CONFIG_ARCH_OMAP1510 | ||
172 | if (cpu_is_omap1510() && gpio < 16) | ||
173 | return 0; | ||
174 | #endif | ||
175 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
176 | if ((cpu_is_omap16xx()) && gpio < 64) | ||
177 | return 0; | ||
178 | #endif | ||
179 | #ifdef CONFIG_ARCH_OMAP730 | ||
180 | if (cpu_is_omap730() && gpio < 192) | ||
181 | return 0; | ||
182 | #endif | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | static int check_gpio(int gpio) | ||
187 | { | ||
188 | if (unlikely(gpio_valid(gpio)) < 0) { | ||
189 | printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); | ||
190 | dump_stack(); | ||
191 | return -1; | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) | ||
197 | { | ||
198 | u32 reg = bank->base; | ||
199 | u32 l; | ||
200 | |||
201 | switch (bank->method) { | ||
202 | case METHOD_MPUIO: | ||
203 | reg += OMAP_MPUIO_IO_CNTL; | ||
204 | break; | ||
205 | case METHOD_GPIO_1510: | ||
206 | reg += OMAP1510_GPIO_DIR_CONTROL; | ||
207 | break; | ||
208 | case METHOD_GPIO_1610: | ||
209 | reg += OMAP1610_GPIO_DIRECTION; | ||
210 | break; | ||
211 | case METHOD_GPIO_730: | ||
212 | reg += OMAP730_GPIO_DIR_CONTROL; | ||
213 | break; | ||
214 | } | ||
215 | l = __raw_readl(reg); | ||
216 | if (is_input) | ||
217 | l |= 1 << gpio; | ||
218 | else | ||
219 | l &= ~(1 << gpio); | ||
220 | __raw_writel(l, reg); | ||
221 | } | ||
222 | |||
223 | void omap_set_gpio_direction(int gpio, int is_input) | ||
224 | { | ||
225 | struct gpio_bank *bank; | ||
226 | |||
227 | if (check_gpio(gpio) < 0) | ||
228 | return; | ||
229 | bank = get_gpio_bank(gpio); | ||
230 | spin_lock(&bank->lock); | ||
231 | _set_gpio_direction(bank, get_gpio_index(gpio), is_input); | ||
232 | spin_unlock(&bank->lock); | ||
233 | } | ||
234 | |||
235 | static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) | ||
236 | { | ||
237 | u32 reg = bank->base; | ||
238 | u32 l = 0; | ||
239 | |||
240 | switch (bank->method) { | ||
241 | case METHOD_MPUIO: | ||
242 | reg += OMAP_MPUIO_OUTPUT; | ||
243 | l = __raw_readl(reg); | ||
244 | if (enable) | ||
245 | l |= 1 << gpio; | ||
246 | else | ||
247 | l &= ~(1 << gpio); | ||
248 | break; | ||
249 | case METHOD_GPIO_1510: | ||
250 | reg += OMAP1510_GPIO_DATA_OUTPUT; | ||
251 | l = __raw_readl(reg); | ||
252 | if (enable) | ||
253 | l |= 1 << gpio; | ||
254 | else | ||
255 | l &= ~(1 << gpio); | ||
256 | break; | ||
257 | case METHOD_GPIO_1610: | ||
258 | if (enable) | ||
259 | reg += OMAP1610_GPIO_SET_DATAOUT; | ||
260 | else | ||
261 | reg += OMAP1610_GPIO_CLEAR_DATAOUT; | ||
262 | l = 1 << gpio; | ||
263 | break; | ||
264 | case METHOD_GPIO_730: | ||
265 | reg += OMAP730_GPIO_DATA_OUTPUT; | ||
266 | l = __raw_readl(reg); | ||
267 | if (enable) | ||
268 | l |= 1 << gpio; | ||
269 | else | ||
270 | l &= ~(1 << gpio); | ||
271 | break; | ||
272 | default: | ||
273 | BUG(); | ||
274 | return; | ||
275 | } | ||
276 | __raw_writel(l, reg); | ||
277 | } | ||
278 | |||
279 | void omap_set_gpio_dataout(int gpio, int enable) | ||
280 | { | ||
281 | struct gpio_bank *bank; | ||
282 | |||
283 | if (check_gpio(gpio) < 0) | ||
284 | return; | ||
285 | bank = get_gpio_bank(gpio); | ||
286 | spin_lock(&bank->lock); | ||
287 | _set_gpio_dataout(bank, get_gpio_index(gpio), enable); | ||
288 | spin_unlock(&bank->lock); | ||
289 | } | ||
290 | |||
291 | int omap_get_gpio_datain(int gpio) | ||
292 | { | ||
293 | struct gpio_bank *bank; | ||
294 | u32 reg; | ||
295 | |||
296 | if (check_gpio(gpio) < 0) | ||
297 | return -1; | ||
298 | bank = get_gpio_bank(gpio); | ||
299 | reg = bank->base; | ||
300 | switch (bank->method) { | ||
301 | case METHOD_MPUIO: | ||
302 | reg += OMAP_MPUIO_INPUT_LATCH; | ||
303 | break; | ||
304 | case METHOD_GPIO_1510: | ||
305 | reg += OMAP1510_GPIO_DATA_INPUT; | ||
306 | break; | ||
307 | case METHOD_GPIO_1610: | ||
308 | reg += OMAP1610_GPIO_DATAIN; | ||
309 | break; | ||
310 | case METHOD_GPIO_730: | ||
311 | reg += OMAP730_GPIO_DATA_INPUT; | ||
312 | break; | ||
313 | default: | ||
314 | BUG(); | ||
315 | return -1; | ||
316 | } | ||
317 | return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; | ||
318 | } | ||
319 | |||
320 | static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge) | ||
321 | { | ||
322 | u32 reg = bank->base; | ||
323 | u32 l; | ||
324 | |||
325 | switch (bank->method) { | ||
326 | case METHOD_MPUIO: | ||
327 | reg += OMAP_MPUIO_GPIO_INT_EDGE; | ||
328 | l = __raw_readl(reg); | ||
329 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
330 | l |= 1 << gpio; | ||
331 | else | ||
332 | l &= ~(1 << gpio); | ||
333 | __raw_writel(l, reg); | ||
334 | break; | ||
335 | case METHOD_GPIO_1510: | ||
336 | reg += OMAP1510_GPIO_INT_CONTROL; | ||
337 | l = __raw_readl(reg); | ||
338 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
339 | l |= 1 << gpio; | ||
340 | else | ||
341 | l &= ~(1 << gpio); | ||
342 | __raw_writel(l, reg); | ||
343 | break; | ||
344 | case METHOD_GPIO_1610: | ||
345 | edge &= 0x03; | ||
346 | if (gpio & 0x08) | ||
347 | reg += OMAP1610_GPIO_EDGE_CTRL2; | ||
348 | else | ||
349 | reg += OMAP1610_GPIO_EDGE_CTRL1; | ||
350 | gpio &= 0x07; | ||
351 | l = __raw_readl(reg); | ||
352 | l &= ~(3 << (gpio << 1)); | ||
353 | l |= edge << (gpio << 1); | ||
354 | __raw_writel(l, reg); | ||
355 | break; | ||
356 | case METHOD_GPIO_730: | ||
357 | reg += OMAP730_GPIO_INT_CONTROL; | ||
358 | l = __raw_readl(reg); | ||
359 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
360 | l |= 1 << gpio; | ||
361 | else | ||
362 | l &= ~(1 << gpio); | ||
363 | __raw_writel(l, reg); | ||
364 | break; | ||
365 | default: | ||
366 | BUG(); | ||
367 | return; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | void omap_set_gpio_edge_ctrl(int gpio, int edge) | ||
372 | { | ||
373 | struct gpio_bank *bank; | ||
374 | |||
375 | if (check_gpio(gpio) < 0) | ||
376 | return; | ||
377 | bank = get_gpio_bank(gpio); | ||
378 | spin_lock(&bank->lock); | ||
379 | _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge); | ||
380 | spin_unlock(&bank->lock); | ||
381 | } | ||
382 | |||
383 | |||
384 | static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio) | ||
385 | { | ||
386 | u32 reg = bank->base, l; | ||
387 | |||
388 | switch (bank->method) { | ||
389 | case METHOD_MPUIO: | ||
390 | l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE); | ||
391 | return (l & (1 << gpio)) ? | ||
392 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
393 | case METHOD_GPIO_1510: | ||
394 | l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL); | ||
395 | return (l & (1 << gpio)) ? | ||
396 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
397 | case METHOD_GPIO_1610: | ||
398 | if (gpio & 0x08) | ||
399 | reg += OMAP1610_GPIO_EDGE_CTRL2; | ||
400 | else | ||
401 | reg += OMAP1610_GPIO_EDGE_CTRL1; | ||
402 | return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03; | ||
403 | case METHOD_GPIO_730: | ||
404 | l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL); | ||
405 | return (l & (1 << gpio)) ? | ||
406 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
407 | default: | ||
408 | BUG(); | ||
409 | return -1; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) | ||
414 | { | ||
415 | u32 reg = bank->base; | ||
416 | |||
417 | switch (bank->method) { | ||
418 | case METHOD_MPUIO: | ||
419 | /* MPUIO irqstatus is reset by reading the status register, | ||
420 | * so do nothing here */ | ||
421 | return; | ||
422 | case METHOD_GPIO_1510: | ||
423 | reg += OMAP1510_GPIO_INT_STATUS; | ||
424 | break; | ||
425 | case METHOD_GPIO_1610: | ||
426 | reg += OMAP1610_GPIO_IRQSTATUS1; | ||
427 | break; | ||
428 | case METHOD_GPIO_730: | ||
429 | reg += OMAP730_GPIO_INT_STATUS; | ||
430 | break; | ||
431 | default: | ||
432 | BUG(); | ||
433 | return; | ||
434 | } | ||
435 | __raw_writel(gpio_mask, reg); | ||
436 | } | ||
437 | |||
438 | static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) | ||
439 | { | ||
440 | _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); | ||
441 | } | ||
442 | |||
443 | static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) | ||
444 | { | ||
445 | u32 reg = bank->base; | ||
446 | u32 l; | ||
447 | |||
448 | switch (bank->method) { | ||
449 | case METHOD_MPUIO: | ||
450 | reg += OMAP_MPUIO_GPIO_MASKIT; | ||
451 | l = __raw_readl(reg); | ||
452 | if (enable) | ||
453 | l &= ~(gpio_mask); | ||
454 | else | ||
455 | l |= gpio_mask; | ||
456 | break; | ||
457 | case METHOD_GPIO_1510: | ||
458 | reg += OMAP1510_GPIO_INT_MASK; | ||
459 | l = __raw_readl(reg); | ||
460 | if (enable) | ||
461 | l &= ~(gpio_mask); | ||
462 | else | ||
463 | l |= gpio_mask; | ||
464 | break; | ||
465 | case METHOD_GPIO_1610: | ||
466 | if (enable) | ||
467 | reg += OMAP1610_GPIO_SET_IRQENABLE1; | ||
468 | else | ||
469 | reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; | ||
470 | l = gpio_mask; | ||
471 | break; | ||
472 | case METHOD_GPIO_730: | ||
473 | reg += OMAP730_GPIO_INT_MASK; | ||
474 | l = __raw_readl(reg); | ||
475 | if (enable) | ||
476 | l &= ~(gpio_mask); | ||
477 | else | ||
478 | l |= gpio_mask; | ||
479 | break; | ||
480 | default: | ||
481 | BUG(); | ||
482 | return; | ||
483 | } | ||
484 | __raw_writel(l, reg); | ||
485 | } | ||
486 | |||
487 | static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) | ||
488 | { | ||
489 | _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); | ||
490 | } | ||
491 | |||
492 | int omap_request_gpio(int gpio) | ||
493 | { | ||
494 | struct gpio_bank *bank; | ||
495 | |||
496 | if (check_gpio(gpio) < 0) | ||
497 | return -EINVAL; | ||
498 | |||
499 | bank = get_gpio_bank(gpio); | ||
500 | spin_lock(&bank->lock); | ||
501 | if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) { | ||
502 | printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio); | ||
503 | dump_stack(); | ||
504 | spin_unlock(&bank->lock); | ||
505 | return -1; | ||
506 | } | ||
507 | bank->reserved_map |= (1 << get_gpio_index(gpio)); | ||
508 | #ifdef CONFIG_ARCH_OMAP1510 | ||
509 | if (bank->method == METHOD_GPIO_1510) { | ||
510 | u32 reg; | ||
511 | |||
512 | /* Claim the pin for the ARM */ | ||
513 | reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; | ||
514 | __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg); | ||
515 | } | ||
516 | #endif | ||
517 | spin_unlock(&bank->lock); | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | void omap_free_gpio(int gpio) | ||
523 | { | ||
524 | struct gpio_bank *bank; | ||
525 | |||
526 | if (check_gpio(gpio) < 0) | ||
527 | return; | ||
528 | bank = get_gpio_bank(gpio); | ||
529 | spin_lock(&bank->lock); | ||
530 | if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) { | ||
531 | printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio); | ||
532 | dump_stack(); | ||
533 | spin_unlock(&bank->lock); | ||
534 | return; | ||
535 | } | ||
536 | bank->reserved_map &= ~(1 << get_gpio_index(gpio)); | ||
537 | _set_gpio_direction(bank, get_gpio_index(gpio), 1); | ||
538 | _set_gpio_irqenable(bank, gpio, 0); | ||
539 | _clear_gpio_irqstatus(bank, gpio); | ||
540 | spin_unlock(&bank->lock); | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | * We need to unmask the GPIO bank interrupt as soon as possible to | ||
545 | * avoid missing GPIO interrupts for other lines in the bank. | ||
546 | * Then we need to mask-read-clear-unmask the triggered GPIO lines | ||
547 | * in the bank to avoid missing nested interrupts for a GPIO line. | ||
548 | * If we wait to unmask individual GPIO lines in the bank after the | ||
549 | * line's interrupt handler has been run, we may miss some nested | ||
550 | * interrupts. | ||
551 | */ | ||
552 | static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, | ||
553 | struct pt_regs *regs) | ||
554 | { | ||
555 | u32 isr_reg = 0; | ||
556 | u32 isr; | ||
557 | unsigned int gpio_irq; | ||
558 | struct gpio_bank *bank; | ||
559 | |||
560 | desc->chip->ack(irq); | ||
561 | |||
562 | bank = (struct gpio_bank *) desc->data; | ||
563 | if (bank->method == METHOD_MPUIO) | ||
564 | isr_reg = bank->base + OMAP_MPUIO_GPIO_INT; | ||
565 | #ifdef CONFIG_ARCH_OMAP1510 | ||
566 | if (bank->method == METHOD_GPIO_1510) | ||
567 | isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; | ||
568 | #endif | ||
569 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
570 | if (bank->method == METHOD_GPIO_1610) | ||
571 | isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; | ||
572 | #endif | ||
573 | #ifdef CONFIG_ARCH_OMAP730 | ||
574 | if (bank->method == METHOD_GPIO_730) | ||
575 | isr_reg = bank->base + OMAP730_GPIO_INT_STATUS; | ||
576 | #endif | ||
577 | |||
578 | isr = __raw_readl(isr_reg); | ||
579 | _enable_gpio_irqbank(bank, isr, 0); | ||
580 | _clear_gpio_irqbank(bank, isr); | ||
581 | _enable_gpio_irqbank(bank, isr, 1); | ||
582 | desc->chip->unmask(irq); | ||
583 | |||
584 | if (unlikely(!isr)) | ||
585 | return; | ||
586 | |||
587 | gpio_irq = bank->virtual_irq_start; | ||
588 | for (; isr != 0; isr >>= 1, gpio_irq++) { | ||
589 | struct irqdesc *d; | ||
590 | if (!(isr & 1)) | ||
591 | continue; | ||
592 | d = irq_desc + gpio_irq; | ||
593 | d->handle(gpio_irq, d, regs); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | static void gpio_ack_irq(unsigned int irq) | ||
598 | { | ||
599 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
600 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
601 | |||
602 | _clear_gpio_irqstatus(bank, gpio); | ||
603 | } | ||
604 | |||
605 | static void gpio_mask_irq(unsigned int irq) | ||
606 | { | ||
607 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
608 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
609 | |||
610 | _set_gpio_irqenable(bank, gpio, 0); | ||
611 | } | ||
612 | |||
613 | static void gpio_unmask_irq(unsigned int irq) | ||
614 | { | ||
615 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
616 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
617 | |||
618 | if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) { | ||
619 | printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n", | ||
620 | gpio); | ||
621 | _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE); | ||
622 | } | ||
623 | _set_gpio_irqenable(bank, gpio, 1); | ||
624 | } | ||
625 | |||
626 | static void mpuio_ack_irq(unsigned int irq) | ||
627 | { | ||
628 | /* The ISR is reset automatically, so do nothing here. */ | ||
629 | } | ||
630 | |||
631 | static void mpuio_mask_irq(unsigned int irq) | ||
632 | { | ||
633 | unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); | ||
634 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
635 | |||
636 | _set_gpio_irqenable(bank, gpio, 0); | ||
637 | } | ||
638 | |||
639 | static void mpuio_unmask_irq(unsigned int irq) | ||
640 | { | ||
641 | unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); | ||
642 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
643 | |||
644 | _set_gpio_irqenable(bank, gpio, 1); | ||
645 | } | ||
646 | |||
647 | static struct irqchip gpio_irq_chip = { | ||
648 | .ack = gpio_ack_irq, | ||
649 | .mask = gpio_mask_irq, | ||
650 | .unmask = gpio_unmask_irq, | ||
651 | }; | ||
652 | |||
653 | static struct irqchip mpuio_irq_chip = { | ||
654 | .ack = mpuio_ack_irq, | ||
655 | .mask = mpuio_mask_irq, | ||
656 | .unmask = mpuio_unmask_irq | ||
657 | }; | ||
658 | |||
659 | static int initialized = 0; | ||
660 | |||
661 | static int __init _omap_gpio_init(void) | ||
662 | { | ||
663 | int i; | ||
664 | struct gpio_bank *bank; | ||
665 | |||
666 | initialized = 1; | ||
667 | |||
668 | #ifdef CONFIG_ARCH_OMAP1510 | ||
669 | if (cpu_is_omap1510()) { | ||
670 | printk(KERN_INFO "OMAP1510 GPIO hardware\n"); | ||
671 | gpio_bank_count = 2; | ||
672 | gpio_bank = gpio_bank_1510; | ||
673 | } | ||
674 | #endif | ||
675 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
676 | if (cpu_is_omap16xx()) { | ||
677 | int rev; | ||
678 | |||
679 | gpio_bank_count = 5; | ||
680 | gpio_bank = gpio_bank_1610; | ||
681 | rev = omap_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION); | ||
682 | printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", | ||
683 | (rev >> 4) & 0x0f, rev & 0x0f); | ||
684 | } | ||
685 | #endif | ||
686 | #ifdef CONFIG_ARCH_OMAP730 | ||
687 | if (cpu_is_omap730()) { | ||
688 | printk(KERN_INFO "OMAP730 GPIO hardware\n"); | ||
689 | gpio_bank_count = 7; | ||
690 | gpio_bank = gpio_bank_730; | ||
691 | } | ||
692 | #endif | ||
693 | for (i = 0; i < gpio_bank_count; i++) { | ||
694 | int j, gpio_count = 16; | ||
695 | |||
696 | bank = &gpio_bank[i]; | ||
697 | bank->reserved_map = 0; | ||
698 | bank->base = IO_ADDRESS(bank->base); | ||
699 | spin_lock_init(&bank->lock); | ||
700 | if (bank->method == METHOD_MPUIO) { | ||
701 | omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT); | ||
702 | } | ||
703 | #ifdef CONFIG_ARCH_OMAP1510 | ||
704 | if (bank->method == METHOD_GPIO_1510) { | ||
705 | __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); | ||
706 | __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); | ||
707 | } | ||
708 | #endif | ||
709 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
710 | if (bank->method == METHOD_GPIO_1610) { | ||
711 | __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); | ||
712 | __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); | ||
713 | } | ||
714 | #endif | ||
715 | #ifdef CONFIG_ARCH_OMAP730 | ||
716 | if (bank->method == METHOD_GPIO_730) { | ||
717 | __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK); | ||
718 | __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS); | ||
719 | |||
720 | gpio_count = 32; /* 730 has 32-bit GPIOs */ | ||
721 | } | ||
722 | #endif | ||
723 | for (j = bank->virtual_irq_start; | ||
724 | j < bank->virtual_irq_start + gpio_count; j++) { | ||
725 | if (bank->method == METHOD_MPUIO) | ||
726 | set_irq_chip(j, &mpuio_irq_chip); | ||
727 | else | ||
728 | set_irq_chip(j, &gpio_irq_chip); | ||
729 | set_irq_handler(j, do_simple_IRQ); | ||
730 | set_irq_flags(j, IRQF_VALID); | ||
731 | } | ||
732 | set_irq_chained_handler(bank->irq, gpio_irq_handler); | ||
733 | set_irq_data(bank->irq, bank); | ||
734 | } | ||
735 | |||
736 | /* Enable system clock for GPIO module. | ||
737 | * The CAM_CLK_CTRL *is* really the right place. */ | ||
738 | if (cpu_is_omap1610() || cpu_is_omap1710()) | ||
739 | omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); | ||
740 | |||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * This may get called early from board specific init | ||
746 | */ | ||
747 | int omap_gpio_init(void) | ||
748 | { | ||
749 | if (!initialized) | ||
750 | return _omap_gpio_init(); | ||
751 | else | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | EXPORT_SYMBOL(omap_request_gpio); | ||
756 | EXPORT_SYMBOL(omap_free_gpio); | ||
757 | EXPORT_SYMBOL(omap_set_gpio_direction); | ||
758 | EXPORT_SYMBOL(omap_set_gpio_dataout); | ||
759 | EXPORT_SYMBOL(omap_get_gpio_datain); | ||
760 | EXPORT_SYMBOL(omap_set_gpio_edge_ctrl); | ||
761 | |||
762 | arch_initcall(omap_gpio_init); | ||
diff --git a/arch/arm/mach-omap/irq.c b/arch/arm/mach-omap/irq.c new file mode 100644 index 000000000000..f01c99266a86 --- /dev/null +++ b/arch/arm/mach-omap/irq.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/irq.c | ||
3 | * | ||
4 | * Interrupt handler for all OMAP boards | ||
5 | * | ||
6 | * Copyright (C) 2004 Nokia Corporation | ||
7 | * Written by Tony Lindgren <tony@atomide.com> | ||
8 | * Major cleanups by Juha Yrjölä <juha.yrjola@nokia.com> | ||
9 | * | ||
10 | * Completely re-written to support various OMAP chips with bank specific | ||
11 | * interrupt handlers. | ||
12 | * | ||
13 | * Some snippets of the code taken from the older OMAP interrupt handler | ||
14 | * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon <glonnon@ridgerun.com> | ||
15 | * | ||
16 | * GPIO interrupt handler moved to gpio.c by Juha Yrjola | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or modify it | ||
19 | * under the terms of the GNU General Public License as published by the | ||
20 | * Free Software Foundation; either version 2 of the License, or (at your | ||
21 | * option) any later version. | ||
22 | * | ||
23 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
24 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
26 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
28 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
29 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
30 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * You should have received a copy of the GNU General Public License along | ||
35 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
36 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
37 | */ | ||
38 | |||
39 | #include <linux/config.h> | ||
40 | #include <linux/init.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/sched.h> | ||
43 | #include <linux/interrupt.h> | ||
44 | #include <linux/ptrace.h> | ||
45 | |||
46 | #include <asm/hardware.h> | ||
47 | #include <asm/irq.h> | ||
48 | #include <asm/mach/irq.h> | ||
49 | #include <asm/arch/gpio.h> | ||
50 | |||
51 | #include <asm/io.h> | ||
52 | |||
53 | #define IRQ_BANK(irq) ((irq) >> 5) | ||
54 | #define IRQ_BIT(irq) ((irq) & 0x1f) | ||
55 | |||
56 | struct omap_irq_bank { | ||
57 | unsigned long base_reg; | ||
58 | unsigned long trigger_map; | ||
59 | }; | ||
60 | |||
61 | static unsigned int irq_bank_count = 0; | ||
62 | static struct omap_irq_bank *irq_banks; | ||
63 | |||
64 | static inline unsigned int irq_bank_readl(int bank, int offset) | ||
65 | { | ||
66 | return omap_readl(irq_banks[bank].base_reg + offset); | ||
67 | } | ||
68 | |||
69 | static inline void irq_bank_writel(unsigned long value, int bank, int offset) | ||
70 | { | ||
71 | omap_writel(value, irq_banks[bank].base_reg + offset); | ||
72 | } | ||
73 | |||
74 | static void omap_ack_irq(unsigned int irq) | ||
75 | { | ||
76 | if (irq > 31) | ||
77 | omap_writel(0x1, OMAP_IH2_BASE + IRQ_CONTROL_REG_OFFSET); | ||
78 | |||
79 | omap_writel(0x1, OMAP_IH1_BASE + IRQ_CONTROL_REG_OFFSET); | ||
80 | } | ||
81 | |||
82 | static void omap_mask_irq(unsigned int irq) | ||
83 | { | ||
84 | int bank = IRQ_BANK(irq); | ||
85 | u32 l; | ||
86 | |||
87 | l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); | ||
88 | l |= 1 << IRQ_BIT(irq); | ||
89 | omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); | ||
90 | } | ||
91 | |||
92 | static void omap_unmask_irq(unsigned int irq) | ||
93 | { | ||
94 | int bank = IRQ_BANK(irq); | ||
95 | u32 l; | ||
96 | |||
97 | l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); | ||
98 | l &= ~(1 << IRQ_BIT(irq)); | ||
99 | omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); | ||
100 | } | ||
101 | |||
102 | static void omap_mask_ack_irq(unsigned int irq) | ||
103 | { | ||
104 | omap_mask_irq(irq); | ||
105 | omap_ack_irq(irq); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Allows tuning the IRQ type and priority | ||
110 | * | ||
111 | * NOTE: There is currently no OMAP fiq handler for Linux. Read the | ||
112 | * mailing list threads on FIQ handlers if you are planning to | ||
113 | * add a FIQ handler for OMAP. | ||
114 | */ | ||
115 | static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) | ||
116 | { | ||
117 | signed int bank; | ||
118 | unsigned long val, offset; | ||
119 | |||
120 | bank = IRQ_BANK(irq); | ||
121 | /* FIQ is only available on bank 0 interrupts */ | ||
122 | fiq = bank ? 0 : (fiq & 0x1); | ||
123 | val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1); | ||
124 | offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4; | ||
125 | irq_bank_writel(val, bank, offset); | ||
126 | } | ||
127 | |||
128 | #ifdef CONFIG_ARCH_OMAP730 | ||
129 | static struct omap_irq_bank omap730_irq_banks[] = { | ||
130 | { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, | ||
131 | { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, | ||
132 | { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, | ||
133 | }; | ||
134 | #endif | ||
135 | |||
136 | #ifdef CONFIG_ARCH_OMAP1510 | ||
137 | static struct omap_irq_bank omap1510_irq_banks[] = { | ||
138 | { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, | ||
139 | { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, | ||
140 | }; | ||
141 | #endif | ||
142 | |||
143 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
144 | |||
145 | static struct omap_irq_bank omap1610_irq_banks[] = { | ||
146 | { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, | ||
147 | { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, | ||
148 | { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xfffff7ff }, | ||
149 | { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, | ||
150 | }; | ||
151 | #endif | ||
152 | |||
153 | static struct irqchip omap_irq_chip = { | ||
154 | .ack = omap_mask_ack_irq, | ||
155 | .mask = omap_mask_irq, | ||
156 | .unmask = omap_unmask_irq, | ||
157 | }; | ||
158 | |||
159 | void __init omap_init_irq(void) | ||
160 | { | ||
161 | int i, j; | ||
162 | |||
163 | #ifdef CONFIG_ARCH_OMAP730 | ||
164 | if (cpu_is_omap730()) { | ||
165 | irq_banks = omap730_irq_banks; | ||
166 | irq_bank_count = ARRAY_SIZE(omap730_irq_banks); | ||
167 | } | ||
168 | #endif | ||
169 | #ifdef CONFIG_ARCH_OMAP1510 | ||
170 | if (cpu_is_omap1510()) { | ||
171 | irq_banks = omap1510_irq_banks; | ||
172 | irq_bank_count = ARRAY_SIZE(omap1510_irq_banks); | ||
173 | } | ||
174 | #endif | ||
175 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
176 | if (cpu_is_omap16xx()) { | ||
177 | irq_banks = omap1610_irq_banks; | ||
178 | irq_bank_count = ARRAY_SIZE(omap1610_irq_banks); | ||
179 | } | ||
180 | #endif | ||
181 | printk("Total of %i interrupts in %i interrupt banks\n", | ||
182 | irq_bank_count * 32, irq_bank_count); | ||
183 | |||
184 | /* Mask and clear all interrupts */ | ||
185 | for (i = 0; i < irq_bank_count; i++) { | ||
186 | irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET); | ||
187 | irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET); | ||
188 | } | ||
189 | |||
190 | /* Clear any pending interrupts */ | ||
191 | irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET); | ||
192 | irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET); | ||
193 | |||
194 | /* Enable interrupts in global mask */ | ||
195 | if (cpu_is_omap730()) { | ||
196 | irq_bank_writel(0x0, 0, IRQ_GMR_REG_OFFSET); | ||
197 | } | ||
198 | |||
199 | /* Install the interrupt handlers for each bank */ | ||
200 | for (i = 0; i < irq_bank_count; i++) { | ||
201 | for (j = i * 32; j < (i + 1) * 32; j++) { | ||
202 | int irq_trigger; | ||
203 | |||
204 | irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j); | ||
205 | omap_irq_set_cfg(j, 0, 0, irq_trigger); | ||
206 | |||
207 | set_irq_chip(j, &omap_irq_chip); | ||
208 | set_irq_handler(j, do_level_IRQ); | ||
209 | set_irq_flags(j, IRQF_VALID); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | /* Unmask level 2 handler */ | ||
214 | if (cpu_is_omap730()) { | ||
215 | omap_unmask_irq(INT_730_IH2_IRQ); | ||
216 | } else { | ||
217 | omap_unmask_irq(INT_IH2_IRQ); | ||
218 | } | ||
219 | } | ||
diff --git a/arch/arm/mach-omap/leds-h2p2-debug.c b/arch/arm/mach-omap/leds-h2p2-debug.c new file mode 100644 index 000000000000..6e98290cca5c --- /dev/null +++ b/arch/arm/mach-omap/leds-h2p2-debug.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/leds-h2p2-debug.c | ||
3 | * | ||
4 | * Copyright 2003 by Texas Instruments Incorporated | ||
5 | * | ||
6 | * There are 16 LEDs on the debug board (all green); four may be used | ||
7 | * for logical 'green', 'amber', 'red', and 'blue' (after "claiming"). | ||
8 | * | ||
9 | * The "surfer" expansion board and H2 sample board also have two-color | ||
10 | * green+red LEDs (in parallel), used here for timer and idle indicators. | ||
11 | */ | ||
12 | #include <linux/config.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel_stat.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/version.h> | ||
17 | |||
18 | #include <asm/io.h> | ||
19 | #include <asm/hardware.h> | ||
20 | #include <asm/leds.h> | ||
21 | #include <asm/system.h> | ||
22 | |||
23 | #include <asm/arch/fpga.h> | ||
24 | #include <asm/arch/gpio.h> | ||
25 | |||
26 | #include "leds.h" | ||
27 | |||
28 | |||
29 | #define GPIO_LED_RED 3 | ||
30 | #define GPIO_LED_GREEN OMAP_MPUIO(4) | ||
31 | |||
32 | |||
33 | #define LED_STATE_ENABLED 0x01 | ||
34 | #define LED_STATE_CLAIMED 0x02 | ||
35 | #define LED_TIMER_ON 0x04 | ||
36 | |||
37 | #define GPIO_IDLE GPIO_LED_GREEN | ||
38 | #define GPIO_TIMER GPIO_LED_RED | ||
39 | |||
40 | |||
41 | void h2p2_dbg_leds_event(led_event_t evt) | ||
42 | { | ||
43 | unsigned long flags; | ||
44 | |||
45 | static struct h2p2_dbg_fpga __iomem *fpga; | ||
46 | static u16 led_state, hw_led_state; | ||
47 | |||
48 | local_irq_save(flags); | ||
49 | |||
50 | if (!(led_state & LED_STATE_ENABLED) && evt != led_start) | ||
51 | goto done; | ||
52 | |||
53 | switch (evt) { | ||
54 | case led_start: | ||
55 | if (!fpga) | ||
56 | fpga = ioremap(H2P2_DBG_FPGA_START, | ||
57 | H2P2_DBG_FPGA_SIZE); | ||
58 | if (fpga) { | ||
59 | led_state |= LED_STATE_ENABLED; | ||
60 | __raw_writew(~0, &fpga->leds); | ||
61 | } | ||
62 | break; | ||
63 | |||
64 | case led_stop: | ||
65 | case led_halted: | ||
66 | /* all leds off during suspend or shutdown */ | ||
67 | omap_set_gpio_dataout(GPIO_TIMER, 0); | ||
68 | omap_set_gpio_dataout(GPIO_IDLE, 0); | ||
69 | __raw_writew(~0, &fpga->leds); | ||
70 | led_state &= ~LED_STATE_ENABLED; | ||
71 | if (evt == led_halted) { | ||
72 | iounmap(fpga); | ||
73 | fpga = NULL; | ||
74 | } | ||
75 | goto done; | ||
76 | |||
77 | case led_claim: | ||
78 | led_state |= LED_STATE_CLAIMED; | ||
79 | hw_led_state = 0; | ||
80 | break; | ||
81 | |||
82 | case led_release: | ||
83 | led_state &= ~LED_STATE_CLAIMED; | ||
84 | break; | ||
85 | |||
86 | #ifdef CONFIG_LEDS_TIMER | ||
87 | case led_timer: | ||
88 | led_state ^= LED_TIMER_ON; | ||
89 | omap_set_gpio_dataout(GPIO_TIMER, led_state & LED_TIMER_ON); | ||
90 | goto done; | ||
91 | #endif | ||
92 | |||
93 | #ifdef CONFIG_LEDS_CPU | ||
94 | case led_idle_start: | ||
95 | omap_set_gpio_dataout(GPIO_IDLE, 1); | ||
96 | goto done; | ||
97 | |||
98 | case led_idle_end: | ||
99 | omap_set_gpio_dataout(GPIO_IDLE, 0); | ||
100 | goto done; | ||
101 | #endif | ||
102 | |||
103 | case led_green_on: | ||
104 | hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; | ||
105 | break; | ||
106 | case led_green_off: | ||
107 | hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; | ||
108 | break; | ||
109 | |||
110 | case led_amber_on: | ||
111 | hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; | ||
112 | break; | ||
113 | case led_amber_off: | ||
114 | hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; | ||
115 | break; | ||
116 | |||
117 | case led_red_on: | ||
118 | hw_led_state |= H2P2_DBG_FPGA_LED_RED; | ||
119 | break; | ||
120 | case led_red_off: | ||
121 | hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; | ||
122 | break; | ||
123 | |||
124 | case led_blue_on: | ||
125 | hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; | ||
126 | break; | ||
127 | case led_blue_off: | ||
128 | hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; | ||
129 | break; | ||
130 | |||
131 | default: | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | |||
136 | /* | ||
137 | * Actually burn the LEDs | ||
138 | */ | ||
139 | if (led_state & LED_STATE_CLAIMED) | ||
140 | __raw_writew(~hw_led_state, &fpga->leds); | ||
141 | |||
142 | done: | ||
143 | local_irq_restore(flags); | ||
144 | } | ||
diff --git a/arch/arm/mach-omap/leds-innovator.c b/arch/arm/mach-omap/leds-innovator.c new file mode 100644 index 000000000000..8043b7d0f66e --- /dev/null +++ b/arch/arm/mach-omap/leds-innovator.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/leds-innovator.c | ||
3 | */ | ||
4 | #include <linux/config.h> | ||
5 | #include <linux/init.h> | ||
6 | |||
7 | #include <asm/hardware.h> | ||
8 | #include <asm/leds.h> | ||
9 | #include <asm/system.h> | ||
10 | |||
11 | #include "leds.h" | ||
12 | |||
13 | |||
14 | #define LED_STATE_ENABLED 1 | ||
15 | #define LED_STATE_CLAIMED 2 | ||
16 | |||
17 | static unsigned int led_state; | ||
18 | static unsigned int hw_led_state; | ||
19 | |||
20 | void innovator_leds_event(led_event_t evt) | ||
21 | { | ||
22 | unsigned long flags; | ||
23 | |||
24 | local_irq_save(flags); | ||
25 | |||
26 | switch (evt) { | ||
27 | case led_start: | ||
28 | hw_led_state = 0; | ||
29 | led_state = LED_STATE_ENABLED; | ||
30 | break; | ||
31 | |||
32 | case led_stop: | ||
33 | led_state &= ~LED_STATE_ENABLED; | ||
34 | hw_led_state = 0; | ||
35 | break; | ||
36 | |||
37 | case led_claim: | ||
38 | led_state |= LED_STATE_CLAIMED; | ||
39 | hw_led_state = 0; | ||
40 | break; | ||
41 | |||
42 | case led_release: | ||
43 | led_state &= ~LED_STATE_CLAIMED; | ||
44 | hw_led_state = 0; | ||
45 | break; | ||
46 | |||
47 | #ifdef CONFIG_LEDS_TIMER | ||
48 | case led_timer: | ||
49 | if (!(led_state & LED_STATE_CLAIMED)) | ||
50 | hw_led_state ^= 0; | ||
51 | break; | ||
52 | #endif | ||
53 | |||
54 | #ifdef CONFIG_LEDS_CPU | ||
55 | case led_idle_start: | ||
56 | if (!(led_state & LED_STATE_CLAIMED)) | ||
57 | hw_led_state |= 0; | ||
58 | break; | ||
59 | |||
60 | case led_idle_end: | ||
61 | if (!(led_state & LED_STATE_CLAIMED)) | ||
62 | hw_led_state &= ~0; | ||
63 | break; | ||
64 | #endif | ||
65 | |||
66 | case led_halted: | ||
67 | break; | ||
68 | |||
69 | case led_green_on: | ||
70 | if (led_state & LED_STATE_CLAIMED) | ||
71 | hw_led_state &= ~0; | ||
72 | break; | ||
73 | |||
74 | case led_green_off: | ||
75 | if (led_state & LED_STATE_CLAIMED) | ||
76 | hw_led_state |= 0; | ||
77 | break; | ||
78 | |||
79 | case led_amber_on: | ||
80 | break; | ||
81 | |||
82 | case led_amber_off: | ||
83 | break; | ||
84 | |||
85 | case led_red_on: | ||
86 | if (led_state & LED_STATE_CLAIMED) | ||
87 | hw_led_state &= ~0; | ||
88 | break; | ||
89 | |||
90 | case led_red_off: | ||
91 | if (led_state & LED_STATE_CLAIMED) | ||
92 | hw_led_state |= 0; | ||
93 | break; | ||
94 | |||
95 | default: | ||
96 | break; | ||
97 | } | ||
98 | |||
99 | if (led_state & LED_STATE_ENABLED) | ||
100 | ; | ||
101 | |||
102 | local_irq_restore(flags); | ||
103 | } | ||
diff --git a/arch/arm/mach-omap/leds-osk.c b/arch/arm/mach-omap/leds-osk.c new file mode 100644 index 000000000000..f5177f430793 --- /dev/null +++ b/arch/arm/mach-omap/leds-osk.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/leds-osk.c | ||
3 | * | ||
4 | * LED driver for OSK, and optionally Mistral QVGA, boards | ||
5 | */ | ||
6 | #include <linux/config.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/workqueue.h> | ||
9 | |||
10 | #include <asm/hardware.h> | ||
11 | #include <asm/leds.h> | ||
12 | #include <asm/system.h> | ||
13 | |||
14 | #include <asm/arch/gpio.h> | ||
15 | #include <asm/arch/tps65010.h> | ||
16 | |||
17 | #include "leds.h" | ||
18 | |||
19 | |||
20 | #define LED_STATE_ENABLED (1 << 0) | ||
21 | #define LED_STATE_CLAIMED (1 << 1) | ||
22 | static u8 led_state; | ||
23 | |||
24 | #define GREEN_LED (1 << 0) /* TPS65010 LED1 */ | ||
25 | #define AMBER_LED (1 << 1) /* TPS65010 LED2 */ | ||
26 | #define RED_LED (1 << 2) /* TPS65010 GPIO2 */ | ||
27 | #define TIMER_LED (1 << 3) /* Mistral board */ | ||
28 | #define IDLE_LED (1 << 4) /* Mistral board */ | ||
29 | static u8 hw_led_state; | ||
30 | |||
31 | |||
32 | /* TPS65010 leds are changed using i2c -- from a task context. | ||
33 | * Using one of these for the "idle" LED would be impractical... | ||
34 | */ | ||
35 | #define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED) | ||
36 | |||
37 | static u8 tps_leds_change; | ||
38 | |||
39 | static void tps_work(void *unused) | ||
40 | { | ||
41 | for (;;) { | ||
42 | u8 leds; | ||
43 | |||
44 | local_irq_disable(); | ||
45 | leds = tps_leds_change; | ||
46 | tps_leds_change = 0; | ||
47 | local_irq_enable(); | ||
48 | |||
49 | if (!leds) | ||
50 | break; | ||
51 | |||
52 | /* careful: the set_led() value is on/off/blink */ | ||
53 | if (leds & GREEN_LED) | ||
54 | tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED)); | ||
55 | if (leds & AMBER_LED) | ||
56 | tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED)); | ||
57 | |||
58 | /* the gpio led doesn't have that issue */ | ||
59 | if (leds & RED_LED) | ||
60 | tps65010_set_gpio_out_value(GPIO2, | ||
61 | !(hw_led_state & RED_LED)); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static DECLARE_WORK(work, tps_work, NULL); | ||
66 | |||
67 | #ifdef CONFIG_FB_OMAP | ||
68 | |||
69 | /* For now, all system indicators require the Mistral board, since that | ||
70 | * LED can be manipulated without a task context. This LED is either red, | ||
71 | * or green, but not both; it can't give the full "disco led" effect. | ||
72 | */ | ||
73 | |||
74 | #define GPIO_LED_RED 3 | ||
75 | #define GPIO_LED_GREEN OMAP_MPUIO(4) | ||
76 | |||
77 | static void mistral_setled(void) | ||
78 | { | ||
79 | int red = 0; | ||
80 | int green = 0; | ||
81 | |||
82 | if (hw_led_state & TIMER_LED) | ||
83 | red = 1; | ||
84 | else if (hw_led_state & IDLE_LED) | ||
85 | green = 1; | ||
86 | // else both sides are disabled | ||
87 | |||
88 | omap_set_gpio_dataout(GPIO_LED_GREEN, green); | ||
89 | omap_set_gpio_dataout(GPIO_LED_RED, red); | ||
90 | } | ||
91 | |||
92 | #endif | ||
93 | |||
94 | void osk_leds_event(led_event_t evt) | ||
95 | { | ||
96 | unsigned long flags; | ||
97 | u16 leds; | ||
98 | |||
99 | local_irq_save(flags); | ||
100 | |||
101 | if (!(led_state & LED_STATE_ENABLED) && evt != led_start) | ||
102 | goto done; | ||
103 | |||
104 | leds = hw_led_state; | ||
105 | switch (evt) { | ||
106 | case led_start: | ||
107 | led_state |= LED_STATE_ENABLED; | ||
108 | hw_led_state = 0; | ||
109 | leds = ~0; | ||
110 | break; | ||
111 | |||
112 | case led_halted: | ||
113 | case led_stop: | ||
114 | led_state &= ~LED_STATE_ENABLED; | ||
115 | hw_led_state = 0; | ||
116 | // NOTE: work may still be pending!! | ||
117 | break; | ||
118 | |||
119 | case led_claim: | ||
120 | led_state |= LED_STATE_CLAIMED; | ||
121 | hw_led_state = 0; | ||
122 | leds = ~0; | ||
123 | break; | ||
124 | |||
125 | case led_release: | ||
126 | led_state &= ~LED_STATE_CLAIMED; | ||
127 | hw_led_state = 0; | ||
128 | break; | ||
129 | |||
130 | #ifdef CONFIG_FB_OMAP | ||
131 | |||
132 | #ifdef CONFIG_LEDS_TIMER | ||
133 | case led_timer: | ||
134 | hw_led_state ^= TIMER_LED; | ||
135 | mistral_setled(); | ||
136 | break; | ||
137 | #endif | ||
138 | |||
139 | #ifdef CONFIG_LEDS_CPU | ||
140 | case led_idle_start: | ||
141 | hw_led_state |= IDLE_LED; | ||
142 | mistral_setled(); | ||
143 | break; | ||
144 | |||
145 | case led_idle_end: | ||
146 | hw_led_state &= ~IDLE_LED; | ||
147 | mistral_setled(); | ||
148 | break; | ||
149 | #endif | ||
150 | |||
151 | #endif /* CONFIG_FB_OMAP */ | ||
152 | |||
153 | /* "green" == tps LED1 (leftmost, normally power-good) | ||
154 | * works only with DC adapter, not on battery power! | ||
155 | */ | ||
156 | case led_green_on: | ||
157 | if (led_state & LED_STATE_CLAIMED) | ||
158 | hw_led_state |= GREEN_LED; | ||
159 | break; | ||
160 | case led_green_off: | ||
161 | if (led_state & LED_STATE_CLAIMED) | ||
162 | hw_led_state &= ~GREEN_LED; | ||
163 | break; | ||
164 | |||
165 | /* "amber" == tps LED2 (middle) */ | ||
166 | case led_amber_on: | ||
167 | if (led_state & LED_STATE_CLAIMED) | ||
168 | hw_led_state |= AMBER_LED; | ||
169 | break; | ||
170 | case led_amber_off: | ||
171 | if (led_state & LED_STATE_CLAIMED) | ||
172 | hw_led_state &= ~AMBER_LED; | ||
173 | break; | ||
174 | |||
175 | /* "red" == LED on tps gpio3 (rightmost) */ | ||
176 | case led_red_on: | ||
177 | if (led_state & LED_STATE_CLAIMED) | ||
178 | hw_led_state |= RED_LED; | ||
179 | break; | ||
180 | case led_red_off: | ||
181 | if (led_state & LED_STATE_CLAIMED) | ||
182 | hw_led_state &= ~RED_LED; | ||
183 | break; | ||
184 | |||
185 | default: | ||
186 | break; | ||
187 | } | ||
188 | |||
189 | leds ^= hw_led_state; | ||
190 | leds &= TPS_LEDS; | ||
191 | if (leds && (led_state & LED_STATE_CLAIMED)) { | ||
192 | tps_leds_change |= leds; | ||
193 | schedule_work(&work); | ||
194 | } | ||
195 | |||
196 | done: | ||
197 | local_irq_restore(flags); | ||
198 | } | ||
diff --git a/arch/arm/mach-omap/leds.c b/arch/arm/mach-omap/leds.c new file mode 100644 index 000000000000..8ab21fe98e1b --- /dev/null +++ b/arch/arm/mach-omap/leds.c | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/leds.c | ||
3 | * | ||
4 | * OMAP LEDs dispatcher | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/init.h> | ||
8 | |||
9 | #include <asm/leds.h> | ||
10 | #include <asm/mach-types.h> | ||
11 | |||
12 | #include <asm/arch/gpio.h> | ||
13 | #include <asm/arch/mux.h> | ||
14 | |||
15 | #include "leds.h" | ||
16 | |||
17 | static int __init | ||
18 | omap_leds_init(void) | ||
19 | { | ||
20 | if (machine_is_omap_innovator()) | ||
21 | leds_event = innovator_leds_event; | ||
22 | |||
23 | else if (machine_is_omap_h2() || machine_is_omap_perseus2()) | ||
24 | leds_event = h2p2_dbg_leds_event; | ||
25 | |||
26 | else if (machine_is_omap_osk()) | ||
27 | leds_event = osk_leds_event; | ||
28 | |||
29 | else | ||
30 | return -1; | ||
31 | |||
32 | if (machine_is_omap_h2() | ||
33 | || machine_is_omap_perseus2() | ||
34 | || machine_is_omap_osk()) { | ||
35 | |||
36 | /* LED1/LED2 pins can be used as GPIO (as done here), or by | ||
37 | * the LPG (works even in deep sleep!), to drive a bicolor | ||
38 | * LED on the H2 sample board, and another on the H2/P2 | ||
39 | * "surfer" expansion board. | ||
40 | * | ||
41 | * The same pins drive a LED on the OSK Mistral board, but | ||
42 | * that's a different kind of LED (just one color at a time). | ||
43 | */ | ||
44 | omap_cfg_reg(P18_1610_GPIO3); | ||
45 | if (omap_request_gpio(3) == 0) | ||
46 | omap_set_gpio_direction(3, 0); | ||
47 | else | ||
48 | printk(KERN_WARNING "LED: can't get GPIO3/red?\n"); | ||
49 | |||
50 | omap_cfg_reg(MPUIO4); | ||
51 | if (omap_request_gpio(OMAP_MPUIO(4)) == 0) | ||
52 | omap_set_gpio_direction(OMAP_MPUIO(4), 0); | ||
53 | else | ||
54 | printk(KERN_WARNING "LED: can't get MPUIO4/green?\n"); | ||
55 | } | ||
56 | |||
57 | leds_event(led_start); | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | __initcall(omap_leds_init); | ||
diff --git a/arch/arm/mach-omap/leds.h b/arch/arm/mach-omap/leds.h new file mode 100644 index 000000000000..a1e9fedc376c --- /dev/null +++ b/arch/arm/mach-omap/leds.h | |||
@@ -0,0 +1,3 @@ | |||
1 | extern void innovator_leds_event(led_event_t evt); | ||
2 | extern void h2p2_dbg_leds_event(led_event_t evt); | ||
3 | extern void osk_leds_event(led_event_t evt); | ||
diff --git a/arch/arm/mach-omap/mcbsp.c b/arch/arm/mach-omap/mcbsp.c new file mode 100644 index 000000000000..7c4ad7713091 --- /dev/null +++ b/arch/arm/mach-omap/mcbsp.c | |||
@@ -0,0 +1,685 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/omap/mcbsp.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Nokia Corporation | ||
5 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> | ||
6 | * | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Multichannel mode not supported. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/wait.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/err.h> | ||
22 | |||
23 | #include <asm/delay.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq.h> | ||
26 | |||
27 | #include <asm/arch/dma.h> | ||
28 | #include <asm/arch/mux.h> | ||
29 | #include <asm/arch/irqs.h> | ||
30 | #include <asm/arch/mcbsp.h> | ||
31 | |||
32 | #include <asm/hardware/clock.h> | ||
33 | |||
34 | #ifdef CONFIG_MCBSP_DEBUG | ||
35 | #define DBG(x...) printk(x) | ||
36 | #else | ||
37 | #define DBG(x...) do { } while (0) | ||
38 | #endif | ||
39 | |||
40 | struct omap_mcbsp { | ||
41 | u32 io_base; | ||
42 | u8 id; | ||
43 | u8 free; | ||
44 | omap_mcbsp_word_length rx_word_length; | ||
45 | omap_mcbsp_word_length tx_word_length; | ||
46 | |||
47 | /* IRQ based TX/RX */ | ||
48 | int rx_irq; | ||
49 | int tx_irq; | ||
50 | |||
51 | /* DMA stuff */ | ||
52 | u8 dma_rx_sync; | ||
53 | short dma_rx_lch; | ||
54 | u8 dma_tx_sync; | ||
55 | short dma_tx_lch; | ||
56 | |||
57 | /* Completion queues */ | ||
58 | struct completion tx_irq_completion; | ||
59 | struct completion rx_irq_completion; | ||
60 | struct completion tx_dma_completion; | ||
61 | struct completion rx_dma_completion; | ||
62 | |||
63 | spinlock_t lock; | ||
64 | }; | ||
65 | |||
66 | static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; | ||
67 | static struct clk *mcbsp_dsp_ck = 0; | ||
68 | static struct clk *mcbsp_api_ck = 0; | ||
69 | |||
70 | |||
71 | static void omap_mcbsp_dump_reg(u8 id) | ||
72 | { | ||
73 | DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); | ||
74 | DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); | ||
75 | DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); | ||
76 | DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); | ||
77 | DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); | ||
78 | DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); | ||
79 | DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); | ||
80 | DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); | ||
81 | DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); | ||
82 | DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); | ||
83 | DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); | ||
84 | DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); | ||
85 | DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); | ||
86 | DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); | ||
87 | DBG("***********************\n"); | ||
88 | } | ||
89 | |||
90 | |||
91 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
92 | { | ||
93 | struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); | ||
94 | |||
95 | DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); | ||
96 | |||
97 | complete(&mcbsp_tx->tx_irq_completion); | ||
98 | return IRQ_HANDLED; | ||
99 | } | ||
100 | |||
101 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
102 | { | ||
103 | struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); | ||
104 | |||
105 | DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); | ||
106 | |||
107 | complete(&mcbsp_rx->rx_irq_completion); | ||
108 | return IRQ_HANDLED; | ||
109 | } | ||
110 | |||
111 | |||
112 | static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) | ||
113 | { | ||
114 | struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); | ||
115 | |||
116 | DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); | ||
117 | |||
118 | /* We can free the channels */ | ||
119 | omap_free_dma(mcbsp_dma_tx->dma_tx_lch); | ||
120 | mcbsp_dma_tx->dma_tx_lch = -1; | ||
121 | |||
122 | complete(&mcbsp_dma_tx->tx_dma_completion); | ||
123 | } | ||
124 | |||
125 | static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) | ||
126 | { | ||
127 | struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); | ||
128 | |||
129 | DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); | ||
130 | |||
131 | /* We can free the channels */ | ||
132 | omap_free_dma(mcbsp_dma_rx->dma_rx_lch); | ||
133 | mcbsp_dma_rx->dma_rx_lch = -1; | ||
134 | |||
135 | complete(&mcbsp_dma_rx->rx_dma_completion); | ||
136 | } | ||
137 | |||
138 | |||
139 | /* | ||
140 | * omap_mcbsp_config simply write a config to the | ||
141 | * appropriate McBSP. | ||
142 | * You either call this function or set the McBSP registers | ||
143 | * by yourself before calling omap_mcbsp_start(). | ||
144 | */ | ||
145 | |||
146 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) | ||
147 | { | ||
148 | u32 io_base = mcbsp[id].io_base; | ||
149 | |||
150 | DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); | ||
151 | |||
152 | /* We write the given config */ | ||
153 | OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); | ||
154 | OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); | ||
155 | OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); | ||
156 | OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); | ||
157 | OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); | ||
158 | OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); | ||
159 | OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); | ||
160 | OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); | ||
161 | OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); | ||
162 | OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); | ||
163 | OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); | ||
164 | } | ||
165 | |||
166 | |||
167 | |||
168 | static int omap_mcbsp_check(unsigned int id) | ||
169 | { | ||
170 | if (cpu_is_omap730()) { | ||
171 | if (id > OMAP_MAX_MCBSP_COUNT - 1) { | ||
172 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
173 | return -1; | ||
174 | } | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { | ||
179 | if (id > OMAP_MAX_MCBSP_COUNT) { | ||
180 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
181 | return -1; | ||
182 | } | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | return -1; | ||
187 | } | ||
188 | |||
189 | #define EN_XORPCK 1 | ||
190 | #define DSP_RSTCT2 0xe1008014 | ||
191 | |||
192 | static void omap_mcbsp_dsp_request(void) | ||
193 | { | ||
194 | if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { | ||
195 | omap_writew((omap_readw(ARM_RSTCT1) | (1 << 1) | (1 << 2)), | ||
196 | ARM_RSTCT1); | ||
197 | clk_enable(mcbsp_dsp_ck); | ||
198 | clk_enable(mcbsp_api_ck); | ||
199 | |||
200 | /* enable 12MHz clock to mcbsp 1 & 3 */ | ||
201 | __raw_writew(__raw_readw(DSP_IDLECT2) | (1 << EN_XORPCK), | ||
202 | DSP_IDLECT2); | ||
203 | __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, | ||
204 | DSP_RSTCT2); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | static void omap_mcbsp_dsp_free(void) | ||
209 | { | ||
210 | /* Useless for now */ | ||
211 | } | ||
212 | |||
213 | |||
214 | int omap_mcbsp_request(unsigned int id) | ||
215 | { | ||
216 | int err; | ||
217 | |||
218 | if (omap_mcbsp_check(id) < 0) | ||
219 | return -EINVAL; | ||
220 | |||
221 | /* | ||
222 | * On 1510, 1610 and 1710, McBSP1 and McBSP3 | ||
223 | * are DSP public peripherals. | ||
224 | */ | ||
225 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
226 | omap_mcbsp_dsp_request(); | ||
227 | |||
228 | spin_lock(&mcbsp[id].lock); | ||
229 | if (!mcbsp[id].free) { | ||
230 | printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); | ||
231 | spin_unlock(&mcbsp[id].lock); | ||
232 | return -1; | ||
233 | } | ||
234 | |||
235 | mcbsp[id].free = 0; | ||
236 | spin_unlock(&mcbsp[id].lock); | ||
237 | |||
238 | /* We need to get IRQs here */ | ||
239 | err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
240 | "McBSP", | ||
241 | (void *) (&mcbsp[id])); | ||
242 | if (err != 0) { | ||
243 | printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", | ||
244 | mcbsp[id].tx_irq, mcbsp[id].id); | ||
245 | return err; | ||
246 | } | ||
247 | |||
248 | init_completion(&(mcbsp[id].tx_irq_completion)); | ||
249 | |||
250 | |||
251 | err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
252 | "McBSP", | ||
253 | (void *) (&mcbsp[id])); | ||
254 | if (err != 0) { | ||
255 | printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", | ||
256 | mcbsp[id].rx_irq, mcbsp[id].id); | ||
257 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
258 | return err; | ||
259 | } | ||
260 | |||
261 | init_completion(&(mcbsp[id].rx_irq_completion)); | ||
262 | return 0; | ||
263 | |||
264 | } | ||
265 | |||
266 | void omap_mcbsp_free(unsigned int id) | ||
267 | { | ||
268 | if (omap_mcbsp_check(id) < 0) | ||
269 | return; | ||
270 | |||
271 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
272 | omap_mcbsp_dsp_free(); | ||
273 | |||
274 | spin_lock(&mcbsp[id].lock); | ||
275 | if (mcbsp[id].free) { | ||
276 | printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); | ||
277 | spin_unlock(&mcbsp[id].lock); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | mcbsp[id].free = 1; | ||
282 | spin_unlock(&mcbsp[id].lock); | ||
283 | |||
284 | /* Free IRQs */ | ||
285 | free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); | ||
286 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
287 | } | ||
288 | |||
289 | /* | ||
290 | * Here we start the McBSP, by enabling the sample | ||
291 | * generator, both transmitter and receivers, | ||
292 | * and the frame sync. | ||
293 | */ | ||
294 | void omap_mcbsp_start(unsigned int id) | ||
295 | { | ||
296 | u32 io_base; | ||
297 | u16 w; | ||
298 | |||
299 | if (omap_mcbsp_check(id) < 0) | ||
300 | return; | ||
301 | |||
302 | io_base = mcbsp[id].io_base; | ||
303 | |||
304 | mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); | ||
305 | mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); | ||
306 | |||
307 | /* Start the sample generator */ | ||
308 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
309 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||
310 | |||
311 | /* Enable transmitter and receiver */ | ||
312 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
313 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | ||
314 | |||
315 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
316 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | ||
317 | |||
318 | udelay(100); | ||
319 | |||
320 | /* Start frame sync */ | ||
321 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
322 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||
323 | |||
324 | /* Dump McBSP Regs */ | ||
325 | omap_mcbsp_dump_reg(id); | ||
326 | |||
327 | } | ||
328 | |||
329 | void omap_mcbsp_stop(unsigned int id) | ||
330 | { | ||
331 | u32 io_base; | ||
332 | u16 w; | ||
333 | |||
334 | if (omap_mcbsp_check(id) < 0) | ||
335 | return; | ||
336 | |||
337 | io_base = mcbsp[id].io_base; | ||
338 | |||
339 | /* Reset transmitter */ | ||
340 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
341 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | ||
342 | |||
343 | /* Reset receiver */ | ||
344 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
345 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | ||
346 | |||
347 | /* Reset the sample rate generator */ | ||
348 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
349 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||
350 | } | ||
351 | |||
352 | |||
353 | /* | ||
354 | * IRQ based word transmission. | ||
355 | */ | ||
356 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | ||
357 | { | ||
358 | u32 io_base; | ||
359 | omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; | ||
360 | |||
361 | if (omap_mcbsp_check(id) < 0) | ||
362 | return; | ||
363 | |||
364 | io_base = mcbsp[id].io_base; | ||
365 | |||
366 | wait_for_completion(&(mcbsp[id].tx_irq_completion)); | ||
367 | |||
368 | if (word_length > OMAP_MCBSP_WORD_16) | ||
369 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | ||
370 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | ||
371 | } | ||
372 | |||
373 | u32 omap_mcbsp_recv_word(unsigned int id) | ||
374 | { | ||
375 | u32 io_base; | ||
376 | u16 word_lsb, word_msb = 0; | ||
377 | omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; | ||
378 | |||
379 | if (omap_mcbsp_check(id) < 0) | ||
380 | return -EINVAL; | ||
381 | |||
382 | io_base = mcbsp[id].io_base; | ||
383 | |||
384 | wait_for_completion(&(mcbsp[id].rx_irq_completion)); | ||
385 | |||
386 | if (word_length > OMAP_MCBSP_WORD_16) | ||
387 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | ||
388 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | ||
389 | |||
390 | return (word_lsb | (word_msb << 16)); | ||
391 | } | ||
392 | |||
393 | |||
394 | /* | ||
395 | * Simple DMA based buffer rx/tx routines. | ||
396 | * Nothing fancy, just a single buffer tx/rx through DMA. | ||
397 | * The DMA resources are released once the transfer is done. | ||
398 | * For anything fancier, you should use your own customized DMA | ||
399 | * routines and callbacks. | ||
400 | */ | ||
401 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
402 | { | ||
403 | int dma_tx_ch; | ||
404 | |||
405 | if (omap_mcbsp_check(id) < 0) | ||
406 | return -EINVAL; | ||
407 | |||
408 | if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, | ||
409 | &mcbsp[id], | ||
410 | &dma_tx_ch)) { | ||
411 | printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); | ||
412 | return -EAGAIN; | ||
413 | } | ||
414 | mcbsp[id].dma_tx_lch = dma_tx_ch; | ||
415 | |||
416 | DBG("TX DMA on channel %d\n", dma_tx_ch); | ||
417 | |||
418 | init_completion(&(mcbsp[id].tx_dma_completion)); | ||
419 | |||
420 | omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, | ||
421 | OMAP_DMA_DATA_TYPE_S16, | ||
422 | length >> 1, 1, | ||
423 | OMAP_DMA_SYNC_ELEMENT); | ||
424 | |||
425 | omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, | ||
426 | OMAP_DMA_PORT_TIPB, | ||
427 | OMAP_DMA_AMODE_CONSTANT, | ||
428 | mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); | ||
429 | |||
430 | omap_set_dma_src_params(mcbsp[id].dma_tx_lch, | ||
431 | OMAP_DMA_PORT_EMIFF, | ||
432 | OMAP_DMA_AMODE_POST_INC, | ||
433 | buffer); | ||
434 | |||
435 | omap_start_dma(mcbsp[id].dma_tx_lch); | ||
436 | wait_for_completion(&(mcbsp[id].tx_dma_completion)); | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | |||
441 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
442 | { | ||
443 | int dma_rx_ch; | ||
444 | |||
445 | if (omap_mcbsp_check(id) < 0) | ||
446 | return -EINVAL; | ||
447 | |||
448 | if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, | ||
449 | &mcbsp[id], | ||
450 | &dma_rx_ch)) { | ||
451 | printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); | ||
452 | return -EAGAIN; | ||
453 | } | ||
454 | mcbsp[id].dma_rx_lch = dma_rx_ch; | ||
455 | |||
456 | DBG("RX DMA on channel %d\n", dma_rx_ch); | ||
457 | |||
458 | init_completion(&(mcbsp[id].rx_dma_completion)); | ||
459 | |||
460 | omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, | ||
461 | OMAP_DMA_DATA_TYPE_S16, | ||
462 | length >> 1, 1, | ||
463 | OMAP_DMA_SYNC_ELEMENT); | ||
464 | |||
465 | omap_set_dma_src_params(mcbsp[id].dma_rx_lch, | ||
466 | OMAP_DMA_PORT_TIPB, | ||
467 | OMAP_DMA_AMODE_CONSTANT, | ||
468 | mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); | ||
469 | |||
470 | omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, | ||
471 | OMAP_DMA_PORT_EMIFF, | ||
472 | OMAP_DMA_AMODE_POST_INC, | ||
473 | buffer); | ||
474 | |||
475 | omap_start_dma(mcbsp[id].dma_rx_lch); | ||
476 | wait_for_completion(&(mcbsp[id].rx_dma_completion)); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | |||
481 | /* | ||
482 | * SPI wrapper. | ||
483 | * Since SPI setup is much simpler than the generic McBSP one, | ||
484 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | ||
485 | * Once this is done, you can call omap_mcbsp_start(). | ||
486 | */ | ||
487 | void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) | ||
488 | { | ||
489 | struct omap_mcbsp_reg_cfg mcbsp_cfg; | ||
490 | |||
491 | if (omap_mcbsp_check(id) < 0) | ||
492 | return; | ||
493 | |||
494 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | ||
495 | |||
496 | /* SPI has only one frame */ | ||
497 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | ||
498 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | ||
499 | |||
500 | /* Clock stop mode */ | ||
501 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) | ||
502 | mcbsp_cfg.spcr1 |= (1 << 12); | ||
503 | else | ||
504 | mcbsp_cfg.spcr1 |= (3 << 11); | ||
505 | |||
506 | /* Set clock parities */ | ||
507 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
508 | mcbsp_cfg.pcr0 |= CLKRP; | ||
509 | else | ||
510 | mcbsp_cfg.pcr0 &= ~CLKRP; | ||
511 | |||
512 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
513 | mcbsp_cfg.pcr0 &= ~CLKXP; | ||
514 | else | ||
515 | mcbsp_cfg.pcr0 |= CLKXP; | ||
516 | |||
517 | /* Set SCLKME to 0 and CLKSM to 1 */ | ||
518 | mcbsp_cfg.pcr0 &= ~SCLKME; | ||
519 | mcbsp_cfg.srgr2 |= CLKSM; | ||
520 | |||
521 | /* Set FSXP */ | ||
522 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | ||
523 | mcbsp_cfg.pcr0 &= ~FSXP; | ||
524 | else | ||
525 | mcbsp_cfg.pcr0 |= FSXP; | ||
526 | |||
527 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | ||
528 | mcbsp_cfg.pcr0 |= CLKXM; | ||
529 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); | ||
530 | mcbsp_cfg.pcr0 |= FSXM; | ||
531 | mcbsp_cfg.srgr2 &= ~FSGM; | ||
532 | mcbsp_cfg.xcr2 |= XDATDLY(1); | ||
533 | mcbsp_cfg.rcr2 |= RDATDLY(1); | ||
534 | } | ||
535 | else { | ||
536 | mcbsp_cfg.pcr0 &= ~CLKXM; | ||
537 | mcbsp_cfg.srgr1 |= CLKGDV(1); | ||
538 | mcbsp_cfg.pcr0 &= ~FSXM; | ||
539 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | ||
540 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | ||
541 | } | ||
542 | |||
543 | mcbsp_cfg.xcr2 &= ~XPHASE; | ||
544 | mcbsp_cfg.rcr2 &= ~RPHASE; | ||
545 | |||
546 | omap_mcbsp_config(id, &mcbsp_cfg); | ||
547 | } | ||
548 | |||
549 | |||
550 | /* | ||
551 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
552 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
553 | */ | ||
554 | struct omap_mcbsp_info { | ||
555 | u32 virt_base; | ||
556 | u8 dma_rx_sync, dma_tx_sync; | ||
557 | u16 rx_irq, tx_irq; | ||
558 | }; | ||
559 | |||
560 | #ifdef CONFIG_ARCH_OMAP730 | ||
561 | static const struct omap_mcbsp_info mcbsp_730[] = { | ||
562 | [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), | ||
563 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
564 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
565 | .rx_irq = INT_730_McBSP1RX, | ||
566 | .tx_irq = INT_730_McBSP1TX }, | ||
567 | [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), | ||
568 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
569 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
570 | .rx_irq = INT_730_McBSP2RX, | ||
571 | .tx_irq = INT_730_McBSP2TX }, | ||
572 | }; | ||
573 | #endif | ||
574 | |||
575 | #ifdef CONFIG_ARCH_OMAP1510 | ||
576 | static const struct omap_mcbsp_info mcbsp_1510[] = { | ||
577 | [0] = { .virt_base = OMAP1510_MCBSP1_BASE, | ||
578 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
579 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
580 | .rx_irq = INT_McBSP1RX, | ||
581 | .tx_irq = INT_McBSP1TX }, | ||
582 | [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), | ||
583 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
584 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
585 | .rx_irq = INT_1510_SPI_RX, | ||
586 | .tx_irq = INT_1510_SPI_TX }, | ||
587 | [2] = { .virt_base = OMAP1510_MCBSP3_BASE, | ||
588 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
589 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
590 | .rx_irq = INT_McBSP3RX, | ||
591 | .tx_irq = INT_McBSP3TX }, | ||
592 | }; | ||
593 | #endif | ||
594 | |||
595 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
596 | static const struct omap_mcbsp_info mcbsp_1610[] = { | ||
597 | [0] = { .virt_base = OMAP1610_MCBSP1_BASE, | ||
598 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
599 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
600 | .rx_irq = INT_McBSP1RX, | ||
601 | .tx_irq = INT_McBSP1TX }, | ||
602 | [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), | ||
603 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
604 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
605 | .rx_irq = INT_1610_McBSP2_RX, | ||
606 | .tx_irq = INT_1610_McBSP2_TX }, | ||
607 | [2] = { .virt_base = OMAP1610_MCBSP3_BASE, | ||
608 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
609 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
610 | .rx_irq = INT_McBSP3RX, | ||
611 | .tx_irq = INT_McBSP3TX }, | ||
612 | }; | ||
613 | #endif | ||
614 | |||
615 | static int __init omap_mcbsp_init(void) | ||
616 | { | ||
617 | int mcbsp_count = 0, i; | ||
618 | static const struct omap_mcbsp_info *mcbsp_info; | ||
619 | |||
620 | printk("Initializing OMAP McBSP system\n"); | ||
621 | |||
622 | mcbsp_dsp_ck = clk_get(0, "dsp_ck"); | ||
623 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
624 | printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); | ||
625 | return PTR_ERR(mcbsp_dsp_ck); | ||
626 | } | ||
627 | mcbsp_api_ck = clk_get(0, "api_ck"); | ||
628 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
629 | printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); | ||
630 | return PTR_ERR(mcbsp_api_ck); | ||
631 | } | ||
632 | |||
633 | #ifdef CONFIG_ARCH_OMAP730 | ||
634 | if (cpu_is_omap730()) { | ||
635 | mcbsp_info = mcbsp_730; | ||
636 | mcbsp_count = ARRAY_SIZE(mcbsp_730); | ||
637 | } | ||
638 | #endif | ||
639 | #ifdef CONFIG_ARCH_OMAP1510 | ||
640 | if (cpu_is_omap1510()) { | ||
641 | mcbsp_info = mcbsp_1510; | ||
642 | mcbsp_count = ARRAY_SIZE(mcbsp_1510); | ||
643 | } | ||
644 | #endif | ||
645 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
646 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
647 | mcbsp_info = mcbsp_1610; | ||
648 | mcbsp_count = ARRAY_SIZE(mcbsp_1610); | ||
649 | } | ||
650 | #endif | ||
651 | for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { | ||
652 | if (i >= mcbsp_count) { | ||
653 | mcbsp[i].io_base = 0; | ||
654 | mcbsp[i].free = 0; | ||
655 | continue; | ||
656 | } | ||
657 | mcbsp[i].id = i + 1; | ||
658 | mcbsp[i].free = 1; | ||
659 | mcbsp[i].dma_tx_lch = -1; | ||
660 | mcbsp[i].dma_rx_lch = -1; | ||
661 | |||
662 | mcbsp[i].io_base = mcbsp_info[i].virt_base; | ||
663 | mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; | ||
664 | mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; | ||
665 | mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; | ||
666 | mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; | ||
667 | spin_lock_init(&mcbsp[i].lock); | ||
668 | } | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | |||
674 | arch_initcall(omap_mcbsp_init); | ||
675 | |||
676 | EXPORT_SYMBOL(omap_mcbsp_config); | ||
677 | EXPORT_SYMBOL(omap_mcbsp_request); | ||
678 | EXPORT_SYMBOL(omap_mcbsp_free); | ||
679 | EXPORT_SYMBOL(omap_mcbsp_start); | ||
680 | EXPORT_SYMBOL(omap_mcbsp_stop); | ||
681 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); | ||
682 | EXPORT_SYMBOL(omap_mcbsp_recv_word); | ||
683 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); | ||
684 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); | ||
685 | EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); | ||
diff --git a/arch/arm/mach-omap/mux.c b/arch/arm/mach-omap/mux.c new file mode 100644 index 000000000000..bcf3c6e5ecd0 --- /dev/null +++ b/arch/arm/mach-omap/mux.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/mux.c | ||
3 | * | ||
4 | * Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h | ||
5 | * | ||
6 | * Copyright (C) 2003 Nokia Corporation | ||
7 | * | ||
8 | * Written by Tony Lindgren <tony.lindgren@nokia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/config.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <asm/system.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <linux/spinlock.h> | ||
31 | |||
32 | #define __MUX_C__ | ||
33 | #include <asm/arch/mux.h> | ||
34 | |||
35 | #ifdef CONFIG_OMAP_MUX | ||
36 | |||
37 | /* | ||
38 | * Sets the Omap MUX and PULL_DWN registers based on the table | ||
39 | */ | ||
40 | int __init_or_module | ||
41 | omap_cfg_reg(const reg_cfg_t reg_cfg) | ||
42 | { | ||
43 | static DEFINE_SPINLOCK(mux_spin_lock); | ||
44 | |||
45 | unsigned long flags; | ||
46 | reg_cfg_set *cfg; | ||
47 | unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, | ||
48 | pull_orig = 0, pull = 0; | ||
49 | unsigned int mask, warn = 0; | ||
50 | |||
51 | if (reg_cfg > ARRAY_SIZE(reg_cfg_table)) { | ||
52 | printk(KERN_ERR "MUX: reg_cfg %d\n", reg_cfg); | ||
53 | return -EINVAL; | ||
54 | } | ||
55 | |||
56 | cfg = ®_cfg_table[reg_cfg]; | ||
57 | |||
58 | /* | ||
59 | * We do a pretty long section here with lock on, but pin muxing | ||
60 | * should only happen on driver init for each driver, so it's not time | ||
61 | * critical. | ||
62 | */ | ||
63 | spin_lock_irqsave(&mux_spin_lock, flags); | ||
64 | |||
65 | /* Check the mux register in question */ | ||
66 | if (cfg->mux_reg) { | ||
67 | unsigned tmp1, tmp2; | ||
68 | |||
69 | reg_orig = omap_readl(cfg->mux_reg); | ||
70 | |||
71 | /* The mux registers always seem to be 3 bits long */ | ||
72 | mask = (0x7 << cfg->mask_offset); | ||
73 | tmp1 = reg_orig & mask; | ||
74 | reg = reg_orig & ~mask; | ||
75 | |||
76 | tmp2 = (cfg->mask << cfg->mask_offset); | ||
77 | reg |= tmp2; | ||
78 | |||
79 | if (tmp1 != tmp2) | ||
80 | warn = 1; | ||
81 | |||
82 | omap_writel(reg, cfg->mux_reg); | ||
83 | } | ||
84 | |||
85 | /* Check for pull up or pull down selection on 1610 */ | ||
86 | if (!cpu_is_omap1510()) { | ||
87 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
88 | pu_pd_orig = omap_readl(cfg->pu_pd_reg); | ||
89 | mask = 1 << cfg->pull_bit; | ||
90 | |||
91 | if (cfg->pu_pd_val) { | ||
92 | if (!(pu_pd_orig & mask)) | ||
93 | warn = 1; | ||
94 | /* Use pull up */ | ||
95 | pu_pd = pu_pd_orig | mask; | ||
96 | } else { | ||
97 | if (pu_pd_orig & mask) | ||
98 | warn = 1; | ||
99 | /* Use pull down */ | ||
100 | pu_pd = pu_pd_orig & ~mask; | ||
101 | } | ||
102 | omap_writel(pu_pd, cfg->pu_pd_reg); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /* Check for an associated pull down register */ | ||
107 | if (cfg->pull_reg) { | ||
108 | pull_orig = omap_readl(cfg->pull_reg); | ||
109 | mask = 1 << cfg->pull_bit; | ||
110 | |||
111 | if (cfg->pull_val) { | ||
112 | if (pull_orig & mask) | ||
113 | warn = 1; | ||
114 | /* Low bit = pull enabled */ | ||
115 | pull = pull_orig & ~mask; | ||
116 | } else { | ||
117 | if (!(pull_orig & mask)) | ||
118 | warn = 1; | ||
119 | /* High bit = pull disabled */ | ||
120 | pull = pull_orig | mask; | ||
121 | } | ||
122 | |||
123 | omap_writel(pull, cfg->pull_reg); | ||
124 | } | ||
125 | |||
126 | if (warn) { | ||
127 | #ifdef CONFIG_OMAP_MUX_WARNINGS | ||
128 | printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); | ||
129 | #endif | ||
130 | } | ||
131 | |||
132 | #ifdef CONFIG_OMAP_MUX_DEBUG | ||
133 | if (cfg->debug || warn) { | ||
134 | printk("MUX: Setting register %s\n", cfg->name); | ||
135 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
136 | cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); | ||
137 | |||
138 | if (!cpu_is_omap1510()) { | ||
139 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
140 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
141 | cfg->pu_pd_name, cfg->pu_pd_reg, | ||
142 | pu_pd_orig, pu_pd); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | if (cfg->pull_reg) | ||
147 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
148 | cfg->pull_name, cfg->pull_reg, pull_orig, pull); | ||
149 | } | ||
150 | #endif | ||
151 | |||
152 | spin_unlock_irqrestore(&mux_spin_lock, flags); | ||
153 | |||
154 | #ifdef CONFIG_OMAP_MUX_ERRORS | ||
155 | return warn ? -ETXTBSY : 0; | ||
156 | #else | ||
157 | return 0; | ||
158 | #endif | ||
159 | } | ||
160 | |||
161 | EXPORT_SYMBOL(omap_cfg_reg); | ||
162 | |||
163 | #endif /* CONFIG_OMAP_MUX */ | ||
diff --git a/arch/arm/mach-omap/ocpi.c b/arch/arm/mach-omap/ocpi.c new file mode 100644 index 000000000000..c9ced134a75d --- /dev/null +++ b/arch/arm/mach-omap/ocpi.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/ocpi.c | ||
3 | * | ||
4 | * Minimal OCP bus support for omap16xx | ||
5 | * | ||
6 | * Copyright (C) 2003 - 2005 Nokia Corporation | ||
7 | * Written by Tony Lindgren <tony@atomide.com> | ||
8 | * | ||
9 | * Modified for clock framework by Paul Mundt <paul.mundt@nokia.com>. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/config.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/version.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/err.h> | ||
35 | |||
36 | #include <asm/io.h> | ||
37 | #include <asm/hardware/clock.h> | ||
38 | #include <asm/arch/hardware.h> | ||
39 | |||
40 | #define OCPI_BASE 0xfffec320 | ||
41 | #define OCPI_FAULT (OCPI_BASE + 0x00) | ||
42 | #define OCPI_CMD_FAULT (OCPI_BASE + 0x04) | ||
43 | #define OCPI_SINT0 (OCPI_BASE + 0x08) | ||
44 | #define OCPI_TABORT (OCPI_BASE + 0x0c) | ||
45 | #define OCPI_SINT1 (OCPI_BASE + 0x10) | ||
46 | #define OCPI_PROT (OCPI_BASE + 0x14) | ||
47 | #define OCPI_SEC (OCPI_BASE + 0x18) | ||
48 | |||
49 | /* USB OHCI OCPI access error registers */ | ||
50 | #define HOSTUEADDR 0xfffba0e0 | ||
51 | #define HOSTUESTATUS 0xfffba0e4 | ||
52 | |||
53 | static struct clk *ocpi_ck; | ||
54 | |||
55 | /* | ||
56 | * Enables device access to OMAP buses via the OCPI bridge | ||
57 | * FIXME: Add locking | ||
58 | */ | ||
59 | int ocpi_enable(void) | ||
60 | { | ||
61 | unsigned int val; | ||
62 | |||
63 | if (!cpu_is_omap16xx()) | ||
64 | return -ENODEV; | ||
65 | |||
66 | /* Make sure there's clock for OCPI */ | ||
67 | clk_enable(ocpi_ck); | ||
68 | |||
69 | /* Enable access for OHCI in OCPI */ | ||
70 | val = omap_readl(OCPI_PROT); | ||
71 | val &= ~0xff; | ||
72 | //val &= (1 << 0); /* Allow access only to EMIFS */ | ||
73 | omap_writel(val, OCPI_PROT); | ||
74 | |||
75 | val = omap_readl(OCPI_SEC); | ||
76 | val &= ~0xff; | ||
77 | omap_writel(val, OCPI_SEC); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | EXPORT_SYMBOL(ocpi_enable); | ||
82 | |||
83 | static int __init omap_ocpi_init(void) | ||
84 | { | ||
85 | if (!cpu_is_omap16xx()) | ||
86 | return -ENODEV; | ||
87 | |||
88 | ocpi_ck = clk_get(NULL, "l3_ocpi_ck"); | ||
89 | if (IS_ERR(ocpi_ck)) | ||
90 | return PTR_ERR(ocpi_ck); | ||
91 | |||
92 | clk_use(ocpi_ck); | ||
93 | ocpi_enable(); | ||
94 | printk("OMAP OCPI interconnect driver loaded\n"); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void __exit omap_ocpi_exit(void) | ||
100 | { | ||
101 | /* REVISIT: Disable OCPI */ | ||
102 | |||
103 | if (!cpu_is_omap16xx()) | ||
104 | return; | ||
105 | |||
106 | clk_unuse(ocpi_ck); | ||
107 | clk_put(ocpi_ck); | ||
108 | } | ||
109 | |||
110 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); | ||
111 | MODULE_DESCRIPTION("OMAP OCPI bus controller module"); | ||
112 | MODULE_LICENSE("GPL"); | ||
113 | module_init(omap_ocpi_init); | ||
114 | module_exit(omap_ocpi_exit); | ||
diff --git a/arch/arm/mach-omap/pm.c b/arch/arm/mach-omap/pm.c new file mode 100644 index 000000000000..00fac155df2a --- /dev/null +++ b/arch/arm/mach-omap/pm.c | |||
@@ -0,0 +1,628 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/pm.c | ||
3 | * | ||
4 | * OMAP Power Management Routines | ||
5 | * | ||
6 | * Original code for the SA11x0: | ||
7 | * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> | ||
8 | * | ||
9 | * Modified for the PXA250 by Nicolas Pitre: | ||
10 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
11 | * | ||
12 | * Modified for the OMAP1510 by David Singleton: | ||
13 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
14 | * | ||
15 | * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or modify it | ||
18 | * under the terms of the GNU General Public License as published by the | ||
19 | * Free Software Foundation; either version 2 of the License, or (at your | ||
20 | * option) any later version. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
25 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
28 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
29 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
32 | * | ||
33 | * You should have received a copy of the GNU General Public License along | ||
34 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
35 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
36 | */ | ||
37 | |||
38 | #include <linux/pm.h> | ||
39 | #include <linux/sched.h> | ||
40 | #include <linux/proc_fs.h> | ||
41 | #include <linux/pm.h> | ||
42 | |||
43 | #include <asm/io.h> | ||
44 | #include <asm/mach-types.h> | ||
45 | #include <asm/arch/omap16xx.h> | ||
46 | #include <asm/arch/pm.h> | ||
47 | #include <asm/arch/mux.h> | ||
48 | #include <asm/arch/tc.h> | ||
49 | #include <asm/arch/tps65010.h> | ||
50 | |||
51 | #include "clock.h" | ||
52 | |||
53 | static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; | ||
54 | static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; | ||
55 | static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; | ||
56 | static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; | ||
57 | |||
58 | /* | ||
59 | * Let's power down on idle, but only if we are really | ||
60 | * idle, because once we start down the path of | ||
61 | * going idle we continue to do idle even if we get | ||
62 | * a clock tick interrupt . . | ||
63 | */ | ||
64 | void omap_pm_idle(void) | ||
65 | { | ||
66 | int (*func_ptr)(void) = 0; | ||
67 | unsigned int mask32 = 0; | ||
68 | |||
69 | /* | ||
70 | * If the DSP is being used let's just idle the CPU, the overhead | ||
71 | * to wake up from Big Sleep is big, milliseconds versus micro | ||
72 | * seconds for wait for interrupt. | ||
73 | */ | ||
74 | |||
75 | local_irq_disable(); | ||
76 | local_fiq_disable(); | ||
77 | if (need_resched()) { | ||
78 | local_fiq_enable(); | ||
79 | local_irq_enable(); | ||
80 | return; | ||
81 | } | ||
82 | mask32 = omap_readl(ARM_SYSST); | ||
83 | local_fiq_enable(); | ||
84 | local_irq_enable(); | ||
85 | |||
86 | #if defined(CONFIG_OMAP_32K_TIMER) && defined(CONFIG_NO_IDLE_HZ) | ||
87 | /* Override timer to use VST for the next cycle */ | ||
88 | omap_32k_timer_next_vst_interrupt(); | ||
89 | #endif | ||
90 | |||
91 | if ((mask32 & DSP_IDLE) == 0) { | ||
92 | __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); | ||
93 | } else { | ||
94 | |||
95 | if (cpu_is_omap1510()) { | ||
96 | func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); | ||
97 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
98 | func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); | ||
99 | } else if (cpu_is_omap5912()) { | ||
100 | func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); | ||
101 | } | ||
102 | |||
103 | func_ptr(); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Configuration of the wakeup event is board specific. For the | ||
109 | * moment we put it into this helper function. Later it may move | ||
110 | * to board specific files. | ||
111 | */ | ||
112 | static void omap_pm_wakeup_setup(void) | ||
113 | { | ||
114 | /* | ||
115 | * Enable ARM XOR clock and release peripheral from reset by | ||
116 | * writing 1 to PER_EN bit in ARM_RSTCT2, this is required | ||
117 | * for UART configuration to use UART2 to wake up. | ||
118 | */ | ||
119 | |||
120 | omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); | ||
121 | omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); | ||
122 | omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); | ||
123 | |||
124 | /* | ||
125 | * Turn off all interrupts except L1-2nd level cascade, | ||
126 | * and the L2 wakeup interrupts: keypad and UART2. | ||
127 | */ | ||
128 | |||
129 | omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); | ||
130 | |||
131 | if (cpu_is_omap1510()) { | ||
132 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); | ||
133 | } | ||
134 | |||
135 | if (cpu_is_omap16xx()) { | ||
136 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); | ||
137 | |||
138 | omap_writel(~0x0, OMAP_IH2_1_MIR); | ||
139 | omap_writel(~0x0, OMAP_IH2_2_MIR); | ||
140 | omap_writel(~0x0, OMAP_IH2_3_MIR); | ||
141 | } | ||
142 | |||
143 | /* New IRQ agreement */ | ||
144 | omap_writel(1, OMAP_IH1_CONTROL); | ||
145 | |||
146 | /* external PULL to down, bit 22 = 0 */ | ||
147 | omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2); | ||
148 | } | ||
149 | |||
150 | void omap_pm_suspend(void) | ||
151 | { | ||
152 | unsigned int mask32 = 0; | ||
153 | unsigned long arg0 = 0, arg1 = 0; | ||
154 | int (*func_ptr)(unsigned short, unsigned short) = 0; | ||
155 | unsigned short save_dsp_idlect2; | ||
156 | |||
157 | printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); | ||
158 | |||
159 | if (machine_is_omap_osk()) { | ||
160 | /* Stop LED1 (D9) blink */ | ||
161 | tps65010_set_led(LED1, OFF); | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Step 1: turn off interrupts | ||
166 | */ | ||
167 | |||
168 | local_irq_disable(); | ||
169 | local_fiq_disable(); | ||
170 | |||
171 | /* | ||
172 | * Step 2: save registers | ||
173 | * | ||
174 | * The omap is a strange/beautiful device. The caches, memory | ||
175 | * and register state are preserved across power saves. | ||
176 | * We have to save and restore very little register state to | ||
177 | * idle the omap. | ||
178 | * | ||
179 | * Save interrupt, MPUI, ARM and UPLD control registers. | ||
180 | */ | ||
181 | |||
182 | if (cpu_is_omap1510()) { | ||
183 | MPUI1510_SAVE(OMAP_IH1_MIR); | ||
184 | MPUI1510_SAVE(OMAP_IH2_MIR); | ||
185 | MPUI1510_SAVE(MPUI_CTRL); | ||
186 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
187 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
188 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
189 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
190 | } else if (cpu_is_omap16xx()) { | ||
191 | MPUI1610_SAVE(OMAP_IH1_MIR); | ||
192 | MPUI1610_SAVE(OMAP_IH2_0_MIR); | ||
193 | MPUI1610_SAVE(OMAP_IH2_1_MIR); | ||
194 | MPUI1610_SAVE(OMAP_IH2_2_MIR); | ||
195 | MPUI1610_SAVE(OMAP_IH2_3_MIR); | ||
196 | MPUI1610_SAVE(MPUI_CTRL); | ||
197 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
198 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
199 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
200 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
201 | } | ||
202 | |||
203 | ARM_SAVE(ARM_CKCTL); | ||
204 | ARM_SAVE(ARM_IDLECT1); | ||
205 | ARM_SAVE(ARM_IDLECT2); | ||
206 | ARM_SAVE(ARM_EWUPCT); | ||
207 | ARM_SAVE(ARM_RSTCT1); | ||
208 | ARM_SAVE(ARM_RSTCT2); | ||
209 | ARM_SAVE(ARM_SYSST); | ||
210 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
211 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
212 | |||
213 | /* | ||
214 | * Step 3: LOW_PWR signal enabling | ||
215 | * | ||
216 | * Allow the LOW_PWR signal to be visible on MPUIO5 ball. | ||
217 | */ | ||
218 | if (cpu_is_omap1510()) { | ||
219 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
220 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
221 | OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
222 | } else if (cpu_is_omap16xx()) { | ||
223 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
224 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
225 | OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
226 | } | ||
227 | |||
228 | /* configure LOW_PWR pin */ | ||
229 | omap_cfg_reg(T20_1610_LOW_PWR); | ||
230 | |||
231 | /* | ||
232 | * Step 4: OMAP DSP Shutdown | ||
233 | */ | ||
234 | |||
235 | /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ | ||
236 | omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, | ||
237 | ARM_RSTCT1); | ||
238 | |||
239 | /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ | ||
240 | omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); | ||
241 | |||
242 | /* Set EN_DSPCK = 0, stop DSP block clock */ | ||
243 | omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); | ||
244 | |||
245 | /* Stop any DSP domain clocks */ | ||
246 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
247 | save_dsp_idlect2 = __raw_readw(DSP_IDLECT2); | ||
248 | __raw_writew(0, DSP_IDLECT2); | ||
249 | |||
250 | /* | ||
251 | * Step 5: Wakeup Event Setup | ||
252 | */ | ||
253 | |||
254 | omap_pm_wakeup_setup(); | ||
255 | |||
256 | /* | ||
257 | * Step 6a: ARM and Traffic controller shutdown | ||
258 | * | ||
259 | * Step 6 starts here with clock and watchdog disable | ||
260 | */ | ||
261 | |||
262 | /* stop clocks */ | ||
263 | mask32 = omap_readl(ARM_IDLECT2); | ||
264 | mask32 &= ~(1<<EN_WDTCK); /* bit 0 -> 0 (WDT clock) */ | ||
265 | mask32 |= (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */ | ||
266 | mask32 &= ~(1<<EN_PERCK); /* bit 2 -> 0 (MPUPER_CK clock) */ | ||
267 | mask32 &= ~(1<<EN_LCDCK); /* bit 3 -> 0 (LCDC clock) */ | ||
268 | mask32 &= ~(1<<EN_LBCK); /* bit 4 -> 0 (local bus clock) */ | ||
269 | mask32 |= (1<<EN_APICK); /* bit 6 -> 1 (MPUI clock) */ | ||
270 | mask32 &= ~(1<<EN_TIMCK); /* bit 7 -> 0 (MPU timer clock) */ | ||
271 | mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */ | ||
272 | mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */ | ||
273 | omap_writel(mask32, ARM_IDLECT2); | ||
274 | |||
275 | /* disable ARM watchdog */ | ||
276 | omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); | ||
277 | omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); | ||
278 | |||
279 | /* | ||
280 | * Step 6b: ARM and Traffic controller shutdown | ||
281 | * | ||
282 | * Step 6 continues here. Prepare jump to power management | ||
283 | * assembly code in internal SRAM. | ||
284 | * | ||
285 | * Since the omap_cpu_suspend routine has been copied to | ||
286 | * SRAM, we'll do an indirect procedure call to it and pass the | ||
287 | * contents of arm_idlect1 and arm_idlect2 so it can restore | ||
288 | * them when it wakes up and it will return. | ||
289 | */ | ||
290 | |||
291 | arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; | ||
292 | arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; | ||
293 | |||
294 | if (cpu_is_omap1510()) { | ||
295 | func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); | ||
296 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
297 | func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); | ||
298 | } else if (cpu_is_omap5912()) { | ||
299 | func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Step 6c: ARM and Traffic controller shutdown | ||
304 | * | ||
305 | * Jump to assembly code. The processor will stay there | ||
306 | * until wake up. | ||
307 | */ | ||
308 | |||
309 | func_ptr(arg0, arg1); | ||
310 | |||
311 | /* | ||
312 | * If we are here, processor is woken up! | ||
313 | */ | ||
314 | |||
315 | if (cpu_is_omap1510()) { | ||
316 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
317 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
318 | ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
319 | } else if (cpu_is_omap16xx()) { | ||
320 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
321 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
322 | ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
323 | } | ||
324 | |||
325 | |||
326 | /* Restore DSP clocks */ | ||
327 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
328 | __raw_writew(save_dsp_idlect2, DSP_IDLECT2); | ||
329 | ARM_RESTORE(ARM_IDLECT2); | ||
330 | |||
331 | /* | ||
332 | * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did | ||
333 | */ | ||
334 | |||
335 | ARM_RESTORE(ARM_CKCTL); | ||
336 | ARM_RESTORE(ARM_EWUPCT); | ||
337 | ARM_RESTORE(ARM_RSTCT1); | ||
338 | ARM_RESTORE(ARM_RSTCT2); | ||
339 | ARM_RESTORE(ARM_SYSST); | ||
340 | ULPD_RESTORE(ULPD_CLOCK_CTRL); | ||
341 | ULPD_RESTORE(ULPD_STATUS_REQ); | ||
342 | |||
343 | if (cpu_is_omap1510()) { | ||
344 | MPUI1510_RESTORE(MPUI_CTRL); | ||
345 | MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
346 | MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); | ||
347 | MPUI1510_RESTORE(EMIFS_CONFIG); | ||
348 | MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); | ||
349 | MPUI1510_RESTORE(OMAP_IH1_MIR); | ||
350 | MPUI1510_RESTORE(OMAP_IH2_MIR); | ||
351 | } else if (cpu_is_omap16xx()) { | ||
352 | MPUI1610_RESTORE(MPUI_CTRL); | ||
353 | MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
354 | MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); | ||
355 | MPUI1610_RESTORE(EMIFS_CONFIG); | ||
356 | MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); | ||
357 | |||
358 | MPUI1610_RESTORE(OMAP_IH1_MIR); | ||
359 | MPUI1610_RESTORE(OMAP_IH2_0_MIR); | ||
360 | MPUI1610_RESTORE(OMAP_IH2_1_MIR); | ||
361 | MPUI1610_RESTORE(OMAP_IH2_2_MIR); | ||
362 | MPUI1610_RESTORE(OMAP_IH2_3_MIR); | ||
363 | } | ||
364 | |||
365 | /* | ||
366 | * Reenable interrupts | ||
367 | */ | ||
368 | |||
369 | local_irq_enable(); | ||
370 | local_fiq_enable(); | ||
371 | |||
372 | printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev); | ||
373 | |||
374 | if (machine_is_omap_osk()) { | ||
375 | /* Let LED1 (D9) blink again */ | ||
376 | tps65010_set_led(LED1, BLINK); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
381 | static int g_read_completed; | ||
382 | |||
383 | /* | ||
384 | * Read system PM registers for debugging | ||
385 | */ | ||
386 | static int omap_pm_read_proc( | ||
387 | char *page_buffer, | ||
388 | char **my_first_byte, | ||
389 | off_t virtual_start, | ||
390 | int length, | ||
391 | int *eof, | ||
392 | void *data) | ||
393 | { | ||
394 | int my_buffer_offset = 0; | ||
395 | char * const my_base = page_buffer; | ||
396 | |||
397 | ARM_SAVE(ARM_CKCTL); | ||
398 | ARM_SAVE(ARM_IDLECT1); | ||
399 | ARM_SAVE(ARM_IDLECT2); | ||
400 | ARM_SAVE(ARM_EWUPCT); | ||
401 | ARM_SAVE(ARM_RSTCT1); | ||
402 | ARM_SAVE(ARM_RSTCT2); | ||
403 | ARM_SAVE(ARM_SYSST); | ||
404 | |||
405 | ULPD_SAVE(ULPD_IT_STATUS); | ||
406 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
407 | ULPD_SAVE(ULPD_SOFT_REQ); | ||
408 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
409 | ULPD_SAVE(ULPD_DPLL_CTRL); | ||
410 | ULPD_SAVE(ULPD_POWER_CTRL); | ||
411 | |||
412 | if (cpu_is_omap1510()) { | ||
413 | MPUI1510_SAVE(MPUI_CTRL); | ||
414 | MPUI1510_SAVE(MPUI_DSP_STATUS); | ||
415 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
416 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
417 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
418 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
419 | } else if (cpu_is_omap16xx()) { | ||
420 | MPUI1610_SAVE(MPUI_CTRL); | ||
421 | MPUI1610_SAVE(MPUI_DSP_STATUS); | ||
422 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
423 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
424 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
425 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
426 | } | ||
427 | |||
428 | if (virtual_start == 0) { | ||
429 | g_read_completed = 0; | ||
430 | |||
431 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
432 | "ARM_CKCTL_REG: 0x%-8x \n" | ||
433 | "ARM_IDLECT1_REG: 0x%-8x \n" | ||
434 | "ARM_IDLECT2_REG: 0x%-8x \n" | ||
435 | "ARM_EWUPCT_REG: 0x%-8x \n" | ||
436 | "ARM_RSTCT1_REG: 0x%-8x \n" | ||
437 | "ARM_RSTCT2_REG: 0x%-8x \n" | ||
438 | "ARM_SYSST_REG: 0x%-8x \n" | ||
439 | "ULPD_IT_STATUS_REG: 0x%-4x \n" | ||
440 | "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" | ||
441 | "ULPD_SOFT_REQ_REG: 0x%-4x \n" | ||
442 | "ULPD_DPLL_CTRL_REG: 0x%-4x \n" | ||
443 | "ULPD_STATUS_REQ_REG: 0x%-4x \n" | ||
444 | "ULPD_POWER_CTRL_REG: 0x%-4x \n", | ||
445 | ARM_SHOW(ARM_CKCTL), | ||
446 | ARM_SHOW(ARM_IDLECT1), | ||
447 | ARM_SHOW(ARM_IDLECT2), | ||
448 | ARM_SHOW(ARM_EWUPCT), | ||
449 | ARM_SHOW(ARM_RSTCT1), | ||
450 | ARM_SHOW(ARM_RSTCT2), | ||
451 | ARM_SHOW(ARM_SYSST), | ||
452 | ULPD_SHOW(ULPD_IT_STATUS), | ||
453 | ULPD_SHOW(ULPD_CLOCK_CTRL), | ||
454 | ULPD_SHOW(ULPD_SOFT_REQ), | ||
455 | ULPD_SHOW(ULPD_DPLL_CTRL), | ||
456 | ULPD_SHOW(ULPD_STATUS_REQ), | ||
457 | ULPD_SHOW(ULPD_POWER_CTRL)); | ||
458 | |||
459 | if (cpu_is_omap1510()) { | ||
460 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
461 | "MPUI1510_CTRL_REG 0x%-8x \n" | ||
462 | "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" | ||
463 | "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
464 | "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
465 | "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
466 | "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
467 | MPUI1510_SHOW(MPUI_CTRL), | ||
468 | MPUI1510_SHOW(MPUI_DSP_STATUS), | ||
469 | MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
470 | MPUI1510_SHOW(MPUI_DSP_API_CONFIG), | ||
471 | MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), | ||
472 | MPUI1510_SHOW(EMIFS_CONFIG)); | ||
473 | } else if (cpu_is_omap16xx()) { | ||
474 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
475 | "MPUI1610_CTRL_REG 0x%-8x \n" | ||
476 | "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" | ||
477 | "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
478 | "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
479 | "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
480 | "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
481 | MPUI1610_SHOW(MPUI_CTRL), | ||
482 | MPUI1610_SHOW(MPUI_DSP_STATUS), | ||
483 | MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
484 | MPUI1610_SHOW(MPUI_DSP_API_CONFIG), | ||
485 | MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), | ||
486 | MPUI1610_SHOW(EMIFS_CONFIG)); | ||
487 | } | ||
488 | |||
489 | g_read_completed++; | ||
490 | } else if (g_read_completed >= 1) { | ||
491 | *eof = 1; | ||
492 | return 0; | ||
493 | } | ||
494 | g_read_completed++; | ||
495 | |||
496 | *my_first_byte = page_buffer; | ||
497 | return my_buffer_offset; | ||
498 | } | ||
499 | |||
500 | static void omap_pm_init_proc(void) | ||
501 | { | ||
502 | struct proc_dir_entry *entry; | ||
503 | |||
504 | entry = create_proc_read_entry("driver/omap_pm", | ||
505 | S_IWUSR | S_IRUGO, NULL, | ||
506 | omap_pm_read_proc, 0); | ||
507 | } | ||
508 | |||
509 | #endif /* DEBUG && CONFIG_PROC_FS */ | ||
510 | |||
511 | /* | ||
512 | * omap_pm_prepare - Do preliminary suspend work. | ||
513 | * @state: suspend state we're entering. | ||
514 | * | ||
515 | */ | ||
516 | //#include <asm/arch/hardware.h> | ||
517 | |||
518 | static int omap_pm_prepare(suspend_state_t state) | ||
519 | { | ||
520 | int error = 0; | ||
521 | |||
522 | switch (state) | ||
523 | { | ||
524 | case PM_SUSPEND_STANDBY: | ||
525 | case PM_SUSPEND_MEM: | ||
526 | break; | ||
527 | |||
528 | case PM_SUSPEND_DISK: | ||
529 | return -ENOTSUPP; | ||
530 | |||
531 | default: | ||
532 | return -EINVAL; | ||
533 | } | ||
534 | |||
535 | return error; | ||
536 | } | ||
537 | |||
538 | |||
539 | /* | ||
540 | * omap_pm_enter - Actually enter a sleep state. | ||
541 | * @state: State we're entering. | ||
542 | * | ||
543 | */ | ||
544 | |||
545 | static int omap_pm_enter(suspend_state_t state) | ||
546 | { | ||
547 | switch (state) | ||
548 | { | ||
549 | case PM_SUSPEND_STANDBY: | ||
550 | case PM_SUSPEND_MEM: | ||
551 | omap_pm_suspend(); | ||
552 | break; | ||
553 | |||
554 | case PM_SUSPEND_DISK: | ||
555 | return -ENOTSUPP; | ||
556 | |||
557 | default: | ||
558 | return -EINVAL; | ||
559 | } | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | |||
565 | /** | ||
566 | * omap_pm_finish - Finish up suspend sequence. | ||
567 | * @state: State we're coming out of. | ||
568 | * | ||
569 | * This is called after we wake back up (or if entering the sleep state | ||
570 | * failed). | ||
571 | */ | ||
572 | |||
573 | static int omap_pm_finish(suspend_state_t state) | ||
574 | { | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | |||
579 | struct pm_ops omap_pm_ops ={ | ||
580 | .pm_disk_mode = 0, | ||
581 | .prepare = omap_pm_prepare, | ||
582 | .enter = omap_pm_enter, | ||
583 | .finish = omap_pm_finish, | ||
584 | }; | ||
585 | |||
586 | static int __init omap_pm_init(void) | ||
587 | { | ||
588 | printk("Power Management for TI OMAP.\n"); | ||
589 | pm_idle = omap_pm_idle; | ||
590 | /* | ||
591 | * We copy the assembler sleep/wakeup routines to SRAM. | ||
592 | * These routines need to be in SRAM as that's the only | ||
593 | * memory the MPU can see when it wakes up. | ||
594 | */ | ||
595 | |||
596 | #ifdef CONFIG_ARCH_OMAP1510 | ||
597 | if (cpu_is_omap1510()) { | ||
598 | memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND, | ||
599 | omap1510_idle_loop_suspend, | ||
600 | omap1510_idle_loop_suspend_sz); | ||
601 | memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend, | ||
602 | omap1510_cpu_suspend_sz); | ||
603 | } else | ||
604 | #endif | ||
605 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
606 | memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND, | ||
607 | omap1610_idle_loop_suspend, | ||
608 | omap1610_idle_loop_suspend_sz); | ||
609 | memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
610 | omap1610_cpu_suspend_sz); | ||
611 | } else if (cpu_is_omap5912()) { | ||
612 | memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND, | ||
613 | omap1610_idle_loop_suspend, | ||
614 | omap1610_idle_loop_suspend_sz); | ||
615 | memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
616 | omap1610_cpu_suspend_sz); | ||
617 | } | ||
618 | |||
619 | pm_set_ops(&omap_pm_ops); | ||
620 | |||
621 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
622 | omap_pm_init_proc(); | ||
623 | #endif | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | __initcall(omap_pm_init); | ||
628 | |||
diff --git a/arch/arm/mach-omap/sleep.S b/arch/arm/mach-omap/sleep.S new file mode 100644 index 000000000000..4d426d105828 --- /dev/null +++ b/arch/arm/mach-omap/sleep.S | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/sleep.S | ||
3 | * | ||
4 | * Low-level OMAP1510/1610 sleep/wakeUp support | ||
5 | * | ||
6 | * Initial SA1110 code: | ||
7 | * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> | ||
8 | * | ||
9 | * Adapted for PXA by Nicolas Pitre: | ||
10 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
11 | * | ||
12 | * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify it | ||
15 | * under the terms of the GNU General Public License as published by the | ||
16 | * Free Software Foundation; either version 2 of the License, or (at your | ||
17 | * option) any later version. | ||
18 | * | ||
19 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
20 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
22 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
25 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
29 | * | ||
30 | * You should have received a copy of the GNU General Public License along | ||
31 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
32 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
33 | */ | ||
34 | |||
35 | #include <linux/config.h> | ||
36 | #include <linux/linkage.h> | ||
37 | #include <asm/assembler.h> | ||
38 | #include <asm/arch/io.h> | ||
39 | #include <asm/arch/pm.h> | ||
40 | |||
41 | .text | ||
42 | |||
43 | /* | ||
44 | * Forces OMAP into idle state | ||
45 | * | ||
46 | * omapXXXX_idle_loop_suspend() | ||
47 | * | ||
48 | * Note: This code get's copied to internal SRAM at boot. When the OMAP | ||
49 | * wakes up it continues execution at the point it went to sleep. | ||
50 | * | ||
51 | * Note: Because of slightly different configuration values we have | ||
52 | * processor specific functions here. | ||
53 | */ | ||
54 | |||
55 | #ifdef CONFIG_ARCH_OMAP1510 | ||
56 | ENTRY(omap1510_idle_loop_suspend) | ||
57 | |||
58 | stmfd sp!, {r0 - r12, lr} @ save registers on stack | ||
59 | |||
60 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
61 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
62 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
63 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
64 | |||
65 | @ turn off clock domains | ||
66 | @ get ARM_IDLECT2 into r2 | ||
67 | ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
68 | mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff | ||
69 | orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 | ||
70 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
71 | |||
72 | @ request ARM idle | ||
73 | @ get ARM_IDLECT1 into r1 | ||
74 | ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
75 | orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff | ||
76 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
77 | |||
78 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
79 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
80 | l_1510: subs r5, r5, #1 | ||
81 | bne l_1510 | ||
82 | /* | ||
83 | * Let's wait for the next clock tick to wake us up. | ||
84 | */ | ||
85 | mov r0, #0 | ||
86 | mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt | ||
87 | /* | ||
88 | * omap1510_idle_loop_suspend()'s resume point. | ||
89 | * | ||
90 | * It will just start executing here, so we'll restore stuff from the | ||
91 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
92 | */ | ||
93 | |||
94 | @ restore ARM_IDLECT1 and ARM_IDLECT2 and return | ||
95 | @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 | ||
96 | strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
97 | strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
98 | |||
99 | ldmfd sp!, {r0 - r12, pc} @ restore regs and return | ||
100 | |||
101 | ENTRY(omap1510_idle_loop_suspend_sz) | ||
102 | .word . - omap1510_idle_loop_suspend | ||
103 | #endif /* CONFIG_ARCH_OMAP1510 */ | ||
104 | |||
105 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
106 | ENTRY(omap1610_idle_loop_suspend) | ||
107 | |||
108 | stmfd sp!, {r0 - r12, lr} @ save registers on stack | ||
109 | |||
110 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
111 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
112 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
113 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
114 | |||
115 | @ turn off clock domains | ||
116 | @ get ARM_IDLECT2 into r2 | ||
117 | ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
118 | mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff | ||
119 | orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 | ||
120 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
121 | |||
122 | @ request ARM idle | ||
123 | @ get ARM_IDLECT1 into r1 | ||
124 | ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
125 | orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff | ||
126 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
127 | |||
128 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
129 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
130 | l_1610: subs r5, r5, #1 | ||
131 | bne l_1610 | ||
132 | /* | ||
133 | * Let's wait for the next clock tick to wake us up. | ||
134 | */ | ||
135 | mov r0, #0 | ||
136 | mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt | ||
137 | /* | ||
138 | * omap1610_idle_loop_suspend()'s resume point. | ||
139 | * | ||
140 | * It will just start executing here, so we'll restore stuff from the | ||
141 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
142 | */ | ||
143 | |||
144 | @ restore ARM_IDLECT1 and ARM_IDLECT2 and return | ||
145 | @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 | ||
146 | strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
147 | strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
148 | |||
149 | ldmfd sp!, {r0 - r12, pc} @ restore regs and return | ||
150 | |||
151 | ENTRY(omap1610_idle_loop_suspend_sz) | ||
152 | .word . - omap1610_idle_loop_suspend | ||
153 | #endif /* CONFIG_ARCH_OMAP16XX */ | ||
154 | |||
155 | /* | ||
156 | * Forces OMAP into deep sleep state | ||
157 | * | ||
158 | * omapXXXX_cpu_suspend() | ||
159 | * | ||
160 | * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed | ||
161 | * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 | ||
162 | * in register r1. | ||
163 | * | ||
164 | * Note: This code get's copied to internal SRAM at boot. When the OMAP | ||
165 | * wakes up it continues execution at the point it went to sleep. | ||
166 | * | ||
167 | * Note: Because of errata work arounds we have processor specific functions | ||
168 | * here. They are mostly the same, but slightly different. | ||
169 | * | ||
170 | */ | ||
171 | |||
172 | #ifdef CONFIG_ARCH_OMAP1510 | ||
173 | ENTRY(omap1510_cpu_suspend) | ||
174 | |||
175 | @ save registers on stack | ||
176 | stmfd sp!, {r0 - r12, lr} | ||
177 | |||
178 | @ load base address of Traffic Controller | ||
179 | mov r4, #TCMIF_ASM_BASE & 0xff000000 | ||
180 | orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 | ||
181 | orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 | ||
182 | |||
183 | @ work around errata of OMAP1510 PDE bit for TC shut down | ||
184 | @ clear PDE bit | ||
185 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
186 | bic r5, r5, #PDE_BIT & 0xff | ||
187 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
188 | |||
189 | @ set PWD_EN bit | ||
190 | and r5, r5, #PWD_EN_BIT & 0xff | ||
191 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
192 | |||
193 | @ prepare to put SDRAM into self-refresh manually | ||
194 | ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
195 | orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 | ||
196 | orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff | ||
197 | str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
198 | |||
199 | @ prepare to put EMIFS to Sleep | ||
200 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
201 | orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff | ||
202 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
203 | |||
204 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
205 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
206 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
207 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
208 | |||
209 | @ turn off clock domains | ||
210 | mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff | ||
211 | orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 | ||
212 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
213 | |||
214 | @ request ARM idle | ||
215 | mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff | ||
216 | orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 | ||
217 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
218 | |||
219 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
220 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
221 | l_1510_2: | ||
222 | subs r5, r5, #1 | ||
223 | bne l_1510_2 | ||
224 | /* | ||
225 | * Let's wait for the next wake up event to wake us up. r0 can't be | ||
226 | * used here because r0 holds ARM_IDLECT1 | ||
227 | */ | ||
228 | mov r2, #0 | ||
229 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt | ||
230 | /* | ||
231 | * omap1510_cpu_suspend()'s resume point. | ||
232 | * | ||
233 | * It will just start executing here, so we'll restore stuff from the | ||
234 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
235 | */ | ||
236 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
237 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
238 | |||
239 | @ restore regs and return | ||
240 | ldmfd sp!, {r0 - r12, pc} | ||
241 | |||
242 | ENTRY(omap1510_cpu_suspend_sz) | ||
243 | .word . - omap1510_cpu_suspend | ||
244 | #endif /* CONFIG_ARCH_OMAP1510 */ | ||
245 | |||
246 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
247 | ENTRY(omap1610_cpu_suspend) | ||
248 | |||
249 | @ save registers on stack | ||
250 | stmfd sp!, {r0 - r12, lr} | ||
251 | |||
252 | @ load base address of Traffic Controller | ||
253 | mov r4, #TCMIF_ASM_BASE & 0xff000000 | ||
254 | orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 | ||
255 | orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 | ||
256 | |||
257 | @ prepare to put SDRAM into self-refresh manually | ||
258 | ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
259 | orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 | ||
260 | orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff | ||
261 | str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
262 | |||
263 | @ prepare to put EMIFS to Sleep | ||
264 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
265 | orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff | ||
266 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
267 | |||
268 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
269 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
270 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
271 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
272 | |||
273 | @ turn off clock domains | ||
274 | mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff | ||
275 | orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 | ||
276 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
277 | |||
278 | @ work around errata of OMAP1610/5912. Enable (!) peripheral | ||
279 | @ clock to let the chip go into deep sleep | ||
280 | ldrh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
281 | orr r5,r5, #EN_PERCK_BIT & 0xff | ||
282 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
283 | |||
284 | @ request ARM idle | ||
285 | mov r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff | ||
286 | orr r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00 | ||
287 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
288 | |||
289 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
290 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
291 | l_1610_2: | ||
292 | subs r5, r5, #1 | ||
293 | bne l_1610_2 | ||
294 | /* | ||
295 | * Let's wait for the next wake up event to wake us up. r0 can't be | ||
296 | * used here because r0 holds ARM_IDLECT1 | ||
297 | */ | ||
298 | mov r2, #0 | ||
299 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt | ||
300 | /* | ||
301 | * omap1610_cpu_suspend()'s resume point. | ||
302 | * | ||
303 | * It will just start executing here, so we'll restore stuff from the | ||
304 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
305 | */ | ||
306 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
307 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
308 | |||
309 | @ restore regs and return | ||
310 | ldmfd sp!, {r0 - r12, pc} | ||
311 | |||
312 | ENTRY(omap1610_cpu_suspend_sz) | ||
313 | .word . - omap1610_cpu_suspend | ||
314 | #endif /* CONFIG_ARCH_OMAP16XX */ | ||
diff --git a/arch/arm/mach-omap/time.c b/arch/arm/mach-omap/time.c new file mode 100644 index 000000000000..4205fdcb632c --- /dev/null +++ b/arch/arm/mach-omap/time.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap/time.c | ||
3 | * | ||
4 | * OMAP Timers | ||
5 | * | ||
6 | * Copyright (C) 2004 Nokia Corporation | ||
7 | * Partial timer rewrite and additional VST timer support by | ||
8 | * Tony Lindgen <tony@atomide.com> and | ||
9 | * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
10 | * | ||
11 | * MPU timer code based on the older MPU timer code for OMAP | ||
12 | * Copyright (C) 2000 RidgeRun, Inc. | ||
13 | * Author: Greg Lonnon <glonnon@ridgerun.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License as published by the | ||
17 | * Free Software Foundation; either version 2 of the License, or (at your | ||
18 | * option) any later version. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
21 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
23 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
26 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
30 | * | ||
31 | * You should have received a copy of the GNU General Public License along | ||
32 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
33 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
34 | */ | ||
35 | |||
36 | #include <linux/config.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/spinlock.h> | ||
43 | |||
44 | #include <asm/system.h> | ||
45 | #include <asm/hardware.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/leds.h> | ||
48 | #include <asm/irq.h> | ||
49 | #include <asm/mach/irq.h> | ||
50 | #include <asm/mach/time.h> | ||
51 | |||
52 | struct sys_timer omap_timer; | ||
53 | |||
54 | #ifdef CONFIG_OMAP_MPU_TIMER | ||
55 | |||
56 | /* | ||
57 | * --------------------------------------------------------------------------- | ||
58 | * MPU timer | ||
59 | * --------------------------------------------------------------------------- | ||
60 | */ | ||
61 | #define OMAP_MPU_TIMER1_BASE (0xfffec500) | ||
62 | #define OMAP_MPU_TIMER2_BASE (0xfffec600) | ||
63 | #define OMAP_MPU_TIMER3_BASE (0xfffec700) | ||
64 | #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE | ||
65 | #define OMAP_MPU_TIMER_OFFSET 0x100 | ||
66 | |||
67 | #define MPU_TIMER_FREE (1 << 6) | ||
68 | #define MPU_TIMER_CLOCK_ENABLE (1 << 5) | ||
69 | #define MPU_TIMER_AR (1 << 1) | ||
70 | #define MPU_TIMER_ST (1 << 0) | ||
71 | |||
72 | /* cycles to nsec conversions taken from arch/i386/kernel/timers/timer_tsc.c, | ||
73 | * converted to use kHz by Kevin Hilman */ | ||
74 | /* convert from cycles(64bits) => nanoseconds (64bits) | ||
75 | * basic equation: | ||
76 | * ns = cycles / (freq / ns_per_sec) | ||
77 | * ns = cycles * (ns_per_sec / freq) | ||
78 | * ns = cycles * (10^9 / (cpu_khz * 10^3)) | ||
79 | * ns = cycles * (10^6 / cpu_khz) | ||
80 | * | ||
81 | * Then we use scaling math (suggested by george at mvista.com) to get: | ||
82 | * ns = cycles * (10^6 * SC / cpu_khz / SC | ||
83 | * ns = cycles * cyc2ns_scale / SC | ||
84 | * | ||
85 | * And since SC is a constant power of two, we can convert the div | ||
86 | * into a shift. | ||
87 | * -johnstul at us.ibm.com "math is hard, lets go shopping!" | ||
88 | */ | ||
89 | static unsigned long cyc2ns_scale; | ||
90 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | ||
91 | |||
92 | static inline void set_cyc2ns_scale(unsigned long cpu_khz) | ||
93 | { | ||
94 | cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; | ||
95 | } | ||
96 | |||
97 | static inline unsigned long long cycles_2_ns(unsigned long long cyc) | ||
98 | { | ||
99 | return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs | ||
104 | * will break. On P2, the timer count rate is 6.5 MHz after programming PTV | ||
105 | * with 0. This divides the 13MHz input by 2, and is undocumented. | ||
106 | */ | ||
107 | #ifdef CONFIG_MACH_OMAP_PERSEUS2 | ||
108 | /* REVISIT: This ifdef construct should be replaced by a query to clock | ||
109 | * framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz. | ||
110 | */ | ||
111 | #define MPU_TICKS_PER_SEC (13000000 / 2) | ||
112 | #else | ||
113 | #define MPU_TICKS_PER_SEC (12000000 / 2) | ||
114 | #endif | ||
115 | |||
116 | #define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1) | ||
117 | |||
118 | typedef struct { | ||
119 | u32 cntl; /* CNTL_TIMER, R/W */ | ||
120 | u32 load_tim; /* LOAD_TIM, W */ | ||
121 | u32 read_tim; /* READ_TIM, R */ | ||
122 | } omap_mpu_timer_regs_t; | ||
123 | |||
124 | #define omap_mpu_timer_base(n) \ | ||
125 | ((volatile omap_mpu_timer_regs_t*)IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ | ||
126 | (n)*OMAP_MPU_TIMER_OFFSET)) | ||
127 | |||
128 | static inline unsigned long omap_mpu_timer_read(int nr) | ||
129 | { | ||
130 | volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); | ||
131 | return timer->read_tim; | ||
132 | } | ||
133 | |||
134 | static inline void omap_mpu_timer_start(int nr, unsigned long load_val) | ||
135 | { | ||
136 | volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); | ||
137 | |||
138 | timer->cntl = MPU_TIMER_CLOCK_ENABLE; | ||
139 | udelay(1); | ||
140 | timer->load_tim = load_val; | ||
141 | udelay(1); | ||
142 | timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST); | ||
143 | } | ||
144 | |||
145 | unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks) | ||
146 | { | ||
147 | unsigned long long nsec; | ||
148 | |||
149 | nsec = cycles_2_ns((unsigned long long)nr_ticks); | ||
150 | return (unsigned long)nsec / 1000; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * Last processed system timer interrupt | ||
155 | */ | ||
156 | static unsigned long omap_mpu_timer_last = 0; | ||
157 | |||
158 | /* | ||
159 | * Returns elapsed usecs since last system timer interrupt | ||
160 | */ | ||
161 | static unsigned long omap_mpu_timer_gettimeoffset(void) | ||
162 | { | ||
163 | unsigned long now = 0 - omap_mpu_timer_read(0); | ||
164 | unsigned long elapsed = now - omap_mpu_timer_last; | ||
165 | |||
166 | return omap_mpu_timer_ticks_to_usecs(elapsed); | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * Elapsed time between interrupts is calculated using timer0. | ||
171 | * Latency during the interrupt is calculated using timer1. | ||
172 | * Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz). | ||
173 | */ | ||
174 | static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id, | ||
175 | struct pt_regs *regs) | ||
176 | { | ||
177 | unsigned long now, latency; | ||
178 | |||
179 | write_seqlock(&xtime_lock); | ||
180 | now = 0 - omap_mpu_timer_read(0); | ||
181 | latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1); | ||
182 | omap_mpu_timer_last = now - latency; | ||
183 | timer_tick(regs); | ||
184 | write_sequnlock(&xtime_lock); | ||
185 | |||
186 | return IRQ_HANDLED; | ||
187 | } | ||
188 | |||
189 | static struct irqaction omap_mpu_timer_irq = { | ||
190 | .name = "mpu timer", | ||
191 | .flags = SA_INTERRUPT, | ||
192 | .handler = omap_mpu_timer_interrupt | ||
193 | }; | ||
194 | |||
195 | static unsigned long omap_mpu_timer1_overflows; | ||
196 | static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id, | ||
197 | struct pt_regs *regs) | ||
198 | { | ||
199 | omap_mpu_timer1_overflows++; | ||
200 | return IRQ_HANDLED; | ||
201 | } | ||
202 | |||
203 | static struct irqaction omap_mpu_timer1_irq = { | ||
204 | .name = "mpu timer1 overflow", | ||
205 | .flags = SA_INTERRUPT, | ||
206 | .handler = omap_mpu_timer1_interrupt | ||
207 | }; | ||
208 | |||
209 | static __init void omap_init_mpu_timer(void) | ||
210 | { | ||
211 | set_cyc2ns_scale(MPU_TICKS_PER_SEC / 1000); | ||
212 | omap_timer.offset = omap_mpu_timer_gettimeoffset; | ||
213 | setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); | ||
214 | setup_irq(INT_TIMER2, &omap_mpu_timer_irq); | ||
215 | omap_mpu_timer_start(0, 0xffffffff); | ||
216 | omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Scheduler clock - returns current time in nanosec units. | ||
221 | */ | ||
222 | unsigned long long sched_clock(void) | ||
223 | { | ||
224 | unsigned long ticks = 0 - omap_mpu_timer_read(0); | ||
225 | unsigned long long ticks64; | ||
226 | |||
227 | ticks64 = omap_mpu_timer1_overflows; | ||
228 | ticks64 <<= 32; | ||
229 | ticks64 |= ticks; | ||
230 | |||
231 | return cycles_2_ns(ticks64); | ||
232 | } | ||
233 | #endif /* CONFIG_OMAP_MPU_TIMER */ | ||
234 | |||
235 | #ifdef CONFIG_OMAP_32K_TIMER | ||
236 | |||
237 | #ifdef CONFIG_ARCH_OMAP1510 | ||
238 | #error OMAP 32KHz timer does not currently work on 1510! | ||
239 | #endif | ||
240 | |||
241 | /* | ||
242 | * --------------------------------------------------------------------------- | ||
243 | * 32KHz OS timer | ||
244 | * | ||
245 | * This currently works only on 16xx, as 1510 does not have the continuous | ||
246 | * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track | ||
247 | * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer | ||
248 | * on 1510 would be possible, but the timer would not be as accurate as | ||
249 | * with the 32KHz synchronized timer. | ||
250 | * --------------------------------------------------------------------------- | ||
251 | */ | ||
252 | #define OMAP_32K_TIMER_BASE 0xfffb9000 | ||
253 | #define OMAP_32K_TIMER_CR 0x08 | ||
254 | #define OMAP_32K_TIMER_TVR 0x00 | ||
255 | #define OMAP_32K_TIMER_TCR 0x04 | ||
256 | |||
257 | #define OMAP_32K_TICKS_PER_HZ (32768 / HZ) | ||
258 | |||
259 | /* | ||
260 | * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 | ||
261 | * so with HZ = 100, TVR = 327.68. | ||
262 | */ | ||
263 | #define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) | ||
264 | #define MAX_SKIP_JIFFIES 25 | ||
265 | #define TIMER_32K_SYNCHRONIZED 0xfffbc410 | ||
266 | |||
267 | #define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ | ||
268 | (((nr_jiffies) * (clock_rate)) / HZ) | ||
269 | |||
270 | static inline void omap_32k_timer_write(int val, int reg) | ||
271 | { | ||
272 | omap_writew(val, reg + OMAP_32K_TIMER_BASE); | ||
273 | } | ||
274 | |||
275 | static inline unsigned long omap_32k_timer_read(int reg) | ||
276 | { | ||
277 | return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * The 32KHz synchronized timer is an additional timer on 16xx. | ||
282 | * It is always running. | ||
283 | */ | ||
284 | static inline unsigned long omap_32k_sync_timer_read(void) | ||
285 | { | ||
286 | return omap_readl(TIMER_32K_SYNCHRONIZED); | ||
287 | } | ||
288 | |||
289 | static inline void omap_32k_timer_start(unsigned long load_val) | ||
290 | { | ||
291 | omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); | ||
292 | omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); | ||
293 | } | ||
294 | |||
295 | static inline void omap_32k_timer_stop(void) | ||
296 | { | ||
297 | omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * Rounds down to nearest usec | ||
302 | */ | ||
303 | static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) | ||
304 | { | ||
305 | return (ticks_32k * 5*5*5*5*5*5) >> 9; | ||
306 | } | ||
307 | |||
308 | static unsigned long omap_32k_last_tick = 0; | ||
309 | |||
310 | /* | ||
311 | * Returns elapsed usecs since last 32k timer interrupt | ||
312 | */ | ||
313 | static unsigned long omap_32k_timer_gettimeoffset(void) | ||
314 | { | ||
315 | unsigned long now = omap_32k_sync_timer_read(); | ||
316 | return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this | ||
321 | * function is also called from other interrupts to remove latency | ||
322 | * issues with dynamic tick. In the dynamic tick case, we need to lock | ||
323 | * with irqsave. | ||
324 | */ | ||
325 | static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, | ||
326 | struct pt_regs *regs) | ||
327 | { | ||
328 | unsigned long flags; | ||
329 | unsigned long now; | ||
330 | |||
331 | write_seqlock_irqsave(&xtime_lock, flags); | ||
332 | now = omap_32k_sync_timer_read(); | ||
333 | |||
334 | while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { | ||
335 | omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; | ||
336 | timer_tick(regs); | ||
337 | } | ||
338 | |||
339 | /* Restart timer so we don't drift off due to modulo or dynamic tick. | ||
340 | * By default we program the next timer to be continuous to avoid | ||
341 | * latencies during high system load. During dynamic tick operation the | ||
342 | * continuous timer can be overridden from pm_idle to be longer. | ||
343 | */ | ||
344 | omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); | ||
345 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
346 | |||
347 | return IRQ_HANDLED; | ||
348 | } | ||
349 | |||
350 | static struct irqaction omap_32k_timer_irq = { | ||
351 | .name = "32KHz timer", | ||
352 | .flags = SA_INTERRUPT, | ||
353 | .handler = omap_32k_timer_interrupt | ||
354 | }; | ||
355 | |||
356 | static __init void omap_init_32k_timer(void) | ||
357 | { | ||
358 | setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); | ||
359 | omap_timer.offset = omap_32k_timer_gettimeoffset; | ||
360 | omap_32k_last_tick = omap_32k_sync_timer_read(); | ||
361 | omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); | ||
362 | } | ||
363 | #endif /* CONFIG_OMAP_32K_TIMER */ | ||
364 | |||
365 | /* | ||
366 | * --------------------------------------------------------------------------- | ||
367 | * Timer initialization | ||
368 | * --------------------------------------------------------------------------- | ||
369 | */ | ||
370 | void __init omap_timer_init(void) | ||
371 | { | ||
372 | #if defined(CONFIG_OMAP_MPU_TIMER) | ||
373 | omap_init_mpu_timer(); | ||
374 | #elif defined(CONFIG_OMAP_32K_TIMER) | ||
375 | omap_init_32k_timer(); | ||
376 | #else | ||
377 | #error No system timer selected in Kconfig! | ||
378 | #endif | ||
379 | } | ||
380 | |||
381 | struct sys_timer omap_timer = { | ||
382 | .init = omap_timer_init, | ||
383 | .offset = NULL, /* Initialized later */ | ||
384 | }; | ||
diff --git a/arch/arm/mach-omap/usb.c b/arch/arm/mach-omap/usb.c new file mode 100644 index 000000000000..6e805d451d0e --- /dev/null +++ b/arch/arm/mach-omap/usb.c | |||
@@ -0,0 +1,594 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-omap/usb.c -- platform level USB initialization | ||
3 | * | ||
4 | * Copyright (C) 2004 Texas Instruments, Inc. | ||
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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #undef DEBUG | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/usb_otg.h> | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/system.h> | ||
35 | #include <asm/hardware.h> | ||
36 | #include <asm/mach-types.h> | ||
37 | |||
38 | #include <asm/arch/mux.h> | ||
39 | #include <asm/arch/usb.h> | ||
40 | #include <asm/arch/board.h> | ||
41 | |||
42 | /* These routines should handle the standard chip-specific modes | ||
43 | * for usb0/1/2 ports, covering basic mux and transceiver setup. | ||
44 | * Call omap_usb_init() once, from INIT_MACHINE(). | ||
45 | * | ||
46 | * Some board-*.c files will need to set up additional mux options, | ||
47 | * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. | ||
48 | */ | ||
49 | |||
50 | /* TESTED ON: | ||
51 | * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
52 | * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables | ||
53 | * - 5912 OSK UDC, with *nonstandard* A-to-A cable | ||
54 | * - 1510 Innovator UDC with bundled usb0 cable | ||
55 | * - 1510 Innovator OHCI with bundled usb1/usb2 cable | ||
56 | * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS | ||
57 | * - 1710 custom development board using alternate pin group | ||
58 | * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
59 | */ | ||
60 | |||
61 | /*-------------------------------------------------------------------------*/ | ||
62 | |||
63 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
64 | |||
65 | static struct otg_transceiver *xceiv; | ||
66 | |||
67 | /** | ||
68 | * otg_get_transceiver - find the (single) OTG transceiver driver | ||
69 | * | ||
70 | * Returns the transceiver driver, after getting a refcount to it; or | ||
71 | * null if there is no such transceiver. The caller is responsible for | ||
72 | * releasing that count. | ||
73 | */ | ||
74 | struct otg_transceiver *otg_get_transceiver(void) | ||
75 | { | ||
76 | if (xceiv) | ||
77 | get_device(xceiv->dev); | ||
78 | return xceiv; | ||
79 | } | ||
80 | EXPORT_SYMBOL(otg_get_transceiver); | ||
81 | |||
82 | int otg_set_transceiver(struct otg_transceiver *x) | ||
83 | { | ||
84 | if (xceiv && x) | ||
85 | return -EBUSY; | ||
86 | xceiv = x; | ||
87 | return 0; | ||
88 | } | ||
89 | EXPORT_SYMBOL(otg_set_transceiver); | ||
90 | |||
91 | #endif | ||
92 | |||
93 | /*-------------------------------------------------------------------------*/ | ||
94 | |||
95 | static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) | ||
96 | { | ||
97 | u32 syscon1 = 0; | ||
98 | |||
99 | if (nwires == 0) { | ||
100 | if (!cpu_is_omap15xx()) { | ||
101 | /* pulldown D+/D- */ | ||
102 | USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | if (is_device) | ||
108 | omap_cfg_reg(W4_USB_PUEN); | ||
109 | |||
110 | /* internal transceiver */ | ||
111 | if (nwires == 2) { | ||
112 | // omap_cfg_reg(P9_USB_DP); | ||
113 | // omap_cfg_reg(R8_USB_DM); | ||
114 | |||
115 | if (cpu_is_omap15xx()) { | ||
116 | /* This works on 1510-Innovator */ | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /* NOTES: | ||
121 | * - peripheral should configure VBUS detection! | ||
122 | * - only peripherals may use the internal D+/D- pulldowns | ||
123 | * - OTG support on this port not yet written | ||
124 | */ | ||
125 | |||
126 | USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4); | ||
127 | if (!is_device) | ||
128 | USB_TRANSCEIVER_CTRL_REG |= (3 << 1); | ||
129 | |||
130 | return 3 << 16; | ||
131 | } | ||
132 | |||
133 | /* alternate pin config, external transceiver */ | ||
134 | if (cpu_is_omap15xx()) { | ||
135 | printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | omap_cfg_reg(V6_USB0_TXD); | ||
140 | omap_cfg_reg(W9_USB0_TXEN); | ||
141 | omap_cfg_reg(W5_USB0_SE0); | ||
142 | |||
143 | /* NOTE: SPEED and SUSP aren't configured here */ | ||
144 | |||
145 | if (nwires != 3) | ||
146 | omap_cfg_reg(Y5_USB0_RCV); | ||
147 | if (nwires != 6) | ||
148 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
149 | |||
150 | switch (nwires) { | ||
151 | case 3: | ||
152 | syscon1 = 2; | ||
153 | break; | ||
154 | case 4: | ||
155 | syscon1 = 1; | ||
156 | break; | ||
157 | case 6: | ||
158 | syscon1 = 3; | ||
159 | omap_cfg_reg(AA9_USB0_VP); | ||
160 | omap_cfg_reg(R9_USB0_VM); | ||
161 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
162 | break; | ||
163 | default: | ||
164 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
165 | 0, nwires); | ||
166 | } | ||
167 | return syscon1 << 16; | ||
168 | } | ||
169 | |||
170 | static u32 __init omap_usb1_init(unsigned nwires) | ||
171 | { | ||
172 | u32 syscon1 = 0; | ||
173 | |||
174 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
175 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; | ||
176 | if (nwires == 0) | ||
177 | return 0; | ||
178 | |||
179 | /* external transceiver */ | ||
180 | omap_cfg_reg(USB1_TXD); | ||
181 | omap_cfg_reg(USB1_TXEN); | ||
182 | if (cpu_is_omap15xx()) { | ||
183 | omap_cfg_reg(USB1_SEO); | ||
184 | omap_cfg_reg(USB1_SPEED); | ||
185 | // SUSP | ||
186 | } else if (cpu_is_omap1610() || cpu_is_omap5912()) { | ||
187 | omap_cfg_reg(W13_1610_USB1_SE0); | ||
188 | omap_cfg_reg(R13_1610_USB1_SPEED); | ||
189 | // SUSP | ||
190 | } else if (cpu_is_omap1710()) { | ||
191 | omap_cfg_reg(R13_1710_USB1_SE0); | ||
192 | // SUSP | ||
193 | } else { | ||
194 | pr_debug("usb unrecognized\n"); | ||
195 | } | ||
196 | if (nwires != 3) | ||
197 | omap_cfg_reg(USB1_RCV); | ||
198 | |||
199 | switch (nwires) { | ||
200 | case 3: | ||
201 | syscon1 = 2; | ||
202 | break; | ||
203 | case 4: | ||
204 | syscon1 = 1; | ||
205 | break; | ||
206 | case 6: | ||
207 | syscon1 = 3; | ||
208 | omap_cfg_reg(USB1_VP); | ||
209 | omap_cfg_reg(USB1_VM); | ||
210 | if (!cpu_is_omap15xx()) | ||
211 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; | ||
212 | break; | ||
213 | default: | ||
214 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
215 | 1, nwires); | ||
216 | } | ||
217 | return syscon1 << 20; | ||
218 | } | ||
219 | |||
220 | static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) | ||
221 | { | ||
222 | u32 syscon1 = 0; | ||
223 | |||
224 | /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ | ||
225 | if (alt_pingroup || nwires == 0) | ||
226 | return 0; | ||
227 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
228 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
229 | |||
230 | /* external transceiver */ | ||
231 | if (cpu_is_omap15xx()) { | ||
232 | omap_cfg_reg(USB2_TXD); | ||
233 | omap_cfg_reg(USB2_TXEN); | ||
234 | omap_cfg_reg(USB2_SEO); | ||
235 | if (nwires != 3) | ||
236 | omap_cfg_reg(USB2_RCV); | ||
237 | /* there is no USB2_SPEED */ | ||
238 | } else if (cpu_is_omap16xx()) { | ||
239 | omap_cfg_reg(V6_USB2_TXD); | ||
240 | omap_cfg_reg(W9_USB2_TXEN); | ||
241 | omap_cfg_reg(W5_USB2_SE0); | ||
242 | if (nwires != 3) | ||
243 | omap_cfg_reg(Y5_USB2_RCV); | ||
244 | // FIXME omap_cfg_reg(USB2_SPEED); | ||
245 | } else { | ||
246 | pr_debug("usb unrecognized\n"); | ||
247 | } | ||
248 | // omap_cfg_reg(USB2_SUSP); | ||
249 | |||
250 | switch (nwires) { | ||
251 | case 3: | ||
252 | syscon1 = 2; | ||
253 | break; | ||
254 | case 4: | ||
255 | syscon1 = 1; | ||
256 | break; | ||
257 | case 6: | ||
258 | syscon1 = 3; | ||
259 | if (cpu_is_omap15xx()) { | ||
260 | omap_cfg_reg(USB2_VP); | ||
261 | omap_cfg_reg(USB2_VM); | ||
262 | } else { | ||
263 | omap_cfg_reg(AA9_USB2_VP); | ||
264 | omap_cfg_reg(R9_USB2_VM); | ||
265 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
266 | } | ||
267 | break; | ||
268 | default: | ||
269 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
270 | 2, nwires); | ||
271 | } | ||
272 | return syscon1 << 24; | ||
273 | } | ||
274 | |||
275 | /*-------------------------------------------------------------------------*/ | ||
276 | |||
277 | #if defined(CONFIG_USB_GADGET_OMAP) || \ | ||
278 | defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ | ||
279 | (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) | ||
280 | static void usb_release(struct device *dev) | ||
281 | { | ||
282 | /* normally not freed */ | ||
283 | } | ||
284 | #endif | ||
285 | |||
286 | #ifdef CONFIG_USB_GADGET_OMAP | ||
287 | |||
288 | static struct resource udc_resources[] = { | ||
289 | /* order is significant! */ | ||
290 | { /* registers */ | ||
291 | .start = IO_ADDRESS(UDC_BASE), | ||
292 | .end = IO_ADDRESS(UDC_BASE + 0xff), | ||
293 | .flags = IORESOURCE_MEM, | ||
294 | }, { /* general IRQ */ | ||
295 | .start = IH2_BASE + 20, | ||
296 | .flags = IORESOURCE_IRQ, | ||
297 | }, { /* PIO IRQ */ | ||
298 | .start = IH2_BASE + 30, | ||
299 | .flags = IORESOURCE_IRQ, | ||
300 | }, { /* SOF IRQ */ | ||
301 | .start = IH2_BASE + 29, | ||
302 | .flags = IORESOURCE_IRQ, | ||
303 | }, | ||
304 | }; | ||
305 | |||
306 | static u64 udc_dmamask = ~(u32)0; | ||
307 | |||
308 | static struct platform_device udc_device = { | ||
309 | .name = "omap_udc", | ||
310 | .id = -1, | ||
311 | .dev = { | ||
312 | .release = usb_release, | ||
313 | .dma_mask = &udc_dmamask, | ||
314 | .coherent_dma_mask = 0xffffffff, | ||
315 | }, | ||
316 | .num_resources = ARRAY_SIZE(udc_resources), | ||
317 | .resource = udc_resources, | ||
318 | }; | ||
319 | |||
320 | #endif | ||
321 | |||
322 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
323 | |||
324 | /* The dmamask must be set for OHCI to work */ | ||
325 | static u64 ohci_dmamask = ~(u32)0; | ||
326 | |||
327 | static struct resource ohci_resources[] = { | ||
328 | { | ||
329 | .start = OMAP_OHCI_BASE, | ||
330 | .end = OMAP_OHCI_BASE + 4096, | ||
331 | .flags = IORESOURCE_MEM, | ||
332 | }, | ||
333 | { | ||
334 | .start = INT_USB_HHC_1, | ||
335 | .flags = IORESOURCE_IRQ, | ||
336 | }, | ||
337 | }; | ||
338 | |||
339 | static struct platform_device ohci_device = { | ||
340 | .name = "ohci", | ||
341 | .id = -1, | ||
342 | .dev = { | ||
343 | .release = usb_release, | ||
344 | .dma_mask = &ohci_dmamask, | ||
345 | .coherent_dma_mask = 0xffffffff, | ||
346 | }, | ||
347 | .num_resources = ARRAY_SIZE(ohci_resources), | ||
348 | .resource = ohci_resources, | ||
349 | }; | ||
350 | |||
351 | #endif | ||
352 | |||
353 | #if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) | ||
354 | |||
355 | static struct resource otg_resources[] = { | ||
356 | /* order is significant! */ | ||
357 | { | ||
358 | .start = IO_ADDRESS(OTG_BASE), | ||
359 | .end = IO_ADDRESS(OTG_BASE + 0xff), | ||
360 | .flags = IORESOURCE_MEM, | ||
361 | }, { | ||
362 | .start = IH2_BASE + 8, | ||
363 | .flags = IORESOURCE_IRQ, | ||
364 | }, | ||
365 | }; | ||
366 | |||
367 | static struct platform_device otg_device = { | ||
368 | .name = "omap_otg", | ||
369 | .id = -1, | ||
370 | .dev = { | ||
371 | .release = usb_release, | ||
372 | }, | ||
373 | .num_resources = ARRAY_SIZE(otg_resources), | ||
374 | .resource = otg_resources, | ||
375 | }; | ||
376 | |||
377 | #endif | ||
378 | |||
379 | /*-------------------------------------------------------------------------*/ | ||
380 | |||
381 | #define ULPD_CLOCK_CTRL_REG __REG16(ULPD_CLOCK_CTRL) | ||
382 | #define ULPD_SOFT_REQ_REG __REG16(ULPD_SOFT_REQ) | ||
383 | |||
384 | |||
385 | // FIXME correct answer depends on hmc_mode, | ||
386 | // as does any nonzero value for config->otg port number | ||
387 | #ifdef CONFIG_USB_GADGET_OMAP | ||
388 | #define is_usb0_device(config) 1 | ||
389 | #else | ||
390 | #define is_usb0_device(config) 0 | ||
391 | #endif | ||
392 | |||
393 | /*-------------------------------------------------------------------------*/ | ||
394 | |||
395 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
396 | |||
397 | void __init | ||
398 | omap_otg_init(struct omap_usb_config *config) | ||
399 | { | ||
400 | u32 syscon = OTG_SYSCON_1_REG & 0xffff; | ||
401 | int status; | ||
402 | int alt_pingroup = 0; | ||
403 | |||
404 | /* NOTE: no bus or clock setup (yet?) */ | ||
405 | |||
406 | syscon = OTG_SYSCON_1_REG & 0xffff; | ||
407 | if (!(syscon & OTG_RESET_DONE)) | ||
408 | pr_debug("USB resets not complete?\n"); | ||
409 | |||
410 | // OTG_IRQ_EN_REG = 0; | ||
411 | |||
412 | /* pin muxing and transceiver pinouts */ | ||
413 | if (config->pins[0] > 2) /* alt pingroup 2 */ | ||
414 | alt_pingroup = 1; | ||
415 | syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
416 | syscon |= omap_usb1_init(config->pins[1]); | ||
417 | syscon |= omap_usb2_init(config->pins[2], alt_pingroup); | ||
418 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
419 | OTG_SYSCON_1_REG = syscon; | ||
420 | |||
421 | syscon = config->hmc_mode; | ||
422 | syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; | ||
423 | #ifdef CONFIG_USB_OTG | ||
424 | if (config->otg) | ||
425 | syscon |= OTG_EN; | ||
426 | #endif | ||
427 | pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); | ||
428 | pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); | ||
429 | OTG_SYSCON_2_REG = syscon; | ||
430 | |||
431 | printk("USB: hmc %d", config->hmc_mode); | ||
432 | if (alt_pingroup) | ||
433 | printk(", usb2 alt %d wires", config->pins[2]); | ||
434 | else if (config->pins[0]) | ||
435 | printk(", usb0 %d wires%s", config->pins[0], | ||
436 | is_usb0_device(config) ? " (dev)" : ""); | ||
437 | if (config->pins[1]) | ||
438 | printk(", usb1 %d wires", config->pins[1]); | ||
439 | if (!alt_pingroup && config->pins[2]) | ||
440 | printk(", usb2 %d wires", config->pins[2]); | ||
441 | if (config->otg) | ||
442 | printk(", Mini-AB on usb%d", config->otg - 1); | ||
443 | printk("\n"); | ||
444 | |||
445 | /* leave USB clocks/controllers off until needed */ | ||
446 | ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; | ||
447 | ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; | ||
448 | ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; | ||
449 | syscon = OTG_SYSCON_1_REG; | ||
450 | syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; | ||
451 | |||
452 | #ifdef CONFIG_USB_GADGET_OMAP | ||
453 | if (config->otg || config->register_dev) { | ||
454 | syscon &= ~DEV_IDLE_EN; | ||
455 | udc_device.dev.platform_data = config; | ||
456 | /* FIXME patch IRQ numbers for omap730 */ | ||
457 | status = platform_device_register(&udc_device); | ||
458 | if (status) | ||
459 | pr_debug("can't register UDC device, %d\n", status); | ||
460 | } | ||
461 | #endif | ||
462 | |||
463 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
464 | if (config->otg || config->register_host) { | ||
465 | syscon &= ~HST_IDLE_EN; | ||
466 | ohci_device.dev.platform_data = config; | ||
467 | if (cpu_is_omap730()) | ||
468 | ohci_resources[1].start = INT_730_USB_HHC_1; | ||
469 | status = platform_device_register(&ohci_device); | ||
470 | if (status) | ||
471 | pr_debug("can't register OHCI device, %d\n", status); | ||
472 | } | ||
473 | #endif | ||
474 | |||
475 | #ifdef CONFIG_USB_OTG | ||
476 | if (config->otg) { | ||
477 | syscon &= ~OTG_IDLE_EN; | ||
478 | otg_device.dev.platform_data = config; | ||
479 | if (cpu_is_omap730()) | ||
480 | otg_resources[1].start = INT_730_USB_OTG; | ||
481 | status = platform_device_register(&otg_device); | ||
482 | if (status) | ||
483 | pr_debug("can't register OTG device, %d\n", status); | ||
484 | } | ||
485 | #endif | ||
486 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
487 | OTG_SYSCON_1_REG = syscon; | ||
488 | |||
489 | status = 0; | ||
490 | } | ||
491 | |||
492 | #else | ||
493 | static inline void omap_otg_init(struct omap_usb_config *config) {} | ||
494 | #endif | ||
495 | |||
496 | /*-------------------------------------------------------------------------*/ | ||
497 | |||
498 | #ifdef CONFIG_ARCH_OMAP1510 | ||
499 | |||
500 | #define ULPD_DPLL_CTRL_REG __REG16(ULPD_DPLL_CTRL) | ||
501 | #define DPLL_IOB (1 << 13) | ||
502 | #define DPLL_PLL_ENABLE (1 << 4) | ||
503 | #define DPLL_LOCK (1 << 0) | ||
504 | |||
505 | #define ULPD_APLL_CTRL_REG __REG16(ULPD_APLL_CTRL) | ||
506 | #define APLL_NDPLL_SWITCH (1 << 0) | ||
507 | |||
508 | |||
509 | static void __init omap_1510_usb_init(struct omap_usb_config *config) | ||
510 | { | ||
511 | int status; | ||
512 | unsigned int val; | ||
513 | |||
514 | omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
515 | omap_usb1_init(config->pins[1]); | ||
516 | omap_usb2_init(config->pins[2], 0); | ||
517 | |||
518 | val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); | ||
519 | val |= (config->hmc_mode << 1); | ||
520 | omap_writel(val, MOD_CONF_CTRL_0); | ||
521 | |||
522 | printk("USB: hmc %d", config->hmc_mode); | ||
523 | if (config->pins[0]) | ||
524 | printk(", usb0 %d wires%s", config->pins[0], | ||
525 | is_usb0_device(config) ? " (dev)" : ""); | ||
526 | if (config->pins[1]) | ||
527 | printk(", usb1 %d wires", config->pins[1]); | ||
528 | if (config->pins[2]) | ||
529 | printk(", usb2 %d wires", config->pins[2]); | ||
530 | printk("\n"); | ||
531 | |||
532 | /* use DPLL for 48 MHz function clock */ | ||
533 | pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG, | ||
534 | ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG); | ||
535 | ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH; | ||
536 | ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE; | ||
537 | ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ; | ||
538 | while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK)) | ||
539 | cpu_relax(); | ||
540 | |||
541 | #ifdef CONFIG_USB_GADGET_OMAP | ||
542 | if (config->register_dev) { | ||
543 | udc_device.dev.platform_data = config; | ||
544 | status = platform_device_register(&udc_device); | ||
545 | if (status) | ||
546 | pr_debug("can't register UDC device, %d\n", status); | ||
547 | /* udc driver gates 48MHz by D+ pullup */ | ||
548 | } | ||
549 | #endif | ||
550 | |||
551 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
552 | if (config->register_host) { | ||
553 | ohci_device.dev.platform_data = config; | ||
554 | status = platform_device_register(&ohci_device); | ||
555 | if (status) | ||
556 | pr_debug("can't register OHCI device, %d\n", status); | ||
557 | /* hcd explicitly gates 48MHz */ | ||
558 | } | ||
559 | #endif | ||
560 | } | ||
561 | |||
562 | #else | ||
563 | static inline void omap_1510_usb_init(struct omap_usb_config *config) {} | ||
564 | #endif | ||
565 | |||
566 | /*-------------------------------------------------------------------------*/ | ||
567 | |||
568 | static struct omap_usb_config platform_data; | ||
569 | |||
570 | static int __init | ||
571 | omap_usb_init(void) | ||
572 | { | ||
573 | const struct omap_usb_config *config; | ||
574 | |||
575 | config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); | ||
576 | if (config == NULL) { | ||
577 | printk(KERN_ERR "USB: No board-specific " | ||
578 | "platform config found\n"); | ||
579 | return -ENODEV; | ||
580 | } | ||
581 | platform_data = *config; | ||
582 | |||
583 | if (cpu_is_omap730() || cpu_is_omap16xx()) | ||
584 | omap_otg_init(&platform_data); | ||
585 | else if (cpu_is_omap15xx()) | ||
586 | omap_1510_usb_init(&platform_data); | ||
587 | else { | ||
588 | printk(KERN_ERR "USB: No init for your chip yet\n"); | ||
589 | return -ENODEV; | ||
590 | } | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | subsys_initcall(omap_usb_init); | ||