diff options
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 112 | ||||
-rw-r--r-- | arch/arm/plat-omap/Makefile | 17 | ||||
-rw-r--r-- | arch/arm/plat-omap/clock.c | 1323 | ||||
-rw-r--r-- | arch/arm/plat-omap/clock.h | 120 | ||||
-rw-r--r-- | arch/arm/plat-omap/common.c | 135 | ||||
-rw-r--r-- | arch/arm/plat-omap/cpu-omap.c | 128 | ||||
-rw-r--r-- | arch/arm/plat-omap/dma.c | 1116 | ||||
-rw-r--r-- | arch/arm/plat-omap/gpio.c | 762 | ||||
-rw-r--r-- | arch/arm/plat-omap/mcbsp.c | 758 | ||||
-rw-r--r-- | arch/arm/plat-omap/mux.c | 160 | ||||
-rw-r--r-- | arch/arm/plat-omap/ocpi.c | 114 | ||||
-rw-r--r-- | arch/arm/plat-omap/pm.c | 632 | ||||
-rw-r--r-- | arch/arm/plat-omap/sleep.S | 314 | ||||
-rw-r--r-- | arch/arm/plat-omap/usb.c | 593 |
14 files changed, 6284 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig new file mode 100644 index 000000000000..345365852f8c --- /dev/null +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -0,0 +1,112 @@ | |||
1 | if ARCH_OMAP | ||
2 | |||
3 | menu "TI OMAP Implementations" | ||
4 | |||
5 | config ARCH_OMAP_OTG | ||
6 | bool | ||
7 | |||
8 | choice | ||
9 | prompt "OMAP System Type" | ||
10 | default ARCH_OMAP1 | ||
11 | |||
12 | config ARCH_OMAP1 | ||
13 | bool "TI OMAP1" | ||
14 | |||
15 | config ARCH_OMAP2 | ||
16 | bool "TI OMAP2" | ||
17 | |||
18 | endchoice | ||
19 | |||
20 | comment "OMAP Feature Selections" | ||
21 | |||
22 | config OMAP_RESET_CLOCKS | ||
23 | bool "Reset unused clocks during boot" | ||
24 | depends on ARCH_OMAP | ||
25 | default n | ||
26 | help | ||
27 | Say Y if you want to reset unused clocks during boot. | ||
28 | This option saves power, but assumes all drivers are | ||
29 | using the clock framework. Broken drivers that do not | ||
30 | yet use clock framework may not work with this option. | ||
31 | If you are booting from another operating system, you | ||
32 | probably do not want this option enabled until your | ||
33 | device drivers work properly. | ||
34 | |||
35 | config OMAP_MUX | ||
36 | bool "OMAP multiplexing support" | ||
37 | depends on ARCH_OMAP | ||
38 | default y | ||
39 | help | ||
40 | Pin multiplexing support for OMAP boards. If your bootloader | ||
41 | sets the multiplexing correctly, say N. Otherwise, or if unsure, | ||
42 | say Y. | ||
43 | |||
44 | config OMAP_MUX_DEBUG | ||
45 | bool "Multiplexing debug output" | ||
46 | depends on OMAP_MUX | ||
47 | default n | ||
48 | help | ||
49 | Makes the multiplexing functions print out a lot of debug info. | ||
50 | This is useful if you want to find out the correct values of the | ||
51 | multiplexing registers. | ||
52 | |||
53 | config OMAP_MUX_WARNINGS | ||
54 | bool "Warn about pins the bootloader didn't set up" | ||
55 | depends on OMAP_MUX | ||
56 | default y | ||
57 | help | ||
58 | Choose Y here to warn whenever driver initialization logic needs | ||
59 | to change the pin multiplexing setup. When there are no warnings | ||
60 | printed, it's safe to deselect OMAP_MUX for your product. | ||
61 | |||
62 | choice | ||
63 | prompt "System timer" | ||
64 | default OMAP_MPU_TIMER | ||
65 | |||
66 | config OMAP_MPU_TIMER | ||
67 | bool "Use mpu timer" | ||
68 | help | ||
69 | Select this option if you want to use the OMAP mpu timer. This | ||
70 | timer provides more intra-tick resolution than the 32KHz timer, | ||
71 | but consumes more power. | ||
72 | |||
73 | config OMAP_32K_TIMER | ||
74 | bool "Use 32KHz timer" | ||
75 | depends on ARCH_OMAP16XX | ||
76 | help | ||
77 | Select this option if you want to enable the OMAP 32KHz timer. | ||
78 | This timer saves power compared to the OMAP_MPU_TIMER, and has | ||
79 | support for no tick during idle. The 32KHz timer provides less | ||
80 | intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is | ||
81 | currently only available for OMAP-16xx. | ||
82 | |||
83 | endchoice | ||
84 | |||
85 | config OMAP_32K_TIMER_HZ | ||
86 | int "Kernel internal timer frequency for 32KHz timer" | ||
87 | range 32 1024 | ||
88 | depends on OMAP_32K_TIMER | ||
89 | default "128" | ||
90 | help | ||
91 | Kernel internal timer frequency should be a divisor of 32768, | ||
92 | such as 64 or 128. | ||
93 | |||
94 | choice | ||
95 | prompt "Low-level debug console UART" | ||
96 | depends on ARCH_OMAP | ||
97 | default OMAP_LL_DEBUG_UART1 | ||
98 | |||
99 | config OMAP_LL_DEBUG_UART1 | ||
100 | bool "UART1" | ||
101 | |||
102 | config OMAP_LL_DEBUG_UART2 | ||
103 | bool "UART2" | ||
104 | |||
105 | config OMAP_LL_DEBUG_UART3 | ||
106 | bool "UART3" | ||
107 | |||
108 | endchoice | ||
109 | |||
110 | endmenu | ||
111 | |||
112 | endif | ||
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile new file mode 100644 index 000000000000..531e11af54d4 --- /dev/null +++ b/arch/arm/plat-omap/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | |||
5 | # Common support | ||
6 | obj-y := common.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o | ||
7 | obj-m := | ||
8 | obj-n := | ||
9 | obj- := | ||
10 | |||
11 | # OCPI interconnect support for 1710, 1610 and 5912 | ||
12 | obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o | ||
13 | |||
14 | # Power Management | ||
15 | obj-$(CONFIG_PM) += pm.o sleep.o | ||
16 | |||
17 | obj-$(CONFIG_CPU_FREQ) += cpu-omap.o | ||
diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c new file mode 100644 index 000000000000..59d91b3262ba --- /dev/null +++ b/arch/arm/plat-omap/clock.c | |||
@@ -0,0 +1,1323 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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/io.h> | ||
18 | #include <asm/semaphore.h> | ||
19 | #include <asm/hardware/clock.h> | ||
20 | #include <asm/arch/board.h> | ||
21 | #include <asm/arch/usb.h> | ||
22 | |||
23 | #include "clock.h" | ||
24 | |||
25 | static LIST_HEAD(clocks); | ||
26 | static DECLARE_MUTEX(clocks_sem); | ||
27 | static DEFINE_SPINLOCK(clockfw_lock); | ||
28 | static void propagate_rate(struct clk * clk); | ||
29 | /* UART clock function */ | ||
30 | static int set_uart_rate(struct clk * clk, unsigned long rate); | ||
31 | /* External clock (MCLK & BCLK) functions */ | ||
32 | static int set_ext_clk_rate(struct clk * clk, unsigned long rate); | ||
33 | static long round_ext_clk_rate(struct clk * clk, unsigned long rate); | ||
34 | static void init_ext_clk(struct clk * clk); | ||
35 | /* MPU virtual clock functions */ | ||
36 | static int select_table_rate(struct clk * clk, unsigned long rate); | ||
37 | static long round_to_table_rate(struct clk * clk, unsigned long rate); | ||
38 | void clk_setdpll(__u16, __u16); | ||
39 | |||
40 | static struct mpu_rate rate_table[] = { | ||
41 | /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL | ||
42 | * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv | ||
43 | */ | ||
44 | #if defined(CONFIG_OMAP_ARM_216MHZ) | ||
45 | { 216000000, 12000000, 216000000, 0x050d, 0x2910 }, /* 1/1/2/2/2/8 */ | ||
46 | #endif | ||
47 | #if defined(CONFIG_OMAP_ARM_195MHZ) | ||
48 | { 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */ | ||
49 | #endif | ||
50 | #if defined(CONFIG_OMAP_ARM_192MHZ) | ||
51 | { 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */ | ||
52 | { 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */ | ||
53 | { 96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */ | ||
54 | { 48000000, 12000000, 192000000, 0x0baf, 0x2810 }, /* 4/8/4/4/8/8 */ | ||
55 | { 24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */ | ||
56 | #endif | ||
57 | #if defined(CONFIG_OMAP_ARM_182MHZ) | ||
58 | { 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */ | ||
59 | #endif | ||
60 | #if defined(CONFIG_OMAP_ARM_168MHZ) | ||
61 | { 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */ | ||
62 | #endif | ||
63 | #if defined(CONFIG_OMAP_ARM_150MHZ) | ||
64 | { 150000000, 12000000, 150000000, 0x010a, 0x2cb0 }, /* 1/1/1/2/4/4 */ | ||
65 | #endif | ||
66 | #if defined(CONFIG_OMAP_ARM_120MHZ) | ||
67 | { 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */ | ||
68 | #endif | ||
69 | #if defined(CONFIG_OMAP_ARM_96MHZ) | ||
70 | { 96000000, 12000000, 96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */ | ||
71 | #endif | ||
72 | #if defined(CONFIG_OMAP_ARM_60MHZ) | ||
73 | { 60000000, 12000000, 60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */ | ||
74 | #endif | ||
75 | #if defined(CONFIG_OMAP_ARM_30MHZ) | ||
76 | { 30000000, 12000000, 60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */ | ||
77 | #endif | ||
78 | { 0, 0, 0, 0, 0 }, | ||
79 | }; | ||
80 | |||
81 | |||
82 | static void ckctl_recalc(struct clk * clk); | ||
83 | int __clk_enable(struct clk *clk); | ||
84 | void __clk_disable(struct clk *clk); | ||
85 | void __clk_unuse(struct clk *clk); | ||
86 | int __clk_use(struct clk *clk); | ||
87 | |||
88 | |||
89 | static void followparent_recalc(struct clk * clk) | ||
90 | { | ||
91 | clk->rate = clk->parent->rate; | ||
92 | } | ||
93 | |||
94 | |||
95 | static void watchdog_recalc(struct clk * clk) | ||
96 | { | ||
97 | clk->rate = clk->parent->rate / 14; | ||
98 | } | ||
99 | |||
100 | static void uart_recalc(struct clk * clk) | ||
101 | { | ||
102 | unsigned int val = omap_readl(clk->enable_reg); | ||
103 | if (val & clk->enable_bit) | ||
104 | clk->rate = 48000000; | ||
105 | else | ||
106 | clk->rate = 12000000; | ||
107 | } | ||
108 | |||
109 | static struct clk ck_ref = { | ||
110 | .name = "ck_ref", | ||
111 | .rate = 12000000, | ||
112 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
113 | ALWAYS_ENABLED, | ||
114 | }; | ||
115 | |||
116 | static struct clk ck_dpll1 = { | ||
117 | .name = "ck_dpll1", | ||
118 | .parent = &ck_ref, | ||
119 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
120 | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
121 | }; | ||
122 | |||
123 | static struct clk ck_dpll1out = { | ||
124 | .name = "ck_dpll1out", | ||
125 | .parent = &ck_dpll1, | ||
126 | .flags = CLOCK_IN_OMAP16XX, | ||
127 | .enable_reg = ARM_IDLECT2, | ||
128 | .enable_bit = EN_CKOUT_ARM, | ||
129 | .recalc = &followparent_recalc, | ||
130 | }; | ||
131 | |||
132 | static struct clk arm_ck = { | ||
133 | .name = "arm_ck", | ||
134 | .parent = &ck_dpll1, | ||
135 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
136 | RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
137 | .rate_offset = CKCTL_ARMDIV_OFFSET, | ||
138 | .recalc = &ckctl_recalc, | ||
139 | }; | ||
140 | |||
141 | static struct clk armper_ck = { | ||
142 | .name = "armper_ck", | ||
143 | .parent = &ck_dpll1, | ||
144 | .flags = CLOCK_IN_OMAP730 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
145 | RATE_CKCTL, | ||
146 | .enable_reg = ARM_IDLECT2, | ||
147 | .enable_bit = EN_PERCK, | ||
148 | .rate_offset = CKCTL_PERDIV_OFFSET, | ||
149 | .recalc = &ckctl_recalc, | ||
150 | }; | ||
151 | |||
152 | static struct clk arm_gpio_ck = { | ||
153 | .name = "arm_gpio_ck", | ||
154 | .parent = &ck_dpll1, | ||
155 | .flags = CLOCK_IN_OMAP1510, | ||
156 | .enable_reg = ARM_IDLECT2, | ||
157 | .enable_bit = EN_GPIOCK, | ||
158 | .recalc = &followparent_recalc, | ||
159 | }; | ||
160 | |||
161 | static struct clk armxor_ck = { | ||
162 | .name = "armxor_ck", | ||
163 | .parent = &ck_ref, | ||
164 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
165 | .enable_reg = ARM_IDLECT2, | ||
166 | .enable_bit = EN_XORPCK, | ||
167 | .recalc = &followparent_recalc, | ||
168 | }; | ||
169 | |||
170 | static struct clk armtim_ck = { | ||
171 | .name = "armtim_ck", | ||
172 | .parent = &ck_ref, | ||
173 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
174 | .enable_reg = ARM_IDLECT2, | ||
175 | .enable_bit = EN_TIMCK, | ||
176 | .recalc = &followparent_recalc, | ||
177 | }; | ||
178 | |||
179 | static struct clk armwdt_ck = { | ||
180 | .name = "armwdt_ck", | ||
181 | .parent = &ck_ref, | ||
182 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
183 | .enable_reg = ARM_IDLECT2, | ||
184 | .enable_bit = EN_WDTCK, | ||
185 | .recalc = &watchdog_recalc, | ||
186 | }; | ||
187 | |||
188 | static struct clk arminth_ck16xx = { | ||
189 | .name = "arminth_ck", | ||
190 | .parent = &arm_ck, | ||
191 | .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, | ||
192 | .recalc = &followparent_recalc, | ||
193 | /* Note: On 16xx the frequency can be divided by 2 by programming | ||
194 | * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 | ||
195 | * | ||
196 | * 1510 version is in TC clocks. | ||
197 | */ | ||
198 | }; | ||
199 | |||
200 | static struct clk dsp_ck = { | ||
201 | .name = "dsp_ck", | ||
202 | .parent = &ck_dpll1, | ||
203 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
204 | RATE_CKCTL, | ||
205 | .enable_reg = ARM_CKCTL, | ||
206 | .enable_bit = EN_DSPCK, | ||
207 | .rate_offset = CKCTL_DSPDIV_OFFSET, | ||
208 | .recalc = &ckctl_recalc, | ||
209 | }; | ||
210 | |||
211 | static struct clk dspmmu_ck = { | ||
212 | .name = "dspmmu_ck", | ||
213 | .parent = &ck_dpll1, | ||
214 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
215 | RATE_CKCTL | ALWAYS_ENABLED, | ||
216 | .rate_offset = CKCTL_DSPMMUDIV_OFFSET, | ||
217 | .recalc = &ckctl_recalc, | ||
218 | }; | ||
219 | |||
220 | static struct clk dspper_ck = { | ||
221 | .name = "dspper_ck", | ||
222 | .parent = &ck_dpll1, | ||
223 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
224 | RATE_CKCTL | DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, | ||
225 | .enable_reg = DSP_IDLECT2, | ||
226 | .enable_bit = EN_PERCK, | ||
227 | .rate_offset = CKCTL_PERDIV_OFFSET, | ||
228 | .recalc = &followparent_recalc, | ||
229 | //.recalc = &ckctl_recalc, | ||
230 | }; | ||
231 | |||
232 | static struct clk dspxor_ck = { | ||
233 | .name = "dspxor_ck", | ||
234 | .parent = &ck_ref, | ||
235 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
236 | DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, | ||
237 | .enable_reg = DSP_IDLECT2, | ||
238 | .enable_bit = EN_XORPCK, | ||
239 | .recalc = &followparent_recalc, | ||
240 | }; | ||
241 | |||
242 | static struct clk dsptim_ck = { | ||
243 | .name = "dsptim_ck", | ||
244 | .parent = &ck_ref, | ||
245 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
246 | DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, | ||
247 | .enable_reg = DSP_IDLECT2, | ||
248 | .enable_bit = EN_DSPTIMCK, | ||
249 | .recalc = &followparent_recalc, | ||
250 | }; | ||
251 | |||
252 | static struct clk tc_ck = { | ||
253 | .name = "tc_ck", | ||
254 | .parent = &ck_dpll1, | ||
255 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | | ||
256 | RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, | ||
257 | .rate_offset = CKCTL_TCDIV_OFFSET, | ||
258 | .recalc = &ckctl_recalc, | ||
259 | }; | ||
260 | |||
261 | static struct clk arminth_ck1510 = { | ||
262 | .name = "arminth_ck", | ||
263 | .parent = &tc_ck, | ||
264 | .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, | ||
265 | .recalc = &followparent_recalc, | ||
266 | /* Note: On 1510 the frequency follows TC_CK | ||
267 | * | ||
268 | * 16xx version is in MPU clocks. | ||
269 | */ | ||
270 | }; | ||
271 | |||
272 | static struct clk tipb_ck = { | ||
273 | .name = "tibp_ck", | ||
274 | .parent = &tc_ck, | ||
275 | .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, | ||
276 | .recalc = &followparent_recalc, | ||
277 | }; | ||
278 | |||
279 | static struct clk l3_ocpi_ck = { | ||
280 | .name = "l3_ocpi_ck", | ||
281 | .parent = &tc_ck, | ||
282 | .flags = CLOCK_IN_OMAP16XX, | ||
283 | .enable_reg = ARM_IDLECT3, | ||
284 | .enable_bit = EN_OCPI_CK, | ||
285 | .recalc = &followparent_recalc, | ||
286 | }; | ||
287 | |||
288 | static struct clk tc1_ck = { | ||
289 | .name = "tc1_ck", | ||
290 | .parent = &tc_ck, | ||
291 | .flags = CLOCK_IN_OMAP16XX, | ||
292 | .enable_reg = ARM_IDLECT3, | ||
293 | .enable_bit = EN_TC1_CK, | ||
294 | .recalc = &followparent_recalc, | ||
295 | }; | ||
296 | |||
297 | static struct clk tc2_ck = { | ||
298 | .name = "tc2_ck", | ||
299 | .parent = &tc_ck, | ||
300 | .flags = CLOCK_IN_OMAP16XX, | ||
301 | .enable_reg = ARM_IDLECT3, | ||
302 | .enable_bit = EN_TC2_CK, | ||
303 | .recalc = &followparent_recalc, | ||
304 | }; | ||
305 | |||
306 | static struct clk dma_ck = { | ||
307 | .name = "dma_ck", | ||
308 | .parent = &tc_ck, | ||
309 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
310 | ALWAYS_ENABLED, | ||
311 | .recalc = &followparent_recalc, | ||
312 | }; | ||
313 | |||
314 | static struct clk dma_lcdfree_ck = { | ||
315 | .name = "dma_lcdfree_ck", | ||
316 | .parent = &tc_ck, | ||
317 | .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, | ||
318 | .recalc = &followparent_recalc, | ||
319 | }; | ||
320 | |||
321 | static struct clk api_ck = { | ||
322 | .name = "api_ck", | ||
323 | .parent = &tc_ck, | ||
324 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | ||
325 | .enable_reg = ARM_IDLECT2, | ||
326 | .enable_bit = EN_APICK, | ||
327 | .recalc = &followparent_recalc, | ||
328 | }; | ||
329 | |||
330 | static struct clk lb_ck = { | ||
331 | .name = "lb_ck", | ||
332 | .parent = &tc_ck, | ||
333 | .flags = CLOCK_IN_OMAP1510, | ||
334 | .enable_reg = ARM_IDLECT2, | ||
335 | .enable_bit = EN_LBCK, | ||
336 | .recalc = &followparent_recalc, | ||
337 | }; | ||
338 | |||
339 | static struct clk rhea1_ck = { | ||
340 | .name = "rhea1_ck", | ||
341 | .parent = &tc_ck, | ||
342 | .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, | ||
343 | .recalc = &followparent_recalc, | ||
344 | }; | ||
345 | |||
346 | static struct clk rhea2_ck = { | ||
347 | .name = "rhea2_ck", | ||
348 | .parent = &tc_ck, | ||
349 | .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, | ||
350 | .recalc = &followparent_recalc, | ||
351 | }; | ||
352 | |||
353 | static struct clk lcd_ck = { | ||
354 | .name = "lcd_ck", | ||
355 | .parent = &ck_dpll1, | ||
356 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | | ||
357 | RATE_CKCTL, | ||
358 | .enable_reg = ARM_IDLECT2, | ||
359 | .enable_bit = EN_LCDCK, | ||
360 | .rate_offset = CKCTL_LCDDIV_OFFSET, | ||
361 | .recalc = &ckctl_recalc, | ||
362 | }; | ||
363 | |||
364 | static struct clk uart1_1510 = { | ||
365 | .name = "uart1_ck", | ||
366 | /* Direct from ULPD, no parent */ | ||
367 | .rate = 12000000, | ||
368 | .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED, | ||
369 | .enable_reg = MOD_CONF_CTRL_0, | ||
370 | .enable_bit = 29, /* Chooses between 12MHz and 48MHz */ | ||
371 | .set_rate = &set_uart_rate, | ||
372 | .recalc = &uart_recalc, | ||
373 | }; | ||
374 | |||
375 | static struct clk uart1_16xx = { | ||
376 | .name = "uart1_ck", | ||
377 | /* Direct from ULPD, no parent */ | ||
378 | .rate = 48000000, | ||
379 | .flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT, | ||
380 | .enable_reg = MOD_CONF_CTRL_0, | ||
381 | .enable_bit = 29, | ||
382 | }; | ||
383 | |||
384 | static struct clk uart2_ck = { | ||
385 | .name = "uart2_ck", | ||
386 | /* Direct from ULPD, no parent */ | ||
387 | .rate = 12000000, | ||
388 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ENABLE_REG_32BIT, | ||
389 | .enable_reg = MOD_CONF_CTRL_0, | ||
390 | .enable_bit = 30, /* Chooses between 12MHz and 48MHz */ | ||
391 | .set_rate = &set_uart_rate, | ||
392 | .recalc = &uart_recalc, | ||
393 | }; | ||
394 | |||
395 | static struct clk uart3_1510 = { | ||
396 | .name = "uart3_ck", | ||
397 | /* Direct from ULPD, no parent */ | ||
398 | .rate = 12000000, | ||
399 | .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED, | ||
400 | .enable_reg = MOD_CONF_CTRL_0, | ||
401 | .enable_bit = 31, /* Chooses between 12MHz and 48MHz */ | ||
402 | .set_rate = &set_uart_rate, | ||
403 | .recalc = &uart_recalc, | ||
404 | }; | ||
405 | |||
406 | static struct clk uart3_16xx = { | ||
407 | .name = "uart3_ck", | ||
408 | /* Direct from ULPD, no parent */ | ||
409 | .rate = 48000000, | ||
410 | .flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT, | ||
411 | .enable_reg = MOD_CONF_CTRL_0, | ||
412 | .enable_bit = 31, | ||
413 | }; | ||
414 | |||
415 | static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ | ||
416 | .name = "usb_clko", | ||
417 | /* Direct from ULPD, no parent */ | ||
418 | .rate = 6000000, | ||
419 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
420 | RATE_FIXED | ENABLE_REG_32BIT, | ||
421 | .enable_reg = ULPD_CLOCK_CTRL, | ||
422 | .enable_bit = USB_MCLK_EN_BIT, | ||
423 | }; | ||
424 | |||
425 | static struct clk usb_hhc_ck1510 = { | ||
426 | .name = "usb_hhc_ck", | ||
427 | /* Direct from ULPD, no parent */ | ||
428 | .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ | ||
429 | .flags = CLOCK_IN_OMAP1510 | | ||
430 | RATE_FIXED | ENABLE_REG_32BIT, | ||
431 | .enable_reg = MOD_CONF_CTRL_0, | ||
432 | .enable_bit = USB_HOST_HHC_UHOST_EN, | ||
433 | }; | ||
434 | |||
435 | static struct clk usb_hhc_ck16xx = { | ||
436 | .name = "usb_hhc_ck", | ||
437 | /* Direct from ULPD, no parent */ | ||
438 | .rate = 48000000, | ||
439 | /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ | ||
440 | .flags = CLOCK_IN_OMAP16XX | | ||
441 | RATE_FIXED | ENABLE_REG_32BIT, | ||
442 | .enable_reg = OTG_BASE + 0x08 /* OTG_SYSCON_2 */, | ||
443 | .enable_bit = 8 /* UHOST_EN */, | ||
444 | }; | ||
445 | |||
446 | static struct clk mclk_1510 = { | ||
447 | .name = "mclk", | ||
448 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
449 | .rate = 12000000, | ||
450 | .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, | ||
451 | }; | ||
452 | |||
453 | static struct clk mclk_16xx = { | ||
454 | .name = "mclk", | ||
455 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
456 | .flags = CLOCK_IN_OMAP16XX, | ||
457 | .enable_reg = COM_CLK_DIV_CTRL_SEL, | ||
458 | .enable_bit = COM_ULPD_PLL_CLK_REQ, | ||
459 | .set_rate = &set_ext_clk_rate, | ||
460 | .round_rate = &round_ext_clk_rate, | ||
461 | .init = &init_ext_clk, | ||
462 | }; | ||
463 | |||
464 | static struct clk bclk_1510 = { | ||
465 | .name = "bclk", | ||
466 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
467 | .rate = 12000000, | ||
468 | .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, | ||
469 | }; | ||
470 | |||
471 | static struct clk bclk_16xx = { | ||
472 | .name = "bclk", | ||
473 | /* Direct from ULPD, no parent. May be enabled by ext hardware. */ | ||
474 | .flags = CLOCK_IN_OMAP16XX, | ||
475 | .enable_reg = SWD_CLK_DIV_CTRL_SEL, | ||
476 | .enable_bit = SWD_ULPD_PLL_CLK_REQ, | ||
477 | .set_rate = &set_ext_clk_rate, | ||
478 | .round_rate = &round_ext_clk_rate, | ||
479 | .init = &init_ext_clk, | ||
480 | }; | ||
481 | |||
482 | static struct clk mmc1_ck = { | ||
483 | .name = "mmc1_ck", | ||
484 | /* Functional clock is direct from ULPD, interface clock is ARMPER */ | ||
485 | .parent = &armper_ck, | ||
486 | .rate = 48000000, | ||
487 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
488 | RATE_FIXED | ENABLE_REG_32BIT, | ||
489 | .enable_reg = MOD_CONF_CTRL_0, | ||
490 | .enable_bit = 23, | ||
491 | }; | ||
492 | |||
493 | static struct clk mmc2_ck = { | ||
494 | .name = "mmc2_ck", | ||
495 | /* Functional clock is direct from ULPD, interface clock is ARMPER */ | ||
496 | .parent = &armper_ck, | ||
497 | .rate = 48000000, | ||
498 | .flags = CLOCK_IN_OMAP16XX | | ||
499 | RATE_FIXED | ENABLE_REG_32BIT, | ||
500 | .enable_reg = MOD_CONF_CTRL_0, | ||
501 | .enable_bit = 20, | ||
502 | }; | ||
503 | |||
504 | static struct clk virtual_ck_mpu = { | ||
505 | .name = "mpu", | ||
506 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | | ||
507 | VIRTUAL_CLOCK | ALWAYS_ENABLED, | ||
508 | .parent = &arm_ck, /* Is smarter alias for */ | ||
509 | .recalc = &followparent_recalc, | ||
510 | .set_rate = &select_table_rate, | ||
511 | .round_rate = &round_to_table_rate, | ||
512 | }; | ||
513 | |||
514 | |||
515 | static struct clk * onchip_clks[] = { | ||
516 | /* non-ULPD clocks */ | ||
517 | &ck_ref, | ||
518 | &ck_dpll1, | ||
519 | /* CK_GEN1 clocks */ | ||
520 | &ck_dpll1out, | ||
521 | &arm_ck, | ||
522 | &armper_ck, | ||
523 | &arm_gpio_ck, | ||
524 | &armxor_ck, | ||
525 | &armtim_ck, | ||
526 | &armwdt_ck, | ||
527 | &arminth_ck1510, &arminth_ck16xx, | ||
528 | /* CK_GEN2 clocks */ | ||
529 | &dsp_ck, | ||
530 | &dspmmu_ck, | ||
531 | &dspper_ck, | ||
532 | &dspxor_ck, | ||
533 | &dsptim_ck, | ||
534 | /* CK_GEN3 clocks */ | ||
535 | &tc_ck, | ||
536 | &tipb_ck, | ||
537 | &l3_ocpi_ck, | ||
538 | &tc1_ck, | ||
539 | &tc2_ck, | ||
540 | &dma_ck, | ||
541 | &dma_lcdfree_ck, | ||
542 | &api_ck, | ||
543 | &lb_ck, | ||
544 | &rhea1_ck, | ||
545 | &rhea2_ck, | ||
546 | &lcd_ck, | ||
547 | /* ULPD clocks */ | ||
548 | &uart1_1510, | ||
549 | &uart1_16xx, | ||
550 | &uart2_ck, | ||
551 | &uart3_1510, | ||
552 | &uart3_16xx, | ||
553 | &usb_clko, | ||
554 | &usb_hhc_ck1510, &usb_hhc_ck16xx, | ||
555 | &mclk_1510, &mclk_16xx, | ||
556 | &bclk_1510, &bclk_16xx, | ||
557 | &mmc1_ck, | ||
558 | &mmc2_ck, | ||
559 | /* Virtual clocks */ | ||
560 | &virtual_ck_mpu, | ||
561 | }; | ||
562 | |||
563 | struct clk *clk_get(struct device *dev, const char *id) | ||
564 | { | ||
565 | struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
566 | |||
567 | down(&clocks_sem); | ||
568 | list_for_each_entry(p, &clocks, node) { | ||
569 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
570 | clk = p; | ||
571 | break; | ||
572 | } | ||
573 | } | ||
574 | up(&clocks_sem); | ||
575 | |||
576 | return clk; | ||
577 | } | ||
578 | EXPORT_SYMBOL(clk_get); | ||
579 | |||
580 | |||
581 | void clk_put(struct clk *clk) | ||
582 | { | ||
583 | if (clk && !IS_ERR(clk)) | ||
584 | module_put(clk->owner); | ||
585 | } | ||
586 | EXPORT_SYMBOL(clk_put); | ||
587 | |||
588 | |||
589 | int __clk_enable(struct clk *clk) | ||
590 | { | ||
591 | __u16 regval16; | ||
592 | __u32 regval32; | ||
593 | |||
594 | if (clk->flags & ALWAYS_ENABLED) | ||
595 | return 0; | ||
596 | |||
597 | if (unlikely(clk->enable_reg == 0)) { | ||
598 | printk(KERN_ERR "clock.c: Enable for %s without enable code\n", | ||
599 | clk->name); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | if (clk->flags & DSP_DOMAIN_CLOCK) { | ||
604 | __clk_use(&api_ck); | ||
605 | } | ||
606 | |||
607 | if (clk->flags & ENABLE_REG_32BIT) { | ||
608 | if (clk->flags & VIRTUAL_IO_ADDRESS) { | ||
609 | regval32 = __raw_readl(clk->enable_reg); | ||
610 | regval32 |= (1 << clk->enable_bit); | ||
611 | __raw_writel(regval32, clk->enable_reg); | ||
612 | } else { | ||
613 | regval32 = omap_readl(clk->enable_reg); | ||
614 | regval32 |= (1 << clk->enable_bit); | ||
615 | omap_writel(regval32, clk->enable_reg); | ||
616 | } | ||
617 | } else { | ||
618 | if (clk->flags & VIRTUAL_IO_ADDRESS) { | ||
619 | regval16 = __raw_readw(clk->enable_reg); | ||
620 | regval16 |= (1 << clk->enable_bit); | ||
621 | __raw_writew(regval16, clk->enable_reg); | ||
622 | } else { | ||
623 | regval16 = omap_readw(clk->enable_reg); | ||
624 | regval16 |= (1 << clk->enable_bit); | ||
625 | omap_writew(regval16, clk->enable_reg); | ||
626 | } | ||
627 | } | ||
628 | |||
629 | if (clk->flags & DSP_DOMAIN_CLOCK) { | ||
630 | __clk_unuse(&api_ck); | ||
631 | } | ||
632 | |||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | |||
637 | void __clk_disable(struct clk *clk) | ||
638 | { | ||
639 | __u16 regval16; | ||
640 | __u32 regval32; | ||
641 | |||
642 | if (clk->enable_reg == 0) | ||
643 | return; | ||
644 | |||
645 | if (clk->flags & DSP_DOMAIN_CLOCK) { | ||
646 | __clk_use(&api_ck); | ||
647 | } | ||
648 | |||
649 | if (clk->flags & ENABLE_REG_32BIT) { | ||
650 | if (clk->flags & VIRTUAL_IO_ADDRESS) { | ||
651 | regval32 = __raw_readl(clk->enable_reg); | ||
652 | regval32 &= ~(1 << clk->enable_bit); | ||
653 | __raw_writel(regval32, clk->enable_reg); | ||
654 | } else { | ||
655 | regval32 = omap_readl(clk->enable_reg); | ||
656 | regval32 &= ~(1 << clk->enable_bit); | ||
657 | omap_writel(regval32, clk->enable_reg); | ||
658 | } | ||
659 | } else { | ||
660 | if (clk->flags & VIRTUAL_IO_ADDRESS) { | ||
661 | regval16 = __raw_readw(clk->enable_reg); | ||
662 | regval16 &= ~(1 << clk->enable_bit); | ||
663 | __raw_writew(regval16, clk->enable_reg); | ||
664 | } else { | ||
665 | regval16 = omap_readw(clk->enable_reg); | ||
666 | regval16 &= ~(1 << clk->enable_bit); | ||
667 | omap_writew(regval16, clk->enable_reg); | ||
668 | } | ||
669 | } | ||
670 | |||
671 | if (clk->flags & DSP_DOMAIN_CLOCK) { | ||
672 | __clk_unuse(&api_ck); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | |||
677 | void __clk_unuse(struct clk *clk) | ||
678 | { | ||
679 | if (clk->usecount > 0 && !(--clk->usecount)) { | ||
680 | __clk_disable(clk); | ||
681 | if (likely(clk->parent)) | ||
682 | __clk_unuse(clk->parent); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | |||
687 | int __clk_use(struct clk *clk) | ||
688 | { | ||
689 | int ret = 0; | ||
690 | if (clk->usecount++ == 0) { | ||
691 | if (likely(clk->parent)) | ||
692 | ret = __clk_use(clk->parent); | ||
693 | |||
694 | if (unlikely(ret != 0)) { | ||
695 | clk->usecount--; | ||
696 | return ret; | ||
697 | } | ||
698 | |||
699 | ret = __clk_enable(clk); | ||
700 | |||
701 | if (unlikely(ret != 0) && clk->parent) { | ||
702 | __clk_unuse(clk->parent); | ||
703 | clk->usecount--; | ||
704 | } | ||
705 | } | ||
706 | |||
707 | return ret; | ||
708 | } | ||
709 | |||
710 | |||
711 | int clk_enable(struct clk *clk) | ||
712 | { | ||
713 | unsigned long flags; | ||
714 | int ret; | ||
715 | |||
716 | spin_lock_irqsave(&clockfw_lock, flags); | ||
717 | ret = __clk_enable(clk); | ||
718 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
719 | return ret; | ||
720 | } | ||
721 | EXPORT_SYMBOL(clk_enable); | ||
722 | |||
723 | |||
724 | void clk_disable(struct clk *clk) | ||
725 | { | ||
726 | unsigned long flags; | ||
727 | |||
728 | spin_lock_irqsave(&clockfw_lock, flags); | ||
729 | __clk_disable(clk); | ||
730 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
731 | } | ||
732 | EXPORT_SYMBOL(clk_disable); | ||
733 | |||
734 | |||
735 | int clk_use(struct clk *clk) | ||
736 | { | ||
737 | unsigned long flags; | ||
738 | int ret = 0; | ||
739 | |||
740 | spin_lock_irqsave(&clockfw_lock, flags); | ||
741 | ret = __clk_use(clk); | ||
742 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
743 | return ret; | ||
744 | } | ||
745 | EXPORT_SYMBOL(clk_use); | ||
746 | |||
747 | |||
748 | void clk_unuse(struct clk *clk) | ||
749 | { | ||
750 | unsigned long flags; | ||
751 | |||
752 | spin_lock_irqsave(&clockfw_lock, flags); | ||
753 | __clk_unuse(clk); | ||
754 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
755 | } | ||
756 | EXPORT_SYMBOL(clk_unuse); | ||
757 | |||
758 | |||
759 | int clk_get_usecount(struct clk *clk) | ||
760 | { | ||
761 | return clk->usecount; | ||
762 | } | ||
763 | EXPORT_SYMBOL(clk_get_usecount); | ||
764 | |||
765 | |||
766 | unsigned long clk_get_rate(struct clk *clk) | ||
767 | { | ||
768 | return clk->rate; | ||
769 | } | ||
770 | EXPORT_SYMBOL(clk_get_rate); | ||
771 | |||
772 | |||
773 | static __u16 verify_ckctl_value(__u16 newval) | ||
774 | { | ||
775 | /* This function checks for following limitations set | ||
776 | * by the hardware (all conditions must be true): | ||
777 | * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 | ||
778 | * ARM_CK >= TC_CK | ||
779 | * DSP_CK >= TC_CK | ||
780 | * DSPMMU_CK >= TC_CK | ||
781 | * | ||
782 | * In addition following rules are enforced: | ||
783 | * LCD_CK <= TC_CK | ||
784 | * ARMPER_CK <= TC_CK | ||
785 | * | ||
786 | * However, maximum frequencies are not checked for! | ||
787 | */ | ||
788 | __u8 per_exp; | ||
789 | __u8 lcd_exp; | ||
790 | __u8 arm_exp; | ||
791 | __u8 dsp_exp; | ||
792 | __u8 tc_exp; | ||
793 | __u8 dspmmu_exp; | ||
794 | |||
795 | per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; | ||
796 | lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; | ||
797 | arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; | ||
798 | dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; | ||
799 | tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; | ||
800 | dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; | ||
801 | |||
802 | if (dspmmu_exp < dsp_exp) | ||
803 | dspmmu_exp = dsp_exp; | ||
804 | if (dspmmu_exp > dsp_exp+1) | ||
805 | dspmmu_exp = dsp_exp+1; | ||
806 | if (tc_exp < arm_exp) | ||
807 | tc_exp = arm_exp; | ||
808 | if (tc_exp < dspmmu_exp) | ||
809 | tc_exp = dspmmu_exp; | ||
810 | if (tc_exp > lcd_exp) | ||
811 | lcd_exp = tc_exp; | ||
812 | if (tc_exp > per_exp) | ||
813 | per_exp = tc_exp; | ||
814 | |||
815 | newval &= 0xf000; | ||
816 | newval |= per_exp << CKCTL_PERDIV_OFFSET; | ||
817 | newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; | ||
818 | newval |= arm_exp << CKCTL_ARMDIV_OFFSET; | ||
819 | newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; | ||
820 | newval |= tc_exp << CKCTL_TCDIV_OFFSET; | ||
821 | newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; | ||
822 | |||
823 | return newval; | ||
824 | } | ||
825 | |||
826 | |||
827 | static int calc_dsor_exp(struct clk *clk, unsigned long rate) | ||
828 | { | ||
829 | /* Note: If target frequency is too low, this function will return 4, | ||
830 | * which is invalid value. Caller must check for this value and act | ||
831 | * accordingly. | ||
832 | * | ||
833 | * Note: This function does not check for following limitations set | ||
834 | * by the hardware (all conditions must be true): | ||
835 | * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 | ||
836 | * ARM_CK >= TC_CK | ||
837 | * DSP_CK >= TC_CK | ||
838 | * DSPMMU_CK >= TC_CK | ||
839 | */ | ||
840 | unsigned long realrate; | ||
841 | struct clk * parent; | ||
842 | unsigned dsor_exp; | ||
843 | |||
844 | if (unlikely(!(clk->flags & RATE_CKCTL))) | ||
845 | return -EINVAL; | ||
846 | |||
847 | parent = clk->parent; | ||
848 | if (unlikely(parent == 0)) | ||
849 | return -EIO; | ||
850 | |||
851 | realrate = parent->rate; | ||
852 | for (dsor_exp=0; dsor_exp<4; dsor_exp++) { | ||
853 | if (realrate <= rate) | ||
854 | break; | ||
855 | |||
856 | realrate /= 2; | ||
857 | } | ||
858 | |||
859 | return dsor_exp; | ||
860 | } | ||
861 | |||
862 | |||
863 | static void ckctl_recalc(struct clk * clk) | ||
864 | { | ||
865 | int dsor; | ||
866 | |||
867 | /* Calculate divisor encoded as 2-bit exponent */ | ||
868 | if (clk->flags & DSP_DOMAIN_CLOCK) { | ||
869 | /* The clock control bits are in DSP domain, | ||
870 | * so api_ck is needed for access. | ||
871 | * Note that DSP_CKCTL virt addr = phys addr, so | ||
872 | * we must use __raw_readw() instead of omap_readw(). | ||
873 | */ | ||
874 | __clk_use(&api_ck); | ||
875 | dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset)); | ||
876 | __clk_unuse(&api_ck); | ||
877 | } else { | ||
878 | dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); | ||
879 | } | ||
880 | if (unlikely(clk->rate == clk->parent->rate / dsor)) | ||
881 | return; /* No change, quick exit */ | ||
882 | clk->rate = clk->parent->rate / dsor; | ||
883 | |||
884 | if (unlikely(clk->flags & RATE_PROPAGATES)) | ||
885 | propagate_rate(clk); | ||
886 | } | ||
887 | |||
888 | |||
889 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
890 | { | ||
891 | int dsor_exp; | ||
892 | |||
893 | if (clk->flags & RATE_FIXED) | ||
894 | return clk->rate; | ||
895 | |||
896 | if (clk->flags & RATE_CKCTL) { | ||
897 | dsor_exp = calc_dsor_exp(clk, rate); | ||
898 | if (dsor_exp < 0) | ||
899 | return dsor_exp; | ||
900 | if (dsor_exp > 3) | ||
901 | dsor_exp = 3; | ||
902 | return clk->parent->rate / (1 << dsor_exp); | ||
903 | } | ||
904 | |||
905 | if(clk->round_rate != 0) | ||
906 | return clk->round_rate(clk, rate); | ||
907 | |||
908 | return clk->rate; | ||
909 | } | ||
910 | EXPORT_SYMBOL(clk_round_rate); | ||
911 | |||
912 | |||
913 | static void propagate_rate(struct clk * clk) | ||
914 | { | ||
915 | struct clk ** clkp; | ||
916 | |||
917 | for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { | ||
918 | if (likely((*clkp)->parent != clk)) continue; | ||
919 | if (likely((*clkp)->recalc)) | ||
920 | (*clkp)->recalc(*clkp); | ||
921 | } | ||
922 | } | ||
923 | |||
924 | |||
925 | static int select_table_rate(struct clk * clk, unsigned long rate) | ||
926 | { | ||
927 | /* Find the highest supported frequency <= rate and switch to it */ | ||
928 | struct mpu_rate * ptr; | ||
929 | |||
930 | if (clk != &virtual_ck_mpu) | ||
931 | return -EINVAL; | ||
932 | |||
933 | for (ptr = rate_table; ptr->rate; ptr++) { | ||
934 | if (ptr->xtal != ck_ref.rate) | ||
935 | continue; | ||
936 | |||
937 | /* DPLL1 cannot be reprogrammed without risking system crash */ | ||
938 | if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate) | ||
939 | continue; | ||
940 | |||
941 | /* Can check only after xtal frequency check */ | ||
942 | if (ptr->rate <= rate) | ||
943 | break; | ||
944 | } | ||
945 | |||
946 | if (!ptr->rate) | ||
947 | return -EINVAL; | ||
948 | |||
949 | if (!ptr->rate) | ||
950 | return -EINVAL; | ||
951 | |||
952 | if (unlikely(ck_dpll1.rate == 0)) { | ||
953 | omap_writew(ptr->dpllctl_val, DPLL_CTL); | ||
954 | ck_dpll1.rate = ptr->pll_rate; | ||
955 | } | ||
956 | omap_writew(ptr->ckctl_val, ARM_CKCTL); | ||
957 | propagate_rate(&ck_dpll1); | ||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | |||
962 | static long round_to_table_rate(struct clk * clk, unsigned long rate) | ||
963 | { | ||
964 | /* Find the highest supported frequency <= rate */ | ||
965 | struct mpu_rate * ptr; | ||
966 | long highest_rate; | ||
967 | |||
968 | if (clk != &virtual_ck_mpu) | ||
969 | return -EINVAL; | ||
970 | |||
971 | highest_rate = -EINVAL; | ||
972 | |||
973 | for (ptr = rate_table; ptr->rate; ptr++) { | ||
974 | if (ptr->xtal != ck_ref.rate) | ||
975 | continue; | ||
976 | |||
977 | highest_rate = ptr->rate; | ||
978 | |||
979 | /* Can check only after xtal frequency check */ | ||
980 | if (ptr->rate <= rate) | ||
981 | break; | ||
982 | } | ||
983 | |||
984 | return highest_rate; | ||
985 | } | ||
986 | |||
987 | |||
988 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
989 | { | ||
990 | int ret = -EINVAL; | ||
991 | int dsor_exp; | ||
992 | __u16 regval; | ||
993 | unsigned long flags; | ||
994 | |||
995 | if (clk->flags & RATE_CKCTL) { | ||
996 | dsor_exp = calc_dsor_exp(clk, rate); | ||
997 | if (dsor_exp > 3) | ||
998 | dsor_exp = -EINVAL; | ||
999 | if (dsor_exp < 0) | ||
1000 | return dsor_exp; | ||
1001 | |||
1002 | spin_lock_irqsave(&clockfw_lock, flags); | ||
1003 | regval = omap_readw(ARM_CKCTL); | ||
1004 | regval &= ~(3 << clk->rate_offset); | ||
1005 | regval |= dsor_exp << clk->rate_offset; | ||
1006 | regval = verify_ckctl_value(regval); | ||
1007 | omap_writew(regval, ARM_CKCTL); | ||
1008 | clk->rate = clk->parent->rate / (1 << dsor_exp); | ||
1009 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
1010 | ret = 0; | ||
1011 | } else if(clk->set_rate != 0) { | ||
1012 | spin_lock_irqsave(&clockfw_lock, flags); | ||
1013 | ret = clk->set_rate(clk, rate); | ||
1014 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
1015 | } | ||
1016 | |||
1017 | if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) | ||
1018 | propagate_rate(clk); | ||
1019 | |||
1020 | return ret; | ||
1021 | } | ||
1022 | EXPORT_SYMBOL(clk_set_rate); | ||
1023 | |||
1024 | |||
1025 | static unsigned calc_ext_dsor(unsigned long rate) | ||
1026 | { | ||
1027 | unsigned dsor; | ||
1028 | |||
1029 | /* MCLK and BCLK divisor selection is not linear: | ||
1030 | * freq = 96MHz / dsor | ||
1031 | * | ||
1032 | * RATIO_SEL range: dsor <-> RATIO_SEL | ||
1033 | * 0..6: (RATIO_SEL+2) <-> (dsor-2) | ||
1034 | * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) | ||
1035 | * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 | ||
1036 | * can not be used. | ||
1037 | */ | ||
1038 | for (dsor = 2; dsor < 96; ++dsor) { | ||
1039 | if ((dsor & 1) && dsor > 8) | ||
1040 | continue; | ||
1041 | if (rate >= 96000000 / dsor) | ||
1042 | break; | ||
1043 | } | ||
1044 | return dsor; | ||
1045 | } | ||
1046 | |||
1047 | /* Only needed on 1510 */ | ||
1048 | static int set_uart_rate(struct clk * clk, unsigned long rate) | ||
1049 | { | ||
1050 | unsigned int val; | ||
1051 | |||
1052 | val = omap_readl(clk->enable_reg); | ||
1053 | if (rate == 12000000) | ||
1054 | val &= ~(1 << clk->enable_bit); | ||
1055 | else if (rate == 48000000) | ||
1056 | val |= (1 << clk->enable_bit); | ||
1057 | else | ||
1058 | return -EINVAL; | ||
1059 | omap_writel(val, clk->enable_reg); | ||
1060 | clk->rate = rate; | ||
1061 | |||
1062 | return 0; | ||
1063 | } | ||
1064 | |||
1065 | static int set_ext_clk_rate(struct clk * clk, unsigned long rate) | ||
1066 | { | ||
1067 | unsigned dsor; | ||
1068 | __u16 ratio_bits; | ||
1069 | |||
1070 | dsor = calc_ext_dsor(rate); | ||
1071 | clk->rate = 96000000 / dsor; | ||
1072 | if (dsor > 8) | ||
1073 | ratio_bits = ((dsor - 8) / 2 + 6) << 2; | ||
1074 | else | ||
1075 | ratio_bits = (dsor - 2) << 2; | ||
1076 | |||
1077 | ratio_bits |= omap_readw(clk->enable_reg) & ~0xfd; | ||
1078 | omap_writew(ratio_bits, clk->enable_reg); | ||
1079 | |||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | static long round_ext_clk_rate(struct clk * clk, unsigned long rate) | ||
1085 | { | ||
1086 | return 96000000 / calc_ext_dsor(rate); | ||
1087 | } | ||
1088 | |||
1089 | |||
1090 | static void init_ext_clk(struct clk * clk) | ||
1091 | { | ||
1092 | unsigned dsor; | ||
1093 | __u16 ratio_bits; | ||
1094 | |||
1095 | /* Determine current rate and ensure clock is based on 96MHz APLL */ | ||
1096 | ratio_bits = omap_readw(clk->enable_reg) & ~1; | ||
1097 | omap_writew(ratio_bits, clk->enable_reg); | ||
1098 | |||
1099 | ratio_bits = (ratio_bits & 0xfc) >> 2; | ||
1100 | if (ratio_bits > 6) | ||
1101 | dsor = (ratio_bits - 6) * 2 + 8; | ||
1102 | else | ||
1103 | dsor = ratio_bits + 2; | ||
1104 | |||
1105 | clk-> rate = 96000000 / dsor; | ||
1106 | } | ||
1107 | |||
1108 | |||
1109 | int clk_register(struct clk *clk) | ||
1110 | { | ||
1111 | down(&clocks_sem); | ||
1112 | list_add(&clk->node, &clocks); | ||
1113 | if (clk->init) | ||
1114 | clk->init(clk); | ||
1115 | up(&clocks_sem); | ||
1116 | return 0; | ||
1117 | } | ||
1118 | EXPORT_SYMBOL(clk_register); | ||
1119 | |||
1120 | void clk_unregister(struct clk *clk) | ||
1121 | { | ||
1122 | down(&clocks_sem); | ||
1123 | list_del(&clk->node); | ||
1124 | up(&clocks_sem); | ||
1125 | } | ||
1126 | EXPORT_SYMBOL(clk_unregister); | ||
1127 | |||
1128 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
1129 | /* | ||
1130 | * Resets some clocks that may be left on from bootloader, | ||
1131 | * but leaves serial clocks on. See also omap_late_clk_reset(). | ||
1132 | */ | ||
1133 | static inline void omap_early_clk_reset(void) | ||
1134 | { | ||
1135 | //omap_writel(0x3 << 29, MOD_CONF_CTRL_0); | ||
1136 | } | ||
1137 | #else | ||
1138 | #define omap_early_clk_reset() {} | ||
1139 | #endif | ||
1140 | |||
1141 | int __init clk_init(void) | ||
1142 | { | ||
1143 | struct clk ** clkp; | ||
1144 | const struct omap_clock_config *info; | ||
1145 | int crystal_type = 0; /* Default 12 MHz */ | ||
1146 | |||
1147 | omap_early_clk_reset(); | ||
1148 | |||
1149 | for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { | ||
1150 | if (((*clkp)->flags &CLOCK_IN_OMAP1510) && cpu_is_omap1510()) { | ||
1151 | clk_register(*clkp); | ||
1152 | continue; | ||
1153 | } | ||
1154 | |||
1155 | if (((*clkp)->flags &CLOCK_IN_OMAP16XX) && cpu_is_omap16xx()) { | ||
1156 | clk_register(*clkp); | ||
1157 | continue; | ||
1158 | } | ||
1159 | |||
1160 | if (((*clkp)->flags &CLOCK_IN_OMAP730) && cpu_is_omap730()) { | ||
1161 | clk_register(*clkp); | ||
1162 | continue; | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); | ||
1167 | if (info != NULL) { | ||
1168 | if (!cpu_is_omap1510()) | ||
1169 | crystal_type = info->system_clock_type; | ||
1170 | } | ||
1171 | |||
1172 | #if defined(CONFIG_ARCH_OMAP730) | ||
1173 | ck_ref.rate = 13000000; | ||
1174 | #elif defined(CONFIG_ARCH_OMAP16XX) | ||
1175 | if (crystal_type == 2) | ||
1176 | ck_ref.rate = 19200000; | ||
1177 | #endif | ||
1178 | |||
1179 | printk("Clocks: ARM_SYSST: 0x%04x DPLL_CTL: 0x%04x ARM_CKCTL: 0x%04x\n", | ||
1180 | omap_readw(ARM_SYSST), omap_readw(DPLL_CTL), | ||
1181 | omap_readw(ARM_CKCTL)); | ||
1182 | |||
1183 | /* We want to be in syncronous scalable mode */ | ||
1184 | omap_writew(0x1000, ARM_SYSST); | ||
1185 | |||
1186 | #ifdef CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER | ||
1187 | /* Use values set by bootloader. Determine PLL rate and recalculate | ||
1188 | * dependent clocks as if kernel had changed PLL or divisors. | ||
1189 | */ | ||
1190 | { | ||
1191 | unsigned pll_ctl_val = omap_readw(DPLL_CTL); | ||
1192 | |||
1193 | ck_dpll1.rate = ck_ref.rate; /* Base xtal rate */ | ||
1194 | if (pll_ctl_val & 0x10) { | ||
1195 | /* PLL enabled, apply multiplier and divisor */ | ||
1196 | if (pll_ctl_val & 0xf80) | ||
1197 | ck_dpll1.rate *= (pll_ctl_val & 0xf80) >> 7; | ||
1198 | ck_dpll1.rate /= ((pll_ctl_val & 0x60) >> 5) + 1; | ||
1199 | } else { | ||
1200 | /* PLL disabled, apply bypass divisor */ | ||
1201 | switch (pll_ctl_val & 0xc) { | ||
1202 | case 0: | ||
1203 | break; | ||
1204 | case 0x4: | ||
1205 | ck_dpll1.rate /= 2; | ||
1206 | break; | ||
1207 | default: | ||
1208 | ck_dpll1.rate /= 4; | ||
1209 | break; | ||
1210 | } | ||
1211 | } | ||
1212 | } | ||
1213 | propagate_rate(&ck_dpll1); | ||
1214 | #else | ||
1215 | /* Find the highest supported frequency and enable it */ | ||
1216 | if (select_table_rate(&virtual_ck_mpu, ~0)) { | ||
1217 | printk(KERN_ERR "System frequencies not set. Check your config.\n"); | ||
1218 | /* Guess sane values (60MHz) */ | ||
1219 | omap_writew(0x2290, DPLL_CTL); | ||
1220 | omap_writew(0x1005, ARM_CKCTL); | ||
1221 | ck_dpll1.rate = 60000000; | ||
1222 | propagate_rate(&ck_dpll1); | ||
1223 | } | ||
1224 | #endif | ||
1225 | /* Cache rates for clocks connected to ck_ref (not dpll1) */ | ||
1226 | propagate_rate(&ck_ref); | ||
1227 | printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld.%01ld/%ld/%ld MHz\n", | ||
1228 | ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10, | ||
1229 | ck_dpll1.rate, arm_ck.rate); | ||
1230 | |||
1231 | #ifdef CONFIG_MACH_OMAP_PERSEUS2 | ||
1232 | /* Select slicer output as OMAP input clock */ | ||
1233 | omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL); | ||
1234 | #endif | ||
1235 | |||
1236 | /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */ | ||
1237 | omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); | ||
1238 | |||
1239 | /* Put DSP/MPUI into reset until needed */ | ||
1240 | omap_writew(0, ARM_RSTCT1); | ||
1241 | omap_writew(1, ARM_RSTCT2); | ||
1242 | omap_writew(0x400, ARM_IDLECT1); | ||
1243 | |||
1244 | /* | ||
1245 | * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) | ||
1246 | * of the ARM_IDLECT2 register must be set to zero. The power-on | ||
1247 | * default value of this bit is one. | ||
1248 | */ | ||
1249 | omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ | ||
1250 | |||
1251 | /* | ||
1252 | * Only enable those clocks we will need, let the drivers | ||
1253 | * enable other clocks as necessary | ||
1254 | */ | ||
1255 | clk_use(&armper_ck); | ||
1256 | clk_use(&armxor_ck); | ||
1257 | clk_use(&armtim_ck); | ||
1258 | |||
1259 | if (cpu_is_omap1510()) | ||
1260 | clk_enable(&arm_gpio_ck); | ||
1261 | |||
1262 | return 0; | ||
1263 | } | ||
1264 | |||
1265 | |||
1266 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
1267 | |||
1268 | static int __init omap_late_clk_reset(void) | ||
1269 | { | ||
1270 | /* Turn off all unused clocks */ | ||
1271 | struct clk *p; | ||
1272 | __u32 regval32; | ||
1273 | |||
1274 | omap_writew(0, SOFT_REQ_REG); | ||
1275 | omap_writew(0, SOFT_REQ_REG2); | ||
1276 | |||
1277 | list_for_each_entry(p, &clocks, node) { | ||
1278 | if (p->usecount > 0 || (p->flags & ALWAYS_ENABLED) || | ||
1279 | p->enable_reg == 0) | ||
1280 | continue; | ||
1281 | |||
1282 | /* Assume no DSP clocks have been activated by bootloader */ | ||
1283 | if (p->flags & DSP_DOMAIN_CLOCK) | ||
1284 | continue; | ||
1285 | |||
1286 | /* Is the clock already disabled? */ | ||
1287 | if (p->flags & ENABLE_REG_32BIT) { | ||
1288 | if (p->flags & VIRTUAL_IO_ADDRESS) | ||
1289 | regval32 = __raw_readl(p->enable_reg); | ||
1290 | else | ||
1291 | regval32 = omap_readl(p->enable_reg); | ||
1292 | } else { | ||
1293 | if (p->flags & VIRTUAL_IO_ADDRESS) | ||
1294 | regval32 = __raw_readw(p->enable_reg); | ||
1295 | else | ||
1296 | regval32 = omap_readw(p->enable_reg); | ||
1297 | } | ||
1298 | |||
1299 | if ((regval32 & (1 << p->enable_bit)) == 0) | ||
1300 | continue; | ||
1301 | |||
1302 | /* FIXME: This clock seems to be necessary but no-one | ||
1303 | * has asked for its activation. */ | ||
1304 | if (p == &tc2_ck // FIX: pm.c (SRAM), CCP, Camera | ||
1305 | || p == &ck_dpll1out // FIX: SoSSI, SSR | ||
1306 | || p == &arm_gpio_ck // FIX: GPIO code for 1510 | ||
1307 | ) { | ||
1308 | printk(KERN_INFO "FIXME: Clock \"%s\" seems unused\n", | ||
1309 | p->name); | ||
1310 | continue; | ||
1311 | } | ||
1312 | |||
1313 | printk(KERN_INFO "Disabling unused clock \"%s\"... ", p->name); | ||
1314 | __clk_disable(p); | ||
1315 | printk(" done\n"); | ||
1316 | } | ||
1317 | |||
1318 | return 0; | ||
1319 | } | ||
1320 | |||
1321 | late_initcall(omap_late_clk_reset); | ||
1322 | |||
1323 | #endif | ||
diff --git a/arch/arm/plat-omap/clock.h b/arch/arm/plat-omap/clock.h new file mode 100644 index 000000000000..a89e1e8c2519 --- /dev/null +++ b/arch/arm/plat-omap/clock.h | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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 | #define DSP_DOMAIN_CLOCK 512 | ||
56 | #define VIRTUAL_IO_ADDRESS 1024 | ||
57 | |||
58 | /* ARM_CKCTL bit shifts */ | ||
59 | #define CKCTL_PERDIV_OFFSET 0 | ||
60 | #define CKCTL_LCDDIV_OFFSET 2 | ||
61 | #define CKCTL_ARMDIV_OFFSET 4 | ||
62 | #define CKCTL_DSPDIV_OFFSET 6 | ||
63 | #define CKCTL_TCDIV_OFFSET 8 | ||
64 | #define CKCTL_DSPMMUDIV_OFFSET 10 | ||
65 | /*#define ARM_TIMXO 12*/ | ||
66 | #define EN_DSPCK 13 | ||
67 | /*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */ | ||
68 | /* DSP_CKCTL bit shifts */ | ||
69 | #define CKCTL_DSPPERDIV_OFFSET 0 | ||
70 | |||
71 | /* ARM_IDLECT1 bit shifts */ | ||
72 | /*#define IDLWDT_ARM 0*/ | ||
73 | /*#define IDLXORP_ARM 1*/ | ||
74 | /*#define IDLPER_ARM 2*/ | ||
75 | /*#define IDLLCD_ARM 3*/ | ||
76 | /*#define IDLLB_ARM 4*/ | ||
77 | /*#define IDLHSAB_ARM 5*/ | ||
78 | /*#define IDLIF_ARM 6*/ | ||
79 | /*#define IDLDPLL_ARM 7*/ | ||
80 | /*#define IDLAPI_ARM 8*/ | ||
81 | /*#define IDLTIM_ARM 9*/ | ||
82 | /*#define SETARM_IDLE 11*/ | ||
83 | |||
84 | /* ARM_IDLECT2 bit shifts */ | ||
85 | #define EN_WDTCK 0 | ||
86 | #define EN_XORPCK 1 | ||
87 | #define EN_PERCK 2 | ||
88 | #define EN_LCDCK 3 | ||
89 | #define EN_LBCK 4 /* Not on 1610/1710 */ | ||
90 | /*#define EN_HSABCK 5*/ | ||
91 | #define EN_APICK 6 | ||
92 | #define EN_TIMCK 7 | ||
93 | #define DMACK_REQ 8 | ||
94 | #define EN_GPIOCK 9 /* Not on 1610/1710 */ | ||
95 | /*#define EN_LBFREECK 10*/ | ||
96 | #define EN_CKOUT_ARM 11 | ||
97 | |||
98 | /* ARM_IDLECT3 bit shifts */ | ||
99 | #define EN_OCPI_CK 0 | ||
100 | #define EN_TC1_CK 2 | ||
101 | #define EN_TC2_CK 4 | ||
102 | |||
103 | /* DSP_IDLECT2 bit shifts (0,1,2 are same as for ARM_IDLECT2) */ | ||
104 | #define EN_DSPTIMCK 5 | ||
105 | |||
106 | /* Various register defines for clock controls scattered around OMAP chip */ | ||
107 | #define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */ | ||
108 | #define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */ | ||
109 | #define SWD_ULPD_PLL_CLK_REQ 1 /* In SWD_CLK_DIV_CTRL_SEL */ | ||
110 | #define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */ | ||
111 | #define SWD_CLK_DIV_CTRL_SEL 0xfffe0874 | ||
112 | #define COM_CLK_DIV_CTRL_SEL 0xfffe0878 | ||
113 | #define SOFT_REQ_REG 0xfffe0834 | ||
114 | #define SOFT_REQ_REG2 0xfffe0880 | ||
115 | |||
116 | int clk_register(struct clk *clk); | ||
117 | void clk_unregister(struct clk *clk); | ||
118 | int clk_init(void); | ||
119 | |||
120 | #endif | ||
diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c new file mode 100644 index 000000000000..ea967a8f6ce5 --- /dev/null +++ b/arch/arm/plat-omap/common.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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 NO_LENGTH_CHECK 0xffffffff | ||
37 | |||
38 | extern int omap_bootloader_tag_len; | ||
39 | extern u8 omap_bootloader_tag[]; | ||
40 | |||
41 | struct omap_board_config_kernel *omap_board_config; | ||
42 | int omap_board_config_size = 0; | ||
43 | |||
44 | static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) | ||
45 | { | ||
46 | struct omap_board_config_kernel *kinfo = NULL; | ||
47 | int i; | ||
48 | |||
49 | #ifdef CONFIG_OMAP_BOOT_TAG | ||
50 | struct omap_board_config_entry *info = NULL; | ||
51 | |||
52 | if (omap_bootloader_tag_len > 4) | ||
53 | info = (struct omap_board_config_entry *) omap_bootloader_tag; | ||
54 | while (info != NULL) { | ||
55 | u8 *next; | ||
56 | |||
57 | if (info->tag == tag) { | ||
58 | if (skip == 0) | ||
59 | break; | ||
60 | skip--; | ||
61 | } | ||
62 | |||
63 | if ((info->len & 0x03) != 0) { | ||
64 | /* We bail out to avoid an alignment fault */ | ||
65 | printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", | ||
66 | info->len, info->tag); | ||
67 | return NULL; | ||
68 | } | ||
69 | next = (u8 *) info + sizeof(*info) + info->len; | ||
70 | if (next >= omap_bootloader_tag + omap_bootloader_tag_len) | ||
71 | info = NULL; | ||
72 | else | ||
73 | info = (struct omap_board_config_entry *) next; | ||
74 | } | ||
75 | if (info != NULL) { | ||
76 | /* Check the length as a lame attempt to check for | ||
77 | * binary inconsistancy. */ | ||
78 | if (len != NO_LENGTH_CHECK) { | ||
79 | /* Word-align len */ | ||
80 | if (len & 0x03) | ||
81 | len = (len + 3) & ~0x03; | ||
82 | if (info->len != len) { | ||
83 | printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", | ||
84 | tag, len, info->len); | ||
85 | return NULL; | ||
86 | } | ||
87 | } | ||
88 | if (len_out != NULL) | ||
89 | *len_out = info->len; | ||
90 | return info->data; | ||
91 | } | ||
92 | #endif | ||
93 | /* Try to find the config from the board-specific structures | ||
94 | * in the kernel. */ | ||
95 | for (i = 0; i < omap_board_config_size; i++) { | ||
96 | if (omap_board_config[i].tag == tag) { | ||
97 | kinfo = &omap_board_config[i]; | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | if (kinfo == NULL) | ||
102 | return NULL; | ||
103 | return kinfo->data; | ||
104 | } | ||
105 | |||
106 | const void *__omap_get_config(u16 tag, size_t len, int nr) | ||
107 | { | ||
108 | return get_config(tag, len, nr, NULL); | ||
109 | } | ||
110 | EXPORT_SYMBOL(__omap_get_config); | ||
111 | |||
112 | const void *omap_get_var_config(u16 tag, size_t *len) | ||
113 | { | ||
114 | return get_config(tag, NO_LENGTH_CHECK, 0, len); | ||
115 | } | ||
116 | EXPORT_SYMBOL(omap_get_var_config); | ||
117 | |||
118 | static int __init omap_add_serial_console(void) | ||
119 | { | ||
120 | const struct omap_serial_console_config *info; | ||
121 | |||
122 | info = omap_get_config(OMAP_TAG_SERIAL_CONSOLE, | ||
123 | struct omap_serial_console_config); | ||
124 | if (info != NULL && info->console_uart) { | ||
125 | static char speed[11], *opt = NULL; | ||
126 | |||
127 | if (info->console_speed) { | ||
128 | snprintf(speed, sizeof(speed), "%u", info->console_speed); | ||
129 | opt = speed; | ||
130 | } | ||
131 | return add_preferred_console("ttyS", info->console_uart - 1, opt); | ||
132 | } | ||
133 | return 0; | ||
134 | } | ||
135 | console_initcall(omap_add_serial_console); | ||
diff --git a/arch/arm/plat-omap/cpu-omap.c b/arch/arm/plat-omap/cpu-omap.c new file mode 100644 index 000000000000..409aac2c4b9d --- /dev/null +++ b/arch/arm/plat-omap/cpu-omap.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-omap/cpu-omap.c | ||
3 | * | ||
4 | * CPU frequency scaling for OMAP | ||
5 | * | ||
6 | * Copyright (C) 2005 Nokia Corporation | ||
7 | * Written by Tony Lindgren <tony@atomide.com> | ||
8 | * | ||
9 | * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King | ||
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 | #include <linux/types.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/cpufreq.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/err.h> | ||
22 | |||
23 | #include <asm/hardware.h> | ||
24 | #include <asm/mach-types.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/system.h> | ||
27 | |||
28 | #include <asm/hardware/clock.h> | ||
29 | |||
30 | /* TODO: Add support for SDRAM timing changes */ | ||
31 | |||
32 | int omap_verify_speed(struct cpufreq_policy *policy) | ||
33 | { | ||
34 | struct clk * mpu_clk; | ||
35 | |||
36 | if (policy->cpu) | ||
37 | return -EINVAL; | ||
38 | |||
39 | cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, | ||
40 | policy->cpuinfo.max_freq); | ||
41 | mpu_clk = clk_get(NULL, "mpu"); | ||
42 | if (IS_ERR(mpu_clk)) | ||
43 | return PTR_ERR(mpu_clk); | ||
44 | policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; | ||
45 | policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; | ||
46 | cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, | ||
47 | policy->cpuinfo.max_freq); | ||
48 | clk_put(mpu_clk); | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | unsigned int omap_getspeed(unsigned int cpu) | ||
54 | { | ||
55 | struct clk * mpu_clk; | ||
56 | unsigned long rate; | ||
57 | |||
58 | if (cpu) | ||
59 | return 0; | ||
60 | |||
61 | mpu_clk = clk_get(NULL, "mpu"); | ||
62 | if (IS_ERR(mpu_clk)) | ||
63 | return 0; | ||
64 | rate = clk_get_rate(mpu_clk) / 1000; | ||
65 | clk_put(mpu_clk); | ||
66 | |||
67 | return rate; | ||
68 | } | ||
69 | |||
70 | static int omap_target(struct cpufreq_policy *policy, | ||
71 | unsigned int target_freq, | ||
72 | unsigned int relation) | ||
73 | { | ||
74 | struct clk * mpu_clk; | ||
75 | struct cpufreq_freqs freqs; | ||
76 | int ret = 0; | ||
77 | |||
78 | mpu_clk = clk_get(NULL, "mpu"); | ||
79 | if (IS_ERR(mpu_clk)) | ||
80 | return PTR_ERR(mpu_clk); | ||
81 | |||
82 | freqs.old = omap_getspeed(0); | ||
83 | freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; | ||
84 | freqs.cpu = 0; | ||
85 | |||
86 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
87 | ret = clk_set_rate(mpu_clk, target_freq * 1000); | ||
88 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
89 | clk_put(mpu_clk); | ||
90 | |||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | static int __init omap_cpu_init(struct cpufreq_policy *policy) | ||
95 | { | ||
96 | struct clk * mpu_clk; | ||
97 | |||
98 | mpu_clk = clk_get(NULL, "mpu"); | ||
99 | if (IS_ERR(mpu_clk)) | ||
100 | return PTR_ERR(mpu_clk); | ||
101 | |||
102 | if (policy->cpu != 0) | ||
103 | return -EINVAL; | ||
104 | policy->cur = policy->min = policy->max = omap_getspeed(0); | ||
105 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
106 | policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; | ||
107 | policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, 216000000) / 1000; | ||
108 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | ||
109 | clk_put(mpu_clk); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static struct cpufreq_driver omap_driver = { | ||
115 | .flags = CPUFREQ_STICKY, | ||
116 | .verify = omap_verify_speed, | ||
117 | .target = omap_target, | ||
118 | .get = omap_getspeed, | ||
119 | .init = omap_cpu_init, | ||
120 | .name = "omap", | ||
121 | }; | ||
122 | |||
123 | static int __init omap_cpufreq_init(void) | ||
124 | { | ||
125 | return cpufreq_register_driver(&omap_driver); | ||
126 | } | ||
127 | |||
128 | arch_initcall(omap_cpufreq_init); | ||
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c new file mode 100644 index 000000000000..c0a5c2fa42bd --- /dev/null +++ b/arch/arm/plat-omap/dma.c | |||
@@ -0,0 +1,1116 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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.callback != NULL) | ||
798 | w |= 1 << 1; /* Block interrupt enable */ | ||
799 | else | ||
800 | w &= ~(1 << 1); | ||
801 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
802 | |||
803 | if (!(lcd_dma.rotate || lcd_dma.mirror || | ||
804 | lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) | ||
805 | return; | ||
806 | |||
807 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
808 | /* Set the double-indexed addressing mode */ | ||
809 | w |= (0x03 << 12); | ||
810 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
811 | |||
812 | omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); | ||
813 | omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); | ||
814 | omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); | ||
815 | } | ||
816 | |||
817 | static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
818 | { | ||
819 | u16 w; | ||
820 | |||
821 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
822 | if (unlikely(!(w & (1 << 3)))) { | ||
823 | printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); | ||
824 | return IRQ_NONE; | ||
825 | } | ||
826 | /* Ack the IRQ */ | ||
827 | w |= (1 << 3); | ||
828 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
829 | lcd_dma.active = 0; | ||
830 | if (lcd_dma.callback != NULL) | ||
831 | lcd_dma.callback(w, lcd_dma.cb_data); | ||
832 | |||
833 | return IRQ_HANDLED; | ||
834 | } | ||
835 | |||
836 | int omap_request_lcd_dma(void (* callback)(u16 status, void *data), | ||
837 | void *data) | ||
838 | { | ||
839 | spin_lock_irq(&lcd_dma.lock); | ||
840 | if (lcd_dma.reserved) { | ||
841 | spin_unlock_irq(&lcd_dma.lock); | ||
842 | printk(KERN_ERR "LCD DMA channel already reserved\n"); | ||
843 | BUG(); | ||
844 | return -EBUSY; | ||
845 | } | ||
846 | lcd_dma.reserved = 1; | ||
847 | spin_unlock_irq(&lcd_dma.lock); | ||
848 | lcd_dma.callback = callback; | ||
849 | lcd_dma.cb_data = data; | ||
850 | lcd_dma.active = 0; | ||
851 | lcd_dma.single_transfer = 0; | ||
852 | lcd_dma.rotate = 0; | ||
853 | lcd_dma.vxres = 0; | ||
854 | lcd_dma.mirror = 0; | ||
855 | lcd_dma.xscale = 0; | ||
856 | lcd_dma.yscale = 0; | ||
857 | lcd_dma.ext_ctrl = 0; | ||
858 | lcd_dma.src_port = 0; | ||
859 | |||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | void omap_free_lcd_dma(void) | ||
864 | { | ||
865 | spin_lock(&lcd_dma.lock); | ||
866 | if (!lcd_dma.reserved) { | ||
867 | spin_unlock(&lcd_dma.lock); | ||
868 | printk(KERN_ERR "LCD DMA is not reserved\n"); | ||
869 | BUG(); | ||
870 | return; | ||
871 | } | ||
872 | if (!enable_1510_mode) | ||
873 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); | ||
874 | lcd_dma.reserved = 0; | ||
875 | spin_unlock(&lcd_dma.lock); | ||
876 | } | ||
877 | |||
878 | void omap_enable_lcd_dma(void) | ||
879 | { | ||
880 | u16 w; | ||
881 | |||
882 | /* Set the Enable bit only if an external controller is | ||
883 | * connected. Otherwise the OMAP internal controller will | ||
884 | * start the transfer when it gets enabled. | ||
885 | */ | ||
886 | if (enable_1510_mode || !lcd_dma.ext_ctrl) | ||
887 | return; | ||
888 | |||
889 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
890 | w |= 1 << 8; | ||
891 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
892 | |||
893 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
894 | w |= 1 << 7; | ||
895 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
896 | |||
897 | lcd_dma.active = 1; | ||
898 | } | ||
899 | |||
900 | void omap_setup_lcd_dma(void) | ||
901 | { | ||
902 | BUG_ON(lcd_dma.active); | ||
903 | if (!enable_1510_mode) { | ||
904 | /* Set some reasonable defaults */ | ||
905 | omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); | ||
906 | omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); | ||
907 | omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); | ||
908 | } | ||
909 | set_b1_regs(); | ||
910 | if (!enable_1510_mode) { | ||
911 | u16 w; | ||
912 | |||
913 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
914 | /* If DMA was already active set the end_prog bit to have | ||
915 | * the programmed register set loaded into the active | ||
916 | * register set. | ||
917 | */ | ||
918 | w |= 1 << 11; /* End_prog */ | ||
919 | if (!lcd_dma.single_transfer) | ||
920 | w |= (3 << 8); /* Auto_init, repeat */ | ||
921 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
922 | } | ||
923 | } | ||
924 | |||
925 | void omap_stop_lcd_dma(void) | ||
926 | { | ||
927 | u16 w; | ||
928 | |||
929 | lcd_dma.active = 0; | ||
930 | if (enable_1510_mode || !lcd_dma.ext_ctrl) | ||
931 | return; | ||
932 | |||
933 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
934 | w &= ~(1 << 7); | ||
935 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
936 | |||
937 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
938 | w &= ~(1 << 8); | ||
939 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
940 | } | ||
941 | |||
942 | /* | ||
943 | * Clears any DMA state so the DMA engine is ready to restart with new buffers | ||
944 | * through omap_start_dma(). Any buffers in flight are discarded. | ||
945 | */ | ||
946 | void omap_clear_dma(int lch) | ||
947 | { | ||
948 | unsigned long flags; | ||
949 | int status; | ||
950 | |||
951 | local_irq_save(flags); | ||
952 | omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN, | ||
953 | OMAP_DMA_CCR(lch)); | ||
954 | status = OMAP_DMA_CSR(lch); /* clear pending interrupts */ | ||
955 | local_irq_restore(flags); | ||
956 | } | ||
957 | |||
958 | /* | ||
959 | * Returns current physical source address for the given DMA channel. | ||
960 | * If the channel is running the caller must disable interrupts prior calling | ||
961 | * this function and process the returned value before re-enabling interrupt to | ||
962 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
963 | * is a chance for CSSA_L register overflow inbetween the two reads resulting | ||
964 | * in incorrect return value. | ||
965 | */ | ||
966 | dma_addr_t omap_get_dma_src_pos(int lch) | ||
967 | { | ||
968 | return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) | | ||
969 | (OMAP_DMA_CSSA_U(lch) << 16)); | ||
970 | } | ||
971 | |||
972 | /* | ||
973 | * Returns current physical destination address for the given DMA channel. | ||
974 | * If the channel is running the caller must disable interrupts prior calling | ||
975 | * this function and process the returned value before re-enabling interrupt to | ||
976 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
977 | * is a chance for CDSA_L register overflow inbetween the two reads resulting | ||
978 | * in incorrect return value. | ||
979 | */ | ||
980 | dma_addr_t omap_get_dma_dst_pos(int lch) | ||
981 | { | ||
982 | return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) | | ||
983 | (OMAP_DMA_CDSA_U(lch) << 16)); | ||
984 | } | ||
985 | |||
986 | int omap_dma_running(void) | ||
987 | { | ||
988 | int lch; | ||
989 | |||
990 | /* Check if LCD DMA is running */ | ||
991 | if (cpu_is_omap16xx()) | ||
992 | if (omap_readw(OMAP1610_DMA_LCD_CCR) & OMAP_DMA_CCR_EN) | ||
993 | return 1; | ||
994 | |||
995 | for (lch = 0; lch < dma_chan_count; lch++) { | ||
996 | u16 w; | ||
997 | |||
998 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
999 | if (w & OMAP_DMA_CCR_EN) | ||
1000 | return 1; | ||
1001 | } | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | static int __init omap_init_dma(void) | ||
1006 | { | ||
1007 | int ch, r; | ||
1008 | |||
1009 | if (cpu_is_omap1510()) { | ||
1010 | printk(KERN_INFO "DMA support for OMAP1510 initialized\n"); | ||
1011 | dma_chan_count = 9; | ||
1012 | enable_1510_mode = 1; | ||
1013 | } else if (cpu_is_omap16xx() || cpu_is_omap730()) { | ||
1014 | printk(KERN_INFO "OMAP DMA hardware version %d\n", | ||
1015 | omap_readw(OMAP_DMA_HW_ID)); | ||
1016 | printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", | ||
1017 | (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L), | ||
1018 | (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L), | ||
1019 | omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3), | ||
1020 | omap_readw(OMAP_DMA_CAPS_4)); | ||
1021 | if (!enable_1510_mode) { | ||
1022 | u16 w; | ||
1023 | |||
1024 | /* Disable OMAP 3.0/3.1 compatibility mode. */ | ||
1025 | w = omap_readw(OMAP_DMA_GSCR); | ||
1026 | w |= 1 << 3; | ||
1027 | omap_writew(w, OMAP_DMA_GSCR); | ||
1028 | dma_chan_count = 16; | ||
1029 | } else | ||
1030 | dma_chan_count = 9; | ||
1031 | } else { | ||
1032 | dma_chan_count = 0; | ||
1033 | return 0; | ||
1034 | } | ||
1035 | |||
1036 | memset(&lcd_dma, 0, sizeof(lcd_dma)); | ||
1037 | spin_lock_init(&lcd_dma.lock); | ||
1038 | spin_lock_init(&dma_chan_lock); | ||
1039 | memset(&dma_chan, 0, sizeof(dma_chan)); | ||
1040 | |||
1041 | for (ch = 0; ch < dma_chan_count; ch++) { | ||
1042 | dma_chan[ch].dev_id = -1; | ||
1043 | dma_chan[ch].next_lch = -1; | ||
1044 | |||
1045 | if (ch >= 6 && enable_1510_mode) | ||
1046 | continue; | ||
1047 | |||
1048 | /* request_irq() doesn't like dev_id (ie. ch) being zero, | ||
1049 | * so we have to kludge around this. */ | ||
1050 | r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA", | ||
1051 | (void *) (ch + 1)); | ||
1052 | if (r != 0) { | ||
1053 | int i; | ||
1054 | |||
1055 | printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n", | ||
1056 | dma_irq[ch], r); | ||
1057 | for (i = 0; i < ch; i++) | ||
1058 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
1059 | return r; | ||
1060 | } | ||
1061 | } | ||
1062 | r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL); | ||
1063 | if (r != 0) { | ||
1064 | int i; | ||
1065 | |||
1066 | printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r); | ||
1067 | for (i = 0; i < dma_chan_count; i++) | ||
1068 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
1069 | return r; | ||
1070 | } | ||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | arch_initcall(omap_init_dma); | ||
1075 | |||
1076 | |||
1077 | EXPORT_SYMBOL(omap_get_dma_src_pos); | ||
1078 | EXPORT_SYMBOL(omap_get_dma_dst_pos); | ||
1079 | EXPORT_SYMBOL(omap_clear_dma); | ||
1080 | EXPORT_SYMBOL(omap_set_dma_priority); | ||
1081 | EXPORT_SYMBOL(omap_request_dma); | ||
1082 | EXPORT_SYMBOL(omap_free_dma); | ||
1083 | EXPORT_SYMBOL(omap_start_dma); | ||
1084 | EXPORT_SYMBOL(omap_stop_dma); | ||
1085 | EXPORT_SYMBOL(omap_enable_dma_irq); | ||
1086 | EXPORT_SYMBOL(omap_disable_dma_irq); | ||
1087 | |||
1088 | EXPORT_SYMBOL(omap_set_dma_transfer_params); | ||
1089 | EXPORT_SYMBOL(omap_set_dma_color_mode); | ||
1090 | |||
1091 | EXPORT_SYMBOL(omap_set_dma_src_params); | ||
1092 | EXPORT_SYMBOL(omap_set_dma_src_index); | ||
1093 | EXPORT_SYMBOL(omap_set_dma_src_data_pack); | ||
1094 | EXPORT_SYMBOL(omap_set_dma_src_burst_mode); | ||
1095 | |||
1096 | EXPORT_SYMBOL(omap_set_dma_dest_params); | ||
1097 | EXPORT_SYMBOL(omap_set_dma_dest_index); | ||
1098 | EXPORT_SYMBOL(omap_set_dma_dest_data_pack); | ||
1099 | EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); | ||
1100 | |||
1101 | EXPORT_SYMBOL(omap_dma_link_lch); | ||
1102 | EXPORT_SYMBOL(omap_dma_unlink_lch); | ||
1103 | |||
1104 | EXPORT_SYMBOL(omap_request_lcd_dma); | ||
1105 | EXPORT_SYMBOL(omap_free_lcd_dma); | ||
1106 | EXPORT_SYMBOL(omap_enable_lcd_dma); | ||
1107 | EXPORT_SYMBOL(omap_setup_lcd_dma); | ||
1108 | EXPORT_SYMBOL(omap_stop_lcd_dma); | ||
1109 | EXPORT_SYMBOL(omap_set_lcd_dma_b1); | ||
1110 | EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); | ||
1111 | EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); | ||
1112 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); | ||
1113 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); | ||
1114 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); | ||
1115 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); | ||
1116 | |||
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c new file mode 100644 index 000000000000..1c85b4e536c2 --- /dev/null +++ b/arch/arm/plat-omap/gpio.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c new file mode 100644 index 000000000000..43567d5edddb --- /dev/null +++ b/arch/arm/plat-omap/mcbsp.c | |||
@@ -0,0 +1,758 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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 | static struct clk *mcbsp_dspxor_ck = 0; | ||
70 | |||
71 | |||
72 | static void omap_mcbsp_dump_reg(u8 id) | ||
73 | { | ||
74 | DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); | ||
75 | DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); | ||
76 | DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); | ||
77 | DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); | ||
78 | DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); | ||
79 | DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); | ||
80 | DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); | ||
81 | DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); | ||
82 | DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); | ||
83 | DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); | ||
84 | DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); | ||
85 | DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); | ||
86 | DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); | ||
87 | DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); | ||
88 | DBG("***********************\n"); | ||
89 | } | ||
90 | |||
91 | |||
92 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
93 | { | ||
94 | struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); | ||
95 | |||
96 | DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); | ||
97 | |||
98 | complete(&mcbsp_tx->tx_irq_completion); | ||
99 | return IRQ_HANDLED; | ||
100 | } | ||
101 | |||
102 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
103 | { | ||
104 | struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); | ||
105 | |||
106 | DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); | ||
107 | |||
108 | complete(&mcbsp_rx->rx_irq_completion); | ||
109 | return IRQ_HANDLED; | ||
110 | } | ||
111 | |||
112 | |||
113 | static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) | ||
114 | { | ||
115 | struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); | ||
116 | |||
117 | DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); | ||
118 | |||
119 | /* We can free the channels */ | ||
120 | omap_free_dma(mcbsp_dma_tx->dma_tx_lch); | ||
121 | mcbsp_dma_tx->dma_tx_lch = -1; | ||
122 | |||
123 | complete(&mcbsp_dma_tx->tx_dma_completion); | ||
124 | } | ||
125 | |||
126 | static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) | ||
127 | { | ||
128 | struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); | ||
129 | |||
130 | DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); | ||
131 | |||
132 | /* We can free the channels */ | ||
133 | omap_free_dma(mcbsp_dma_rx->dma_rx_lch); | ||
134 | mcbsp_dma_rx->dma_rx_lch = -1; | ||
135 | |||
136 | complete(&mcbsp_dma_rx->rx_dma_completion); | ||
137 | } | ||
138 | |||
139 | |||
140 | /* | ||
141 | * omap_mcbsp_config simply write a config to the | ||
142 | * appropriate McBSP. | ||
143 | * You either call this function or set the McBSP registers | ||
144 | * by yourself before calling omap_mcbsp_start(). | ||
145 | */ | ||
146 | |||
147 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) | ||
148 | { | ||
149 | u32 io_base = mcbsp[id].io_base; | ||
150 | |||
151 | DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); | ||
152 | |||
153 | /* We write the given config */ | ||
154 | OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); | ||
155 | OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); | ||
156 | OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); | ||
157 | OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); | ||
158 | OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); | ||
159 | OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); | ||
160 | OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); | ||
161 | OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); | ||
162 | OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); | ||
163 | OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); | ||
164 | OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); | ||
165 | } | ||
166 | |||
167 | |||
168 | |||
169 | static int omap_mcbsp_check(unsigned int id) | ||
170 | { | ||
171 | if (cpu_is_omap730()) { | ||
172 | if (id > OMAP_MAX_MCBSP_COUNT - 1) { | ||
173 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
174 | return -1; | ||
175 | } | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
180 | if (id > OMAP_MAX_MCBSP_COUNT) { | ||
181 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
182 | return -1; | ||
183 | } | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | #define EN_XORPCK 1 | ||
191 | #define DSP_RSTCT2 0xe1008014 | ||
192 | |||
193 | static void omap_mcbsp_dsp_request(void) | ||
194 | { | ||
195 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
196 | clk_use(mcbsp_dsp_ck); | ||
197 | clk_use(mcbsp_api_ck); | ||
198 | |||
199 | /* enable 12MHz clock to mcbsp 1 & 3 */ | ||
200 | clk_use(mcbsp_dspxor_ck); | ||
201 | __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, | ||
202 | DSP_RSTCT2); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | static void omap_mcbsp_dsp_free(void) | ||
207 | { | ||
208 | if (cpu_is_omap1510() || cpu_is_omap16xx()) { | ||
209 | clk_unuse(mcbsp_dspxor_ck); | ||
210 | clk_unuse(mcbsp_dsp_ck); | ||
211 | clk_unuse(mcbsp_api_ck); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | int omap_mcbsp_request(unsigned int id) | ||
216 | { | ||
217 | int err; | ||
218 | |||
219 | if (omap_mcbsp_check(id) < 0) | ||
220 | return -EINVAL; | ||
221 | |||
222 | /* | ||
223 | * On 1510, 1610 and 1710, McBSP1 and McBSP3 | ||
224 | * are DSP public peripherals. | ||
225 | */ | ||
226 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
227 | omap_mcbsp_dsp_request(); | ||
228 | |||
229 | spin_lock(&mcbsp[id].lock); | ||
230 | if (!mcbsp[id].free) { | ||
231 | printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); | ||
232 | spin_unlock(&mcbsp[id].lock); | ||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | mcbsp[id].free = 0; | ||
237 | spin_unlock(&mcbsp[id].lock); | ||
238 | |||
239 | /* We need to get IRQs here */ | ||
240 | err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
241 | "McBSP", | ||
242 | (void *) (&mcbsp[id])); | ||
243 | if (err != 0) { | ||
244 | printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", | ||
245 | mcbsp[id].tx_irq, mcbsp[id].id); | ||
246 | return err; | ||
247 | } | ||
248 | |||
249 | init_completion(&(mcbsp[id].tx_irq_completion)); | ||
250 | |||
251 | |||
252 | err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
253 | "McBSP", | ||
254 | (void *) (&mcbsp[id])); | ||
255 | if (err != 0) { | ||
256 | printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", | ||
257 | mcbsp[id].rx_irq, mcbsp[id].id); | ||
258 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
259 | return err; | ||
260 | } | ||
261 | |||
262 | init_completion(&(mcbsp[id].rx_irq_completion)); | ||
263 | return 0; | ||
264 | |||
265 | } | ||
266 | |||
267 | void omap_mcbsp_free(unsigned int id) | ||
268 | { | ||
269 | if (omap_mcbsp_check(id) < 0) | ||
270 | return; | ||
271 | |||
272 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
273 | omap_mcbsp_dsp_free(); | ||
274 | |||
275 | spin_lock(&mcbsp[id].lock); | ||
276 | if (mcbsp[id].free) { | ||
277 | printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); | ||
278 | spin_unlock(&mcbsp[id].lock); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | mcbsp[id].free = 1; | ||
283 | spin_unlock(&mcbsp[id].lock); | ||
284 | |||
285 | /* Free IRQs */ | ||
286 | free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); | ||
287 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Here we start the McBSP, by enabling the sample | ||
292 | * generator, both transmitter and receivers, | ||
293 | * and the frame sync. | ||
294 | */ | ||
295 | void omap_mcbsp_start(unsigned int id) | ||
296 | { | ||
297 | u32 io_base; | ||
298 | u16 w; | ||
299 | |||
300 | if (omap_mcbsp_check(id) < 0) | ||
301 | return; | ||
302 | |||
303 | io_base = mcbsp[id].io_base; | ||
304 | |||
305 | mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); | ||
306 | mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); | ||
307 | |||
308 | /* Start the sample generator */ | ||
309 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
310 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||
311 | |||
312 | /* Enable transmitter and receiver */ | ||
313 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
314 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | ||
315 | |||
316 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
317 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | ||
318 | |||
319 | udelay(100); | ||
320 | |||
321 | /* Start frame sync */ | ||
322 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
323 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||
324 | |||
325 | /* Dump McBSP Regs */ | ||
326 | omap_mcbsp_dump_reg(id); | ||
327 | |||
328 | } | ||
329 | |||
330 | void omap_mcbsp_stop(unsigned int id) | ||
331 | { | ||
332 | u32 io_base; | ||
333 | u16 w; | ||
334 | |||
335 | if (omap_mcbsp_check(id) < 0) | ||
336 | return; | ||
337 | |||
338 | io_base = mcbsp[id].io_base; | ||
339 | |||
340 | /* Reset transmitter */ | ||
341 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
342 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | ||
343 | |||
344 | /* Reset receiver */ | ||
345 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
346 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | ||
347 | |||
348 | /* Reset the sample rate generator */ | ||
349 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
350 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||
351 | } | ||
352 | |||
353 | |||
354 | /* polled mcbsp i/o operations */ | ||
355 | int omap_mcbsp_pollwrite(unsigned int id, u16 buf) | ||
356 | { | ||
357 | u32 base = mcbsp[id].io_base; | ||
358 | writew(buf, base + OMAP_MCBSP_REG_DXR1); | ||
359 | /* if frame sync error - clear the error */ | ||
360 | if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) { | ||
361 | /* clear error */ | ||
362 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR), | ||
363 | base + OMAP_MCBSP_REG_SPCR2); | ||
364 | /* resend */ | ||
365 | return -1; | ||
366 | } else { | ||
367 | /* wait for transmit confirmation */ | ||
368 | int attemps = 0; | ||
369 | while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) { | ||
370 | if (attemps++ > 1000) { | ||
371 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) & | ||
372 | (~XRST), | ||
373 | base + OMAP_MCBSP_REG_SPCR2); | ||
374 | udelay(10); | ||
375 | writew(readw(base + OMAP_MCBSP_REG_SPCR2) | | ||
376 | (XRST), | ||
377 | base + OMAP_MCBSP_REG_SPCR2); | ||
378 | udelay(10); | ||
379 | printk(KERN_ERR | ||
380 | " Could not write to McBSP Register\n"); | ||
381 | return -2; | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | int omap_mcbsp_pollread(unsigned int id, u16 * buf) | ||
389 | { | ||
390 | u32 base = mcbsp[id].io_base; | ||
391 | /* if frame sync error - clear the error */ | ||
392 | if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) { | ||
393 | /* clear error */ | ||
394 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR), | ||
395 | base + OMAP_MCBSP_REG_SPCR1); | ||
396 | /* resend */ | ||
397 | return -1; | ||
398 | } else { | ||
399 | /* wait for recieve confirmation */ | ||
400 | int attemps = 0; | ||
401 | while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) { | ||
402 | if (attemps++ > 1000) { | ||
403 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) & | ||
404 | (~RRST), | ||
405 | base + OMAP_MCBSP_REG_SPCR1); | ||
406 | udelay(10); | ||
407 | writew(readw(base + OMAP_MCBSP_REG_SPCR1) | | ||
408 | (RRST), | ||
409 | base + OMAP_MCBSP_REG_SPCR1); | ||
410 | udelay(10); | ||
411 | printk(KERN_ERR | ||
412 | " Could not read from McBSP Register\n"); | ||
413 | return -2; | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | *buf = readw(base + OMAP_MCBSP_REG_DRR1); | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * IRQ based word transmission. | ||
423 | */ | ||
424 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | ||
425 | { | ||
426 | u32 io_base; | ||
427 | omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; | ||
428 | |||
429 | if (omap_mcbsp_check(id) < 0) | ||
430 | return; | ||
431 | |||
432 | io_base = mcbsp[id].io_base; | ||
433 | |||
434 | wait_for_completion(&(mcbsp[id].tx_irq_completion)); | ||
435 | |||
436 | if (word_length > OMAP_MCBSP_WORD_16) | ||
437 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | ||
438 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | ||
439 | } | ||
440 | |||
441 | u32 omap_mcbsp_recv_word(unsigned int id) | ||
442 | { | ||
443 | u32 io_base; | ||
444 | u16 word_lsb, word_msb = 0; | ||
445 | omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; | ||
446 | |||
447 | if (omap_mcbsp_check(id) < 0) | ||
448 | return -EINVAL; | ||
449 | |||
450 | io_base = mcbsp[id].io_base; | ||
451 | |||
452 | wait_for_completion(&(mcbsp[id].rx_irq_completion)); | ||
453 | |||
454 | if (word_length > OMAP_MCBSP_WORD_16) | ||
455 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | ||
456 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | ||
457 | |||
458 | return (word_lsb | (word_msb << 16)); | ||
459 | } | ||
460 | |||
461 | |||
462 | /* | ||
463 | * Simple DMA based buffer rx/tx routines. | ||
464 | * Nothing fancy, just a single buffer tx/rx through DMA. | ||
465 | * The DMA resources are released once the transfer is done. | ||
466 | * For anything fancier, you should use your own customized DMA | ||
467 | * routines and callbacks. | ||
468 | */ | ||
469 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
470 | { | ||
471 | int dma_tx_ch; | ||
472 | |||
473 | if (omap_mcbsp_check(id) < 0) | ||
474 | return -EINVAL; | ||
475 | |||
476 | if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, | ||
477 | &mcbsp[id], | ||
478 | &dma_tx_ch)) { | ||
479 | printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); | ||
480 | return -EAGAIN; | ||
481 | } | ||
482 | mcbsp[id].dma_tx_lch = dma_tx_ch; | ||
483 | |||
484 | DBG("TX DMA on channel %d\n", dma_tx_ch); | ||
485 | |||
486 | init_completion(&(mcbsp[id].tx_dma_completion)); | ||
487 | |||
488 | omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, | ||
489 | OMAP_DMA_DATA_TYPE_S16, | ||
490 | length >> 1, 1, | ||
491 | OMAP_DMA_SYNC_ELEMENT); | ||
492 | |||
493 | omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, | ||
494 | OMAP_DMA_PORT_TIPB, | ||
495 | OMAP_DMA_AMODE_CONSTANT, | ||
496 | mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); | ||
497 | |||
498 | omap_set_dma_src_params(mcbsp[id].dma_tx_lch, | ||
499 | OMAP_DMA_PORT_EMIFF, | ||
500 | OMAP_DMA_AMODE_POST_INC, | ||
501 | buffer); | ||
502 | |||
503 | omap_start_dma(mcbsp[id].dma_tx_lch); | ||
504 | wait_for_completion(&(mcbsp[id].tx_dma_completion)); | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | |||
509 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
510 | { | ||
511 | int dma_rx_ch; | ||
512 | |||
513 | if (omap_mcbsp_check(id) < 0) | ||
514 | return -EINVAL; | ||
515 | |||
516 | if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, | ||
517 | &mcbsp[id], | ||
518 | &dma_rx_ch)) { | ||
519 | printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); | ||
520 | return -EAGAIN; | ||
521 | } | ||
522 | mcbsp[id].dma_rx_lch = dma_rx_ch; | ||
523 | |||
524 | DBG("RX DMA on channel %d\n", dma_rx_ch); | ||
525 | |||
526 | init_completion(&(mcbsp[id].rx_dma_completion)); | ||
527 | |||
528 | omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, | ||
529 | OMAP_DMA_DATA_TYPE_S16, | ||
530 | length >> 1, 1, | ||
531 | OMAP_DMA_SYNC_ELEMENT); | ||
532 | |||
533 | omap_set_dma_src_params(mcbsp[id].dma_rx_lch, | ||
534 | OMAP_DMA_PORT_TIPB, | ||
535 | OMAP_DMA_AMODE_CONSTANT, | ||
536 | mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); | ||
537 | |||
538 | omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, | ||
539 | OMAP_DMA_PORT_EMIFF, | ||
540 | OMAP_DMA_AMODE_POST_INC, | ||
541 | buffer); | ||
542 | |||
543 | omap_start_dma(mcbsp[id].dma_rx_lch); | ||
544 | wait_for_completion(&(mcbsp[id].rx_dma_completion)); | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | |||
549 | /* | ||
550 | * SPI wrapper. | ||
551 | * Since SPI setup is much simpler than the generic McBSP one, | ||
552 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | ||
553 | * Once this is done, you can call omap_mcbsp_start(). | ||
554 | */ | ||
555 | void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) | ||
556 | { | ||
557 | struct omap_mcbsp_reg_cfg mcbsp_cfg; | ||
558 | |||
559 | if (omap_mcbsp_check(id) < 0) | ||
560 | return; | ||
561 | |||
562 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | ||
563 | |||
564 | /* SPI has only one frame */ | ||
565 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | ||
566 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | ||
567 | |||
568 | /* Clock stop mode */ | ||
569 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) | ||
570 | mcbsp_cfg.spcr1 |= (1 << 12); | ||
571 | else | ||
572 | mcbsp_cfg.spcr1 |= (3 << 11); | ||
573 | |||
574 | /* Set clock parities */ | ||
575 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
576 | mcbsp_cfg.pcr0 |= CLKRP; | ||
577 | else | ||
578 | mcbsp_cfg.pcr0 &= ~CLKRP; | ||
579 | |||
580 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
581 | mcbsp_cfg.pcr0 &= ~CLKXP; | ||
582 | else | ||
583 | mcbsp_cfg.pcr0 |= CLKXP; | ||
584 | |||
585 | /* Set SCLKME to 0 and CLKSM to 1 */ | ||
586 | mcbsp_cfg.pcr0 &= ~SCLKME; | ||
587 | mcbsp_cfg.srgr2 |= CLKSM; | ||
588 | |||
589 | /* Set FSXP */ | ||
590 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | ||
591 | mcbsp_cfg.pcr0 &= ~FSXP; | ||
592 | else | ||
593 | mcbsp_cfg.pcr0 |= FSXP; | ||
594 | |||
595 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | ||
596 | mcbsp_cfg.pcr0 |= CLKXM; | ||
597 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); | ||
598 | mcbsp_cfg.pcr0 |= FSXM; | ||
599 | mcbsp_cfg.srgr2 &= ~FSGM; | ||
600 | mcbsp_cfg.xcr2 |= XDATDLY(1); | ||
601 | mcbsp_cfg.rcr2 |= RDATDLY(1); | ||
602 | } | ||
603 | else { | ||
604 | mcbsp_cfg.pcr0 &= ~CLKXM; | ||
605 | mcbsp_cfg.srgr1 |= CLKGDV(1); | ||
606 | mcbsp_cfg.pcr0 &= ~FSXM; | ||
607 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | ||
608 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | ||
609 | } | ||
610 | |||
611 | mcbsp_cfg.xcr2 &= ~XPHASE; | ||
612 | mcbsp_cfg.rcr2 &= ~RPHASE; | ||
613 | |||
614 | omap_mcbsp_config(id, &mcbsp_cfg); | ||
615 | } | ||
616 | |||
617 | |||
618 | /* | ||
619 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
620 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
621 | */ | ||
622 | struct omap_mcbsp_info { | ||
623 | u32 virt_base; | ||
624 | u8 dma_rx_sync, dma_tx_sync; | ||
625 | u16 rx_irq, tx_irq; | ||
626 | }; | ||
627 | |||
628 | #ifdef CONFIG_ARCH_OMAP730 | ||
629 | static const struct omap_mcbsp_info mcbsp_730[] = { | ||
630 | [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), | ||
631 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
632 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
633 | .rx_irq = INT_730_McBSP1RX, | ||
634 | .tx_irq = INT_730_McBSP1TX }, | ||
635 | [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), | ||
636 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
637 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
638 | .rx_irq = INT_730_McBSP2RX, | ||
639 | .tx_irq = INT_730_McBSP2TX }, | ||
640 | }; | ||
641 | #endif | ||
642 | |||
643 | #ifdef CONFIG_ARCH_OMAP1510 | ||
644 | static const struct omap_mcbsp_info mcbsp_1510[] = { | ||
645 | [0] = { .virt_base = OMAP1510_MCBSP1_BASE, | ||
646 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
647 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
648 | .rx_irq = INT_McBSP1RX, | ||
649 | .tx_irq = INT_McBSP1TX }, | ||
650 | [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), | ||
651 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
652 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
653 | .rx_irq = INT_1510_SPI_RX, | ||
654 | .tx_irq = INT_1510_SPI_TX }, | ||
655 | [2] = { .virt_base = OMAP1510_MCBSP3_BASE, | ||
656 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
657 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
658 | .rx_irq = INT_McBSP3RX, | ||
659 | .tx_irq = INT_McBSP3TX }, | ||
660 | }; | ||
661 | #endif | ||
662 | |||
663 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
664 | static const struct omap_mcbsp_info mcbsp_1610[] = { | ||
665 | [0] = { .virt_base = OMAP1610_MCBSP1_BASE, | ||
666 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
667 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
668 | .rx_irq = INT_McBSP1RX, | ||
669 | .tx_irq = INT_McBSP1TX }, | ||
670 | [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), | ||
671 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
672 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
673 | .rx_irq = INT_1610_McBSP2_RX, | ||
674 | .tx_irq = INT_1610_McBSP2_TX }, | ||
675 | [2] = { .virt_base = OMAP1610_MCBSP3_BASE, | ||
676 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
677 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
678 | .rx_irq = INT_McBSP3RX, | ||
679 | .tx_irq = INT_McBSP3TX }, | ||
680 | }; | ||
681 | #endif | ||
682 | |||
683 | static int __init omap_mcbsp_init(void) | ||
684 | { | ||
685 | int mcbsp_count = 0, i; | ||
686 | static const struct omap_mcbsp_info *mcbsp_info; | ||
687 | |||
688 | printk("Initializing OMAP McBSP system\n"); | ||
689 | |||
690 | mcbsp_dsp_ck = clk_get(0, "dsp_ck"); | ||
691 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
692 | printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); | ||
693 | return PTR_ERR(mcbsp_dsp_ck); | ||
694 | } | ||
695 | mcbsp_api_ck = clk_get(0, "api_ck"); | ||
696 | if (IS_ERR(mcbsp_api_ck)) { | ||
697 | printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); | ||
698 | return PTR_ERR(mcbsp_api_ck); | ||
699 | } | ||
700 | mcbsp_dspxor_ck = clk_get(0, "dspxor_ck"); | ||
701 | if (IS_ERR(mcbsp_dspxor_ck)) { | ||
702 | printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); | ||
703 | return PTR_ERR(mcbsp_dspxor_ck); | ||
704 | } | ||
705 | |||
706 | #ifdef CONFIG_ARCH_OMAP730 | ||
707 | if (cpu_is_omap730()) { | ||
708 | mcbsp_info = mcbsp_730; | ||
709 | mcbsp_count = ARRAY_SIZE(mcbsp_730); | ||
710 | } | ||
711 | #endif | ||
712 | #ifdef CONFIG_ARCH_OMAP1510 | ||
713 | if (cpu_is_omap1510()) { | ||
714 | mcbsp_info = mcbsp_1510; | ||
715 | mcbsp_count = ARRAY_SIZE(mcbsp_1510); | ||
716 | } | ||
717 | #endif | ||
718 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
719 | if (cpu_is_omap16xx()) { | ||
720 | mcbsp_info = mcbsp_1610; | ||
721 | mcbsp_count = ARRAY_SIZE(mcbsp_1610); | ||
722 | } | ||
723 | #endif | ||
724 | for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { | ||
725 | if (i >= mcbsp_count) { | ||
726 | mcbsp[i].io_base = 0; | ||
727 | mcbsp[i].free = 0; | ||
728 | continue; | ||
729 | } | ||
730 | mcbsp[i].id = i + 1; | ||
731 | mcbsp[i].free = 1; | ||
732 | mcbsp[i].dma_tx_lch = -1; | ||
733 | mcbsp[i].dma_rx_lch = -1; | ||
734 | |||
735 | mcbsp[i].io_base = mcbsp_info[i].virt_base; | ||
736 | mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; | ||
737 | mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; | ||
738 | mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; | ||
739 | mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; | ||
740 | spin_lock_init(&mcbsp[i].lock); | ||
741 | } | ||
742 | |||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | |||
747 | arch_initcall(omap_mcbsp_init); | ||
748 | |||
749 | EXPORT_SYMBOL(omap_mcbsp_config); | ||
750 | EXPORT_SYMBOL(omap_mcbsp_request); | ||
751 | EXPORT_SYMBOL(omap_mcbsp_free); | ||
752 | EXPORT_SYMBOL(omap_mcbsp_start); | ||
753 | EXPORT_SYMBOL(omap_mcbsp_stop); | ||
754 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); | ||
755 | EXPORT_SYMBOL(omap_mcbsp_recv_word); | ||
756 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); | ||
757 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); | ||
758 | EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); | ||
diff --git a/arch/arm/plat-omap/mux.c b/arch/arm/plat-omap/mux.c new file mode 100644 index 000000000000..ea7b955b9c81 --- /dev/null +++ b/arch/arm/plat-omap/mux.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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 = (reg_cfg_set *)®_cfg_table[reg_cfg]; | ||
57 | |||
58 | /* Check the mux register in question */ | ||
59 | if (cfg->mux_reg) { | ||
60 | unsigned tmp1, tmp2; | ||
61 | |||
62 | spin_lock_irqsave(&mux_spin_lock, flags); | ||
63 | reg_orig = omap_readl(cfg->mux_reg); | ||
64 | |||
65 | /* The mux registers always seem to be 3 bits long */ | ||
66 | mask = (0x7 << cfg->mask_offset); | ||
67 | tmp1 = reg_orig & mask; | ||
68 | reg = reg_orig & ~mask; | ||
69 | |||
70 | tmp2 = (cfg->mask << cfg->mask_offset); | ||
71 | reg |= tmp2; | ||
72 | |||
73 | if (tmp1 != tmp2) | ||
74 | warn = 1; | ||
75 | |||
76 | omap_writel(reg, cfg->mux_reg); | ||
77 | spin_unlock_irqrestore(&mux_spin_lock, flags); | ||
78 | } | ||
79 | |||
80 | /* Check for pull up or pull down selection on 1610 */ | ||
81 | if (!cpu_is_omap1510()) { | ||
82 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
83 | spin_lock_irqsave(&mux_spin_lock, flags); | ||
84 | pu_pd_orig = omap_readl(cfg->pu_pd_reg); | ||
85 | mask = 1 << cfg->pull_bit; | ||
86 | |||
87 | if (cfg->pu_pd_val) { | ||
88 | if (!(pu_pd_orig & mask)) | ||
89 | warn = 1; | ||
90 | /* Use pull up */ | ||
91 | pu_pd = pu_pd_orig | mask; | ||
92 | } else { | ||
93 | if (pu_pd_orig & mask) | ||
94 | warn = 1; | ||
95 | /* Use pull down */ | ||
96 | pu_pd = pu_pd_orig & ~mask; | ||
97 | } | ||
98 | omap_writel(pu_pd, cfg->pu_pd_reg); | ||
99 | spin_unlock_irqrestore(&mux_spin_lock, flags); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | /* Check for an associated pull down register */ | ||
104 | if (cfg->pull_reg) { | ||
105 | spin_lock_irqsave(&mux_spin_lock, flags); | ||
106 | pull_orig = omap_readl(cfg->pull_reg); | ||
107 | mask = 1 << cfg->pull_bit; | ||
108 | |||
109 | if (cfg->pull_val) { | ||
110 | if (pull_orig & mask) | ||
111 | warn = 1; | ||
112 | /* Low bit = pull enabled */ | ||
113 | pull = pull_orig & ~mask; | ||
114 | } else { | ||
115 | if (!(pull_orig & mask)) | ||
116 | warn = 1; | ||
117 | /* High bit = pull disabled */ | ||
118 | pull = pull_orig | mask; | ||
119 | } | ||
120 | |||
121 | omap_writel(pull, cfg->pull_reg); | ||
122 | spin_unlock_irqrestore(&mux_spin_lock, flags); | ||
123 | } | ||
124 | |||
125 | if (warn) { | ||
126 | #ifdef CONFIG_OMAP_MUX_WARNINGS | ||
127 | printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); | ||
128 | #endif | ||
129 | } | ||
130 | |||
131 | #ifdef CONFIG_OMAP_MUX_DEBUG | ||
132 | if (cfg->debug || warn) { | ||
133 | printk("MUX: Setting register %s\n", cfg->name); | ||
134 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
135 | cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); | ||
136 | |||
137 | if (!cpu_is_omap1510()) { | ||
138 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
139 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
140 | cfg->pu_pd_name, cfg->pu_pd_reg, | ||
141 | pu_pd_orig, pu_pd); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | if (cfg->pull_reg) | ||
146 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
147 | cfg->pull_name, cfg->pull_reg, pull_orig, pull); | ||
148 | } | ||
149 | #endif | ||
150 | |||
151 | #ifdef CONFIG_OMAP_MUX_ERRORS | ||
152 | return warn ? -ETXTBSY : 0; | ||
153 | #else | ||
154 | return 0; | ||
155 | #endif | ||
156 | } | ||
157 | |||
158 | EXPORT_SYMBOL(omap_cfg_reg); | ||
159 | |||
160 | #endif /* CONFIG_OMAP_MUX */ | ||
diff --git a/arch/arm/plat-omap/ocpi.c b/arch/arm/plat-omap/ocpi.c new file mode 100644 index 000000000000..1fb16f9edfd5 --- /dev/null +++ b/arch/arm/plat-omap/ocpi.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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/plat-omap/pm.c b/arch/arm/plat-omap/pm.c new file mode 100644 index 000000000000..e6536b16c385 --- /dev/null +++ b/arch/arm/plat-omap/pm.c | |||
@@ -0,0 +1,632 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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/time.h> | ||
45 | #include <asm/mach-types.h> | ||
46 | |||
47 | #include <asm/arch/omap16xx.h> | ||
48 | #include <asm/arch/pm.h> | ||
49 | #include <asm/arch/mux.h> | ||
50 | #include <asm/arch/tc.h> | ||
51 | #include <asm/arch/tps65010.h> | ||
52 | |||
53 | #include "clock.h" | ||
54 | |||
55 | static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; | ||
56 | static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; | ||
57 | static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; | ||
58 | static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; | ||
59 | |||
60 | /* | ||
61 | * Let's power down on idle, but only if we are really | ||
62 | * idle, because once we start down the path of | ||
63 | * going idle we continue to do idle even if we get | ||
64 | * a clock tick interrupt . . | ||
65 | */ | ||
66 | void omap_pm_idle(void) | ||
67 | { | ||
68 | int (*func_ptr)(void) = 0; | ||
69 | unsigned int mask32 = 0; | ||
70 | |||
71 | /* | ||
72 | * If the DSP is being used let's just idle the CPU, the overhead | ||
73 | * to wake up from Big Sleep is big, milliseconds versus micro | ||
74 | * seconds for wait for interrupt. | ||
75 | */ | ||
76 | |||
77 | local_irq_disable(); | ||
78 | local_fiq_disable(); | ||
79 | if (need_resched()) { | ||
80 | local_fiq_enable(); | ||
81 | local_irq_enable(); | ||
82 | return; | ||
83 | } | ||
84 | mask32 = omap_readl(ARM_SYSST); | ||
85 | |||
86 | /* | ||
87 | * Since an interrupt may set up a timer, we don't want to | ||
88 | * reprogram the hardware timer with interrupts enabled. | ||
89 | * Re-enable interrupts only after returning from idle. | ||
90 | */ | ||
91 | timer_dyn_reprogram(); | ||
92 | |||
93 | if ((mask32 & DSP_IDLE) == 0) { | ||
94 | __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); | ||
95 | } else { | ||
96 | |||
97 | if (cpu_is_omap1510()) { | ||
98 | func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); | ||
99 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
100 | func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); | ||
101 | } else if (cpu_is_omap5912()) { | ||
102 | func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); | ||
103 | } | ||
104 | |||
105 | func_ptr(); | ||
106 | } | ||
107 | local_fiq_enable(); | ||
108 | local_irq_enable(); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Configuration of the wakeup event is board specific. For the | ||
113 | * moment we put it into this helper function. Later it may move | ||
114 | * to board specific files. | ||
115 | */ | ||
116 | static void omap_pm_wakeup_setup(void) | ||
117 | { | ||
118 | /* | ||
119 | * Enable ARM XOR clock and release peripheral from reset by | ||
120 | * writing 1 to PER_EN bit in ARM_RSTCT2, this is required | ||
121 | * for UART configuration to use UART2 to wake up. | ||
122 | */ | ||
123 | |||
124 | omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); | ||
125 | omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); | ||
126 | omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); | ||
127 | |||
128 | /* | ||
129 | * Turn off all interrupts except L1-2nd level cascade, | ||
130 | * and the L2 wakeup interrupts: keypad and UART2. | ||
131 | */ | ||
132 | |||
133 | omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); | ||
134 | |||
135 | if (cpu_is_omap1510()) { | ||
136 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); | ||
137 | } | ||
138 | |||
139 | if (cpu_is_omap16xx()) { | ||
140 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); | ||
141 | |||
142 | omap_writel(~0x0, OMAP_IH2_1_MIR); | ||
143 | omap_writel(~0x0, OMAP_IH2_2_MIR); | ||
144 | omap_writel(~0x0, OMAP_IH2_3_MIR); | ||
145 | } | ||
146 | |||
147 | /* New IRQ agreement */ | ||
148 | omap_writel(1, OMAP_IH1_CONTROL); | ||
149 | |||
150 | /* external PULL to down, bit 22 = 0 */ | ||
151 | omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2); | ||
152 | } | ||
153 | |||
154 | void omap_pm_suspend(void) | ||
155 | { | ||
156 | unsigned int mask32 = 0; | ||
157 | unsigned long arg0 = 0, arg1 = 0; | ||
158 | int (*func_ptr)(unsigned short, unsigned short) = 0; | ||
159 | unsigned short save_dsp_idlect2; | ||
160 | |||
161 | printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); | ||
162 | |||
163 | if (machine_is_omap_osk()) { | ||
164 | /* Stop LED1 (D9) blink */ | ||
165 | tps65010_set_led(LED1, OFF); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Step 1: turn off interrupts | ||
170 | */ | ||
171 | |||
172 | local_irq_disable(); | ||
173 | local_fiq_disable(); | ||
174 | |||
175 | /* | ||
176 | * Step 2: save registers | ||
177 | * | ||
178 | * The omap is a strange/beautiful device. The caches, memory | ||
179 | * and register state are preserved across power saves. | ||
180 | * We have to save and restore very little register state to | ||
181 | * idle the omap. | ||
182 | * | ||
183 | * Save interrupt, MPUI, ARM and UPLD control registers. | ||
184 | */ | ||
185 | |||
186 | if (cpu_is_omap1510()) { | ||
187 | MPUI1510_SAVE(OMAP_IH1_MIR); | ||
188 | MPUI1510_SAVE(OMAP_IH2_MIR); | ||
189 | MPUI1510_SAVE(MPUI_CTRL); | ||
190 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
191 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
192 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
193 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
194 | } else if (cpu_is_omap16xx()) { | ||
195 | MPUI1610_SAVE(OMAP_IH1_MIR); | ||
196 | MPUI1610_SAVE(OMAP_IH2_0_MIR); | ||
197 | MPUI1610_SAVE(OMAP_IH2_1_MIR); | ||
198 | MPUI1610_SAVE(OMAP_IH2_2_MIR); | ||
199 | MPUI1610_SAVE(OMAP_IH2_3_MIR); | ||
200 | MPUI1610_SAVE(MPUI_CTRL); | ||
201 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
202 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
203 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
204 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
205 | } | ||
206 | |||
207 | ARM_SAVE(ARM_CKCTL); | ||
208 | ARM_SAVE(ARM_IDLECT1); | ||
209 | ARM_SAVE(ARM_IDLECT2); | ||
210 | ARM_SAVE(ARM_EWUPCT); | ||
211 | ARM_SAVE(ARM_RSTCT1); | ||
212 | ARM_SAVE(ARM_RSTCT2); | ||
213 | ARM_SAVE(ARM_SYSST); | ||
214 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
215 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
216 | |||
217 | /* | ||
218 | * Step 3: LOW_PWR signal enabling | ||
219 | * | ||
220 | * Allow the LOW_PWR signal to be visible on MPUIO5 ball. | ||
221 | */ | ||
222 | if (cpu_is_omap1510()) { | ||
223 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
224 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
225 | OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
226 | } else if (cpu_is_omap16xx()) { | ||
227 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
228 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
229 | OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
230 | } | ||
231 | |||
232 | /* configure LOW_PWR pin */ | ||
233 | omap_cfg_reg(T20_1610_LOW_PWR); | ||
234 | |||
235 | /* | ||
236 | * Step 4: OMAP DSP Shutdown | ||
237 | */ | ||
238 | |||
239 | /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ | ||
240 | omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, | ||
241 | ARM_RSTCT1); | ||
242 | |||
243 | /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ | ||
244 | omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); | ||
245 | |||
246 | /* Set EN_DSPCK = 0, stop DSP block clock */ | ||
247 | omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); | ||
248 | |||
249 | /* Stop any DSP domain clocks */ | ||
250 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
251 | save_dsp_idlect2 = __raw_readw(DSP_IDLECT2); | ||
252 | __raw_writew(0, DSP_IDLECT2); | ||
253 | |||
254 | /* | ||
255 | * Step 5: Wakeup Event Setup | ||
256 | */ | ||
257 | |||
258 | omap_pm_wakeup_setup(); | ||
259 | |||
260 | /* | ||
261 | * Step 6a: ARM and Traffic controller shutdown | ||
262 | * | ||
263 | * Step 6 starts here with clock and watchdog disable | ||
264 | */ | ||
265 | |||
266 | /* stop clocks */ | ||
267 | mask32 = omap_readl(ARM_IDLECT2); | ||
268 | mask32 &= ~(1<<EN_WDTCK); /* bit 0 -> 0 (WDT clock) */ | ||
269 | mask32 |= (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */ | ||
270 | mask32 &= ~(1<<EN_PERCK); /* bit 2 -> 0 (MPUPER_CK clock) */ | ||
271 | mask32 &= ~(1<<EN_LCDCK); /* bit 3 -> 0 (LCDC clock) */ | ||
272 | mask32 &= ~(1<<EN_LBCK); /* bit 4 -> 0 (local bus clock) */ | ||
273 | mask32 |= (1<<EN_APICK); /* bit 6 -> 1 (MPUI clock) */ | ||
274 | mask32 &= ~(1<<EN_TIMCK); /* bit 7 -> 0 (MPU timer clock) */ | ||
275 | mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */ | ||
276 | mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */ | ||
277 | omap_writel(mask32, ARM_IDLECT2); | ||
278 | |||
279 | /* disable ARM watchdog */ | ||
280 | omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); | ||
281 | omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); | ||
282 | |||
283 | /* | ||
284 | * Step 6b: ARM and Traffic controller shutdown | ||
285 | * | ||
286 | * Step 6 continues here. Prepare jump to power management | ||
287 | * assembly code in internal SRAM. | ||
288 | * | ||
289 | * Since the omap_cpu_suspend routine has been copied to | ||
290 | * SRAM, we'll do an indirect procedure call to it and pass the | ||
291 | * contents of arm_idlect1 and arm_idlect2 so it can restore | ||
292 | * them when it wakes up and it will return. | ||
293 | */ | ||
294 | |||
295 | arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; | ||
296 | arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; | ||
297 | |||
298 | if (cpu_is_omap1510()) { | ||
299 | func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); | ||
300 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
301 | func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); | ||
302 | } else if (cpu_is_omap5912()) { | ||
303 | func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Step 6c: ARM and Traffic controller shutdown | ||
308 | * | ||
309 | * Jump to assembly code. The processor will stay there | ||
310 | * until wake up. | ||
311 | */ | ||
312 | |||
313 | func_ptr(arg0, arg1); | ||
314 | |||
315 | /* | ||
316 | * If we are here, processor is woken up! | ||
317 | */ | ||
318 | |||
319 | if (cpu_is_omap1510()) { | ||
320 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
321 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
322 | ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
323 | } else if (cpu_is_omap16xx()) { | ||
324 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
325 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
326 | ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
327 | } | ||
328 | |||
329 | |||
330 | /* Restore DSP clocks */ | ||
331 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
332 | __raw_writew(save_dsp_idlect2, DSP_IDLECT2); | ||
333 | ARM_RESTORE(ARM_IDLECT2); | ||
334 | |||
335 | /* | ||
336 | * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did | ||
337 | */ | ||
338 | |||
339 | ARM_RESTORE(ARM_CKCTL); | ||
340 | ARM_RESTORE(ARM_EWUPCT); | ||
341 | ARM_RESTORE(ARM_RSTCT1); | ||
342 | ARM_RESTORE(ARM_RSTCT2); | ||
343 | ARM_RESTORE(ARM_SYSST); | ||
344 | ULPD_RESTORE(ULPD_CLOCK_CTRL); | ||
345 | ULPD_RESTORE(ULPD_STATUS_REQ); | ||
346 | |||
347 | if (cpu_is_omap1510()) { | ||
348 | MPUI1510_RESTORE(MPUI_CTRL); | ||
349 | MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
350 | MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); | ||
351 | MPUI1510_RESTORE(EMIFS_CONFIG); | ||
352 | MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); | ||
353 | MPUI1510_RESTORE(OMAP_IH1_MIR); | ||
354 | MPUI1510_RESTORE(OMAP_IH2_MIR); | ||
355 | } else if (cpu_is_omap16xx()) { | ||
356 | MPUI1610_RESTORE(MPUI_CTRL); | ||
357 | MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
358 | MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); | ||
359 | MPUI1610_RESTORE(EMIFS_CONFIG); | ||
360 | MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); | ||
361 | |||
362 | MPUI1610_RESTORE(OMAP_IH1_MIR); | ||
363 | MPUI1610_RESTORE(OMAP_IH2_0_MIR); | ||
364 | MPUI1610_RESTORE(OMAP_IH2_1_MIR); | ||
365 | MPUI1610_RESTORE(OMAP_IH2_2_MIR); | ||
366 | MPUI1610_RESTORE(OMAP_IH2_3_MIR); | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * Reenable interrupts | ||
371 | */ | ||
372 | |||
373 | local_irq_enable(); | ||
374 | local_fiq_enable(); | ||
375 | |||
376 | printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev); | ||
377 | |||
378 | if (machine_is_omap_osk()) { | ||
379 | /* Let LED1 (D9) blink again */ | ||
380 | tps65010_set_led(LED1, BLINK); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
385 | static int g_read_completed; | ||
386 | |||
387 | /* | ||
388 | * Read system PM registers for debugging | ||
389 | */ | ||
390 | static int omap_pm_read_proc( | ||
391 | char *page_buffer, | ||
392 | char **my_first_byte, | ||
393 | off_t virtual_start, | ||
394 | int length, | ||
395 | int *eof, | ||
396 | void *data) | ||
397 | { | ||
398 | int my_buffer_offset = 0; | ||
399 | char * const my_base = page_buffer; | ||
400 | |||
401 | ARM_SAVE(ARM_CKCTL); | ||
402 | ARM_SAVE(ARM_IDLECT1); | ||
403 | ARM_SAVE(ARM_IDLECT2); | ||
404 | ARM_SAVE(ARM_EWUPCT); | ||
405 | ARM_SAVE(ARM_RSTCT1); | ||
406 | ARM_SAVE(ARM_RSTCT2); | ||
407 | ARM_SAVE(ARM_SYSST); | ||
408 | |||
409 | ULPD_SAVE(ULPD_IT_STATUS); | ||
410 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
411 | ULPD_SAVE(ULPD_SOFT_REQ); | ||
412 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
413 | ULPD_SAVE(ULPD_DPLL_CTRL); | ||
414 | ULPD_SAVE(ULPD_POWER_CTRL); | ||
415 | |||
416 | if (cpu_is_omap1510()) { | ||
417 | MPUI1510_SAVE(MPUI_CTRL); | ||
418 | MPUI1510_SAVE(MPUI_DSP_STATUS); | ||
419 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
420 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
421 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
422 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
423 | } else if (cpu_is_omap16xx()) { | ||
424 | MPUI1610_SAVE(MPUI_CTRL); | ||
425 | MPUI1610_SAVE(MPUI_DSP_STATUS); | ||
426 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
427 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
428 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
429 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
430 | } | ||
431 | |||
432 | if (virtual_start == 0) { | ||
433 | g_read_completed = 0; | ||
434 | |||
435 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
436 | "ARM_CKCTL_REG: 0x%-8x \n" | ||
437 | "ARM_IDLECT1_REG: 0x%-8x \n" | ||
438 | "ARM_IDLECT2_REG: 0x%-8x \n" | ||
439 | "ARM_EWUPCT_REG: 0x%-8x \n" | ||
440 | "ARM_RSTCT1_REG: 0x%-8x \n" | ||
441 | "ARM_RSTCT2_REG: 0x%-8x \n" | ||
442 | "ARM_SYSST_REG: 0x%-8x \n" | ||
443 | "ULPD_IT_STATUS_REG: 0x%-4x \n" | ||
444 | "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" | ||
445 | "ULPD_SOFT_REQ_REG: 0x%-4x \n" | ||
446 | "ULPD_DPLL_CTRL_REG: 0x%-4x \n" | ||
447 | "ULPD_STATUS_REQ_REG: 0x%-4x \n" | ||
448 | "ULPD_POWER_CTRL_REG: 0x%-4x \n", | ||
449 | ARM_SHOW(ARM_CKCTL), | ||
450 | ARM_SHOW(ARM_IDLECT1), | ||
451 | ARM_SHOW(ARM_IDLECT2), | ||
452 | ARM_SHOW(ARM_EWUPCT), | ||
453 | ARM_SHOW(ARM_RSTCT1), | ||
454 | ARM_SHOW(ARM_RSTCT2), | ||
455 | ARM_SHOW(ARM_SYSST), | ||
456 | ULPD_SHOW(ULPD_IT_STATUS), | ||
457 | ULPD_SHOW(ULPD_CLOCK_CTRL), | ||
458 | ULPD_SHOW(ULPD_SOFT_REQ), | ||
459 | ULPD_SHOW(ULPD_DPLL_CTRL), | ||
460 | ULPD_SHOW(ULPD_STATUS_REQ), | ||
461 | ULPD_SHOW(ULPD_POWER_CTRL)); | ||
462 | |||
463 | if (cpu_is_omap1510()) { | ||
464 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
465 | "MPUI1510_CTRL_REG 0x%-8x \n" | ||
466 | "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" | ||
467 | "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
468 | "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
469 | "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
470 | "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
471 | MPUI1510_SHOW(MPUI_CTRL), | ||
472 | MPUI1510_SHOW(MPUI_DSP_STATUS), | ||
473 | MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
474 | MPUI1510_SHOW(MPUI_DSP_API_CONFIG), | ||
475 | MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), | ||
476 | MPUI1510_SHOW(EMIFS_CONFIG)); | ||
477 | } else if (cpu_is_omap16xx()) { | ||
478 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
479 | "MPUI1610_CTRL_REG 0x%-8x \n" | ||
480 | "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" | ||
481 | "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
482 | "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
483 | "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
484 | "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
485 | MPUI1610_SHOW(MPUI_CTRL), | ||
486 | MPUI1610_SHOW(MPUI_DSP_STATUS), | ||
487 | MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
488 | MPUI1610_SHOW(MPUI_DSP_API_CONFIG), | ||
489 | MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), | ||
490 | MPUI1610_SHOW(EMIFS_CONFIG)); | ||
491 | } | ||
492 | |||
493 | g_read_completed++; | ||
494 | } else if (g_read_completed >= 1) { | ||
495 | *eof = 1; | ||
496 | return 0; | ||
497 | } | ||
498 | g_read_completed++; | ||
499 | |||
500 | *my_first_byte = page_buffer; | ||
501 | return my_buffer_offset; | ||
502 | } | ||
503 | |||
504 | static void omap_pm_init_proc(void) | ||
505 | { | ||
506 | struct proc_dir_entry *entry; | ||
507 | |||
508 | entry = create_proc_read_entry("driver/omap_pm", | ||
509 | S_IWUSR | S_IRUGO, NULL, | ||
510 | omap_pm_read_proc, 0); | ||
511 | } | ||
512 | |||
513 | #endif /* DEBUG && CONFIG_PROC_FS */ | ||
514 | |||
515 | /* | ||
516 | * omap_pm_prepare - Do preliminary suspend work. | ||
517 | * @state: suspend state we're entering. | ||
518 | * | ||
519 | */ | ||
520 | //#include <asm/arch/hardware.h> | ||
521 | |||
522 | static int omap_pm_prepare(suspend_state_t state) | ||
523 | { | ||
524 | int error = 0; | ||
525 | |||
526 | switch (state) | ||
527 | { | ||
528 | case PM_SUSPEND_STANDBY: | ||
529 | case PM_SUSPEND_MEM: | ||
530 | break; | ||
531 | |||
532 | case PM_SUSPEND_DISK: | ||
533 | return -ENOTSUPP; | ||
534 | |||
535 | default: | ||
536 | return -EINVAL; | ||
537 | } | ||
538 | |||
539 | return error; | ||
540 | } | ||
541 | |||
542 | |||
543 | /* | ||
544 | * omap_pm_enter - Actually enter a sleep state. | ||
545 | * @state: State we're entering. | ||
546 | * | ||
547 | */ | ||
548 | |||
549 | static int omap_pm_enter(suspend_state_t state) | ||
550 | { | ||
551 | switch (state) | ||
552 | { | ||
553 | case PM_SUSPEND_STANDBY: | ||
554 | case PM_SUSPEND_MEM: | ||
555 | omap_pm_suspend(); | ||
556 | break; | ||
557 | |||
558 | case PM_SUSPEND_DISK: | ||
559 | return -ENOTSUPP; | ||
560 | |||
561 | default: | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | |||
569 | /** | ||
570 | * omap_pm_finish - Finish up suspend sequence. | ||
571 | * @state: State we're coming out of. | ||
572 | * | ||
573 | * This is called after we wake back up (or if entering the sleep state | ||
574 | * failed). | ||
575 | */ | ||
576 | |||
577 | static int omap_pm_finish(suspend_state_t state) | ||
578 | { | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | |||
583 | struct pm_ops omap_pm_ops ={ | ||
584 | .pm_disk_mode = 0, | ||
585 | .prepare = omap_pm_prepare, | ||
586 | .enter = omap_pm_enter, | ||
587 | .finish = omap_pm_finish, | ||
588 | }; | ||
589 | |||
590 | static int __init omap_pm_init(void) | ||
591 | { | ||
592 | printk("Power Management for TI OMAP.\n"); | ||
593 | pm_idle = omap_pm_idle; | ||
594 | /* | ||
595 | * We copy the assembler sleep/wakeup routines to SRAM. | ||
596 | * These routines need to be in SRAM as that's the only | ||
597 | * memory the MPU can see when it wakes up. | ||
598 | */ | ||
599 | |||
600 | #ifdef CONFIG_ARCH_OMAP1510 | ||
601 | if (cpu_is_omap1510()) { | ||
602 | memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND, | ||
603 | omap1510_idle_loop_suspend, | ||
604 | omap1510_idle_loop_suspend_sz); | ||
605 | memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend, | ||
606 | omap1510_cpu_suspend_sz); | ||
607 | } else | ||
608 | #endif | ||
609 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
610 | memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND, | ||
611 | omap1610_idle_loop_suspend, | ||
612 | omap1610_idle_loop_suspend_sz); | ||
613 | memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
614 | omap1610_cpu_suspend_sz); | ||
615 | } else if (cpu_is_omap5912()) { | ||
616 | memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND, | ||
617 | omap1610_idle_loop_suspend, | ||
618 | omap1610_idle_loop_suspend_sz); | ||
619 | memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
620 | omap1610_cpu_suspend_sz); | ||
621 | } | ||
622 | |||
623 | pm_set_ops(&omap_pm_ops); | ||
624 | |||
625 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
626 | omap_pm_init_proc(); | ||
627 | #endif | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | __initcall(omap_pm_init); | ||
632 | |||
diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/plat-omap/sleep.S new file mode 100644 index 000000000000..279490ce772b --- /dev/null +++ b/arch/arm/plat-omap/sleep.S | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-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/plat-omap/usb.c b/arch/arm/plat-omap/usb.c new file mode 100644 index 000000000000..25bc4a8dd763 --- /dev/null +++ b/arch/arm/plat-omap/usb.c | |||
@@ -0,0 +1,593 @@ | |||
1 | /* | ||
2 | * arch/arm/plat-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 | * | ||
45 | * Some board-*.c files will need to set up additional mux options, | ||
46 | * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. | ||
47 | */ | ||
48 | |||
49 | /* TESTED ON: | ||
50 | * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
51 | * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables | ||
52 | * - 5912 OSK UDC, with *nonstandard* A-to-A cable | ||
53 | * - 1510 Innovator UDC with bundled usb0 cable | ||
54 | * - 1510 Innovator OHCI with bundled usb1/usb2 cable | ||
55 | * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS | ||
56 | * - 1710 custom development board using alternate pin group | ||
57 | * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
58 | */ | ||
59 | |||
60 | /*-------------------------------------------------------------------------*/ | ||
61 | |||
62 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
63 | |||
64 | static struct otg_transceiver *xceiv; | ||
65 | |||
66 | /** | ||
67 | * otg_get_transceiver - find the (single) OTG transceiver driver | ||
68 | * | ||
69 | * Returns the transceiver driver, after getting a refcount to it; or | ||
70 | * null if there is no such transceiver. The caller is responsible for | ||
71 | * releasing that count. | ||
72 | */ | ||
73 | struct otg_transceiver *otg_get_transceiver(void) | ||
74 | { | ||
75 | if (xceiv) | ||
76 | get_device(xceiv->dev); | ||
77 | return xceiv; | ||
78 | } | ||
79 | EXPORT_SYMBOL(otg_get_transceiver); | ||
80 | |||
81 | int otg_set_transceiver(struct otg_transceiver *x) | ||
82 | { | ||
83 | if (xceiv && x) | ||
84 | return -EBUSY; | ||
85 | xceiv = x; | ||
86 | return 0; | ||
87 | } | ||
88 | EXPORT_SYMBOL(otg_set_transceiver); | ||
89 | |||
90 | #endif | ||
91 | |||
92 | /*-------------------------------------------------------------------------*/ | ||
93 | |||
94 | static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) | ||
95 | { | ||
96 | u32 syscon1 = 0; | ||
97 | |||
98 | if (nwires == 0) { | ||
99 | if (!cpu_is_omap15xx()) { | ||
100 | /* pulldown D+/D- */ | ||
101 | USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); | ||
102 | } | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | if (is_device) | ||
107 | omap_cfg_reg(W4_USB_PUEN); | ||
108 | |||
109 | /* internal transceiver */ | ||
110 | if (nwires == 2) { | ||
111 | // omap_cfg_reg(P9_USB_DP); | ||
112 | // omap_cfg_reg(R8_USB_DM); | ||
113 | |||
114 | if (cpu_is_omap15xx()) { | ||
115 | /* This works on 1510-Innovator */ | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* NOTES: | ||
120 | * - peripheral should configure VBUS detection! | ||
121 | * - only peripherals may use the internal D+/D- pulldowns | ||
122 | * - OTG support on this port not yet written | ||
123 | */ | ||
124 | |||
125 | USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4); | ||
126 | if (!is_device) | ||
127 | USB_TRANSCEIVER_CTRL_REG |= (3 << 1); | ||
128 | |||
129 | return 3 << 16; | ||
130 | } | ||
131 | |||
132 | /* alternate pin config, external transceiver */ | ||
133 | if (cpu_is_omap15xx()) { | ||
134 | printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | omap_cfg_reg(V6_USB0_TXD); | ||
139 | omap_cfg_reg(W9_USB0_TXEN); | ||
140 | omap_cfg_reg(W5_USB0_SE0); | ||
141 | |||
142 | /* NOTE: SPEED and SUSP aren't configured here */ | ||
143 | |||
144 | if (nwires != 3) | ||
145 | omap_cfg_reg(Y5_USB0_RCV); | ||
146 | if (nwires != 6) | ||
147 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
148 | |||
149 | switch (nwires) { | ||
150 | case 3: | ||
151 | syscon1 = 2; | ||
152 | break; | ||
153 | case 4: | ||
154 | syscon1 = 1; | ||
155 | break; | ||
156 | case 6: | ||
157 | syscon1 = 3; | ||
158 | omap_cfg_reg(AA9_USB0_VP); | ||
159 | omap_cfg_reg(R9_USB0_VM); | ||
160 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
161 | break; | ||
162 | default: | ||
163 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
164 | 0, nwires); | ||
165 | } | ||
166 | return syscon1 << 16; | ||
167 | } | ||
168 | |||
169 | static u32 __init omap_usb1_init(unsigned nwires) | ||
170 | { | ||
171 | u32 syscon1 = 0; | ||
172 | |||
173 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
174 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; | ||
175 | if (nwires == 0) | ||
176 | return 0; | ||
177 | |||
178 | /* external transceiver */ | ||
179 | omap_cfg_reg(USB1_TXD); | ||
180 | omap_cfg_reg(USB1_TXEN); | ||
181 | if (cpu_is_omap15xx()) { | ||
182 | omap_cfg_reg(USB1_SEO); | ||
183 | omap_cfg_reg(USB1_SPEED); | ||
184 | // SUSP | ||
185 | } else if (cpu_is_omap1610() || cpu_is_omap5912()) { | ||
186 | omap_cfg_reg(W13_1610_USB1_SE0); | ||
187 | omap_cfg_reg(R13_1610_USB1_SPEED); | ||
188 | // SUSP | ||
189 | } else if (cpu_is_omap1710()) { | ||
190 | omap_cfg_reg(R13_1710_USB1_SE0); | ||
191 | // SUSP | ||
192 | } else { | ||
193 | pr_debug("usb unrecognized\n"); | ||
194 | } | ||
195 | if (nwires != 3) | ||
196 | omap_cfg_reg(USB1_RCV); | ||
197 | |||
198 | switch (nwires) { | ||
199 | case 3: | ||
200 | syscon1 = 2; | ||
201 | break; | ||
202 | case 4: | ||
203 | syscon1 = 1; | ||
204 | break; | ||
205 | case 6: | ||
206 | syscon1 = 3; | ||
207 | omap_cfg_reg(USB1_VP); | ||
208 | omap_cfg_reg(USB1_VM); | ||
209 | if (!cpu_is_omap15xx()) | ||
210 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; | ||
211 | break; | ||
212 | default: | ||
213 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
214 | 1, nwires); | ||
215 | } | ||
216 | return syscon1 << 20; | ||
217 | } | ||
218 | |||
219 | static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) | ||
220 | { | ||
221 | u32 syscon1 = 0; | ||
222 | |||
223 | /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ | ||
224 | if (alt_pingroup || nwires == 0) | ||
225 | return 0; | ||
226 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
227 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
228 | |||
229 | /* external transceiver */ | ||
230 | if (cpu_is_omap15xx()) { | ||
231 | omap_cfg_reg(USB2_TXD); | ||
232 | omap_cfg_reg(USB2_TXEN); | ||
233 | omap_cfg_reg(USB2_SEO); | ||
234 | if (nwires != 3) | ||
235 | omap_cfg_reg(USB2_RCV); | ||
236 | /* there is no USB2_SPEED */ | ||
237 | } else if (cpu_is_omap16xx()) { | ||
238 | omap_cfg_reg(V6_USB2_TXD); | ||
239 | omap_cfg_reg(W9_USB2_TXEN); | ||
240 | omap_cfg_reg(W5_USB2_SE0); | ||
241 | if (nwires != 3) | ||
242 | omap_cfg_reg(Y5_USB2_RCV); | ||
243 | // FIXME omap_cfg_reg(USB2_SPEED); | ||
244 | } else { | ||
245 | pr_debug("usb unrecognized\n"); | ||
246 | } | ||
247 | // omap_cfg_reg(USB2_SUSP); | ||
248 | |||
249 | switch (nwires) { | ||
250 | case 3: | ||
251 | syscon1 = 2; | ||
252 | break; | ||
253 | case 4: | ||
254 | syscon1 = 1; | ||
255 | break; | ||
256 | case 6: | ||
257 | syscon1 = 3; | ||
258 | if (cpu_is_omap15xx()) { | ||
259 | omap_cfg_reg(USB2_VP); | ||
260 | omap_cfg_reg(USB2_VM); | ||
261 | } else { | ||
262 | omap_cfg_reg(AA9_USB2_VP); | ||
263 | omap_cfg_reg(R9_USB2_VM); | ||
264 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
265 | } | ||
266 | break; | ||
267 | default: | ||
268 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
269 | 2, nwires); | ||
270 | } | ||
271 | return syscon1 << 24; | ||
272 | } | ||
273 | |||
274 | /*-------------------------------------------------------------------------*/ | ||
275 | |||
276 | #if defined(CONFIG_USB_GADGET_OMAP) || \ | ||
277 | defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ | ||
278 | (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) | ||
279 | static void usb_release(struct device *dev) | ||
280 | { | ||
281 | /* normally not freed */ | ||
282 | } | ||
283 | #endif | ||
284 | |||
285 | #ifdef CONFIG_USB_GADGET_OMAP | ||
286 | |||
287 | static struct resource udc_resources[] = { | ||
288 | /* order is significant! */ | ||
289 | { /* registers */ | ||
290 | .start = UDC_BASE, | ||
291 | .end = UDC_BASE + 0xff, | ||
292 | .flags = IORESOURCE_MEM, | ||
293 | }, { /* general IRQ */ | ||
294 | .start = IH2_BASE + 20, | ||
295 | .flags = IORESOURCE_IRQ, | ||
296 | }, { /* PIO IRQ */ | ||
297 | .start = IH2_BASE + 30, | ||
298 | .flags = IORESOURCE_IRQ, | ||
299 | }, { /* SOF IRQ */ | ||
300 | .start = IH2_BASE + 29, | ||
301 | .flags = IORESOURCE_IRQ, | ||
302 | }, | ||
303 | }; | ||
304 | |||
305 | static u64 udc_dmamask = ~(u32)0; | ||
306 | |||
307 | static struct platform_device udc_device = { | ||
308 | .name = "omap_udc", | ||
309 | .id = -1, | ||
310 | .dev = { | ||
311 | .release = usb_release, | ||
312 | .dma_mask = &udc_dmamask, | ||
313 | .coherent_dma_mask = 0xffffffff, | ||
314 | }, | ||
315 | .num_resources = ARRAY_SIZE(udc_resources), | ||
316 | .resource = udc_resources, | ||
317 | }; | ||
318 | |||
319 | #endif | ||
320 | |||
321 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
322 | |||
323 | /* The dmamask must be set for OHCI to work */ | ||
324 | static u64 ohci_dmamask = ~(u32)0; | ||
325 | |||
326 | static struct resource ohci_resources[] = { | ||
327 | { | ||
328 | .start = OMAP_OHCI_BASE, | ||
329 | .end = OMAP_OHCI_BASE + 4096 - 1, | ||
330 | .flags = IORESOURCE_MEM, | ||
331 | }, | ||
332 | { | ||
333 | .start = INT_USB_HHC_1, | ||
334 | .flags = IORESOURCE_IRQ, | ||
335 | }, | ||
336 | }; | ||
337 | |||
338 | static struct platform_device ohci_device = { | ||
339 | .name = "ohci", | ||
340 | .id = -1, | ||
341 | .dev = { | ||
342 | .release = usb_release, | ||
343 | .dma_mask = &ohci_dmamask, | ||
344 | .coherent_dma_mask = 0xffffffff, | ||
345 | }, | ||
346 | .num_resources = ARRAY_SIZE(ohci_resources), | ||
347 | .resource = ohci_resources, | ||
348 | }; | ||
349 | |||
350 | #endif | ||
351 | |||
352 | #if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) | ||
353 | |||
354 | static struct resource otg_resources[] = { | ||
355 | /* order is significant! */ | ||
356 | { | ||
357 | .start = OTG_BASE, | ||
358 | .end = OTG_BASE + 0xff, | ||
359 | .flags = IORESOURCE_MEM, | ||
360 | }, { | ||
361 | .start = IH2_BASE + 8, | ||
362 | .flags = IORESOURCE_IRQ, | ||
363 | }, | ||
364 | }; | ||
365 | |||
366 | static struct platform_device otg_device = { | ||
367 | .name = "omap_otg", | ||
368 | .id = -1, | ||
369 | .dev = { | ||
370 | .release = usb_release, | ||
371 | }, | ||
372 | .num_resources = ARRAY_SIZE(otg_resources), | ||
373 | .resource = otg_resources, | ||
374 | }; | ||
375 | |||
376 | #endif | ||
377 | |||
378 | /*-------------------------------------------------------------------------*/ | ||
379 | |||
380 | #define ULPD_CLOCK_CTRL_REG __REG16(ULPD_CLOCK_CTRL) | ||
381 | #define ULPD_SOFT_REQ_REG __REG16(ULPD_SOFT_REQ) | ||
382 | |||
383 | |||
384 | // FIXME correct answer depends on hmc_mode, | ||
385 | // as does any nonzero value for config->otg port number | ||
386 | #ifdef CONFIG_USB_GADGET_OMAP | ||
387 | #define is_usb0_device(config) 1 | ||
388 | #else | ||
389 | #define is_usb0_device(config) 0 | ||
390 | #endif | ||
391 | |||
392 | /*-------------------------------------------------------------------------*/ | ||
393 | |||
394 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
395 | |||
396 | void __init | ||
397 | omap_otg_init(struct omap_usb_config *config) | ||
398 | { | ||
399 | u32 syscon = OTG_SYSCON_1_REG & 0xffff; | ||
400 | int status; | ||
401 | int alt_pingroup = 0; | ||
402 | |||
403 | /* NOTE: no bus or clock setup (yet?) */ | ||
404 | |||
405 | syscon = OTG_SYSCON_1_REG & 0xffff; | ||
406 | if (!(syscon & OTG_RESET_DONE)) | ||
407 | pr_debug("USB resets not complete?\n"); | ||
408 | |||
409 | // OTG_IRQ_EN_REG = 0; | ||
410 | |||
411 | /* pin muxing and transceiver pinouts */ | ||
412 | if (config->pins[0] > 2) /* alt pingroup 2 */ | ||
413 | alt_pingroup = 1; | ||
414 | syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
415 | syscon |= omap_usb1_init(config->pins[1]); | ||
416 | syscon |= omap_usb2_init(config->pins[2], alt_pingroup); | ||
417 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
418 | OTG_SYSCON_1_REG = syscon; | ||
419 | |||
420 | syscon = config->hmc_mode; | ||
421 | syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; | ||
422 | #ifdef CONFIG_USB_OTG | ||
423 | if (config->otg) | ||
424 | syscon |= OTG_EN; | ||
425 | #endif | ||
426 | pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); | ||
427 | pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); | ||
428 | OTG_SYSCON_2_REG = syscon; | ||
429 | |||
430 | printk("USB: hmc %d", config->hmc_mode); | ||
431 | if (alt_pingroup) | ||
432 | printk(", usb2 alt %d wires", config->pins[2]); | ||
433 | else if (config->pins[0]) | ||
434 | printk(", usb0 %d wires%s", config->pins[0], | ||
435 | is_usb0_device(config) ? " (dev)" : ""); | ||
436 | if (config->pins[1]) | ||
437 | printk(", usb1 %d wires", config->pins[1]); | ||
438 | if (!alt_pingroup && config->pins[2]) | ||
439 | printk(", usb2 %d wires", config->pins[2]); | ||
440 | if (config->otg) | ||
441 | printk(", Mini-AB on usb%d", config->otg - 1); | ||
442 | printk("\n"); | ||
443 | |||
444 | /* leave USB clocks/controllers off until needed */ | ||
445 | ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; | ||
446 | ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; | ||
447 | ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; | ||
448 | syscon = OTG_SYSCON_1_REG; | ||
449 | syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; | ||
450 | |||
451 | #ifdef CONFIG_USB_GADGET_OMAP | ||
452 | if (config->otg || config->register_dev) { | ||
453 | syscon &= ~DEV_IDLE_EN; | ||
454 | udc_device.dev.platform_data = config; | ||
455 | /* FIXME patch IRQ numbers for omap730 */ | ||
456 | status = platform_device_register(&udc_device); | ||
457 | if (status) | ||
458 | pr_debug("can't register UDC device, %d\n", status); | ||
459 | } | ||
460 | #endif | ||
461 | |||
462 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
463 | if (config->otg || config->register_host) { | ||
464 | syscon &= ~HST_IDLE_EN; | ||
465 | ohci_device.dev.platform_data = config; | ||
466 | if (cpu_is_omap730()) | ||
467 | ohci_resources[1].start = INT_730_USB_HHC_1; | ||
468 | status = platform_device_register(&ohci_device); | ||
469 | if (status) | ||
470 | pr_debug("can't register OHCI device, %d\n", status); | ||
471 | } | ||
472 | #endif | ||
473 | |||
474 | #ifdef CONFIG_USB_OTG | ||
475 | if (config->otg) { | ||
476 | syscon &= ~OTG_IDLE_EN; | ||
477 | otg_device.dev.platform_data = config; | ||
478 | if (cpu_is_omap730()) | ||
479 | otg_resources[1].start = INT_730_USB_OTG; | ||
480 | status = platform_device_register(&otg_device); | ||
481 | if (status) | ||
482 | pr_debug("can't register OTG device, %d\n", status); | ||
483 | } | ||
484 | #endif | ||
485 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
486 | OTG_SYSCON_1_REG = syscon; | ||
487 | |||
488 | status = 0; | ||
489 | } | ||
490 | |||
491 | #else | ||
492 | static inline void omap_otg_init(struct omap_usb_config *config) {} | ||
493 | #endif | ||
494 | |||
495 | /*-------------------------------------------------------------------------*/ | ||
496 | |||
497 | #ifdef CONFIG_ARCH_OMAP1510 | ||
498 | |||
499 | #define ULPD_DPLL_CTRL_REG __REG16(ULPD_DPLL_CTRL) | ||
500 | #define DPLL_IOB (1 << 13) | ||
501 | #define DPLL_PLL_ENABLE (1 << 4) | ||
502 | #define DPLL_LOCK (1 << 0) | ||
503 | |||
504 | #define ULPD_APLL_CTRL_REG __REG16(ULPD_APLL_CTRL) | ||
505 | #define APLL_NDPLL_SWITCH (1 << 0) | ||
506 | |||
507 | |||
508 | static void __init omap_1510_usb_init(struct omap_usb_config *config) | ||
509 | { | ||
510 | int status; | ||
511 | unsigned int val; | ||
512 | |||
513 | omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
514 | omap_usb1_init(config->pins[1]); | ||
515 | omap_usb2_init(config->pins[2], 0); | ||
516 | |||
517 | val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); | ||
518 | val |= (config->hmc_mode << 1); | ||
519 | omap_writel(val, MOD_CONF_CTRL_0); | ||
520 | |||
521 | printk("USB: hmc %d", config->hmc_mode); | ||
522 | if (config->pins[0]) | ||
523 | printk(", usb0 %d wires%s", config->pins[0], | ||
524 | is_usb0_device(config) ? " (dev)" : ""); | ||
525 | if (config->pins[1]) | ||
526 | printk(", usb1 %d wires", config->pins[1]); | ||
527 | if (config->pins[2]) | ||
528 | printk(", usb2 %d wires", config->pins[2]); | ||
529 | printk("\n"); | ||
530 | |||
531 | /* use DPLL for 48 MHz function clock */ | ||
532 | pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG, | ||
533 | ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG); | ||
534 | ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH; | ||
535 | ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE; | ||
536 | ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ; | ||
537 | while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK)) | ||
538 | cpu_relax(); | ||
539 | |||
540 | #ifdef CONFIG_USB_GADGET_OMAP | ||
541 | if (config->register_dev) { | ||
542 | udc_device.dev.platform_data = config; | ||
543 | status = platform_device_register(&udc_device); | ||
544 | if (status) | ||
545 | pr_debug("can't register UDC device, %d\n", status); | ||
546 | /* udc driver gates 48MHz by D+ pullup */ | ||
547 | } | ||
548 | #endif | ||
549 | |||
550 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
551 | if (config->register_host) { | ||
552 | ohci_device.dev.platform_data = config; | ||
553 | status = platform_device_register(&ohci_device); | ||
554 | if (status) | ||
555 | pr_debug("can't register OHCI device, %d\n", status); | ||
556 | /* hcd explicitly gates 48MHz */ | ||
557 | } | ||
558 | #endif | ||
559 | } | ||
560 | |||
561 | #else | ||
562 | static inline void omap_1510_usb_init(struct omap_usb_config *config) {} | ||
563 | #endif | ||
564 | |||
565 | /*-------------------------------------------------------------------------*/ | ||
566 | |||
567 | static struct omap_usb_config platform_data; | ||
568 | |||
569 | static int __init | ||
570 | omap_usb_init(void) | ||
571 | { | ||
572 | const struct omap_usb_config *config; | ||
573 | |||
574 | config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); | ||
575 | if (config == NULL) { | ||
576 | printk(KERN_ERR "USB: No board-specific " | ||
577 | "platform config found\n"); | ||
578 | return -ENODEV; | ||
579 | } | ||
580 | platform_data = *config; | ||
581 | |||
582 | if (cpu_is_omap730() || cpu_is_omap16xx()) | ||
583 | omap_otg_init(&platform_data); | ||
584 | else if (cpu_is_omap15xx()) | ||
585 | omap_1510_usb_init(&platform_data); | ||
586 | else { | ||
587 | printk(KERN_ERR "USB: No init for your chip yet\n"); | ||
588 | return -ENODEV; | ||
589 | } | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | subsys_initcall(omap_usb_init); | ||