]>
Commit | Line | Data |
---|---|---|
5e1c5ff4 TL |
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 | */ | |
5e1c5ff4 TL |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/pm.h> | |
15 | #include <linux/console.h> | |
16 | #include <linux/serial.h> | |
17 | #include <linux/tty.h> | |
18 | #include <linux/serial_8250.h> | |
19 | #include <linux/serial_reg.h> | |
f8ce2547 | 20 | #include <linux/clk.h> |
5e1c5ff4 | 21 | |
a09e64fb | 22 | #include <mach/hardware.h> |
5e1c5ff4 TL |
23 | #include <asm/system.h> |
24 | #include <asm/pgtable.h> | |
25 | #include <asm/mach/map.h> | |
5e1c5ff4 | 26 | #include <asm/io.h> |
92105bb7 | 27 | #include <asm/setup.h> |
5e1c5ff4 | 28 | |
a09e64fb RK |
29 | #include <mach/common.h> |
30 | #include <mach/board.h> | |
31 | #include <mach/control.h> | |
32 | #include <mach/mux.h> | |
33 | #include <mach/fpga.h> | |
5e1c5ff4 | 34 | |
a09e64fb | 35 | #include <mach/clock.h> |
5e1c5ff4 | 36 | |
44595982 PW |
37 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
38 | # include "../mach-omap2/sdrc.h" | |
39 | #endif | |
40 | ||
5e1c5ff4 TL |
41 | #define NO_LENGTH_CHECK 0xffffffff |
42 | ||
92105bb7 TL |
43 | unsigned char omap_bootloader_tag[512]; |
44 | int omap_bootloader_tag_len; | |
5e1c5ff4 TL |
45 | |
46 | struct omap_board_config_kernel *omap_board_config; | |
92105bb7 | 47 | int omap_board_config_size; |
5e1c5ff4 TL |
48 | |
49 | static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) | |
50 | { | |
51 | struct omap_board_config_kernel *kinfo = NULL; | |
52 | int i; | |
53 | ||
54 | #ifdef CONFIG_OMAP_BOOT_TAG | |
55 | struct omap_board_config_entry *info = NULL; | |
56 | ||
57 | if (omap_bootloader_tag_len > 4) | |
58 | info = (struct omap_board_config_entry *) omap_bootloader_tag; | |
59 | while (info != NULL) { | |
60 | u8 *next; | |
61 | ||
62 | if (info->tag == tag) { | |
63 | if (skip == 0) | |
64 | break; | |
65 | skip--; | |
66 | } | |
67 | ||
68 | if ((info->len & 0x03) != 0) { | |
69 | /* We bail out to avoid an alignment fault */ | |
70 | printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", | |
71 | info->len, info->tag); | |
72 | return NULL; | |
73 | } | |
74 | next = (u8 *) info + sizeof(*info) + info->len; | |
75 | if (next >= omap_bootloader_tag + omap_bootloader_tag_len) | |
76 | info = NULL; | |
77 | else | |
78 | info = (struct omap_board_config_entry *) next; | |
79 | } | |
80 | if (info != NULL) { | |
81 | /* Check the length as a lame attempt to check for | |
6cbdc8c5 | 82 | * binary inconsistency. */ |
5e1c5ff4 TL |
83 | if (len != NO_LENGTH_CHECK) { |
84 | /* Word-align len */ | |
85 | if (len & 0x03) | |
86 | len = (len + 3) & ~0x03; | |
87 | if (info->len != len) { | |
88 | printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", | |
89 | tag, len, info->len); | |
90 | return NULL; | |
91 | } | |
92 | } | |
93 | if (len_out != NULL) | |
94 | *len_out = info->len; | |
95 | return info->data; | |
96 | } | |
97 | #endif | |
98 | /* Try to find the config from the board-specific structures | |
99 | * in the kernel. */ | |
100 | for (i = 0; i < omap_board_config_size; i++) { | |
101 | if (omap_board_config[i].tag == tag) { | |
c40fae95 TL |
102 | if (skip == 0) { |
103 | kinfo = &omap_board_config[i]; | |
104 | break; | |
105 | } else { | |
106 | skip--; | |
107 | } | |
5e1c5ff4 TL |
108 | } |
109 | } | |
110 | if (kinfo == NULL) | |
111 | return NULL; | |
112 | return kinfo->data; | |
113 | } | |
114 | ||
115 | const void *__omap_get_config(u16 tag, size_t len, int nr) | |
116 | { | |
117 | return get_config(tag, len, nr, NULL); | |
118 | } | |
119 | EXPORT_SYMBOL(__omap_get_config); | |
120 | ||
121 | const void *omap_get_var_config(u16 tag, size_t *len) | |
122 | { | |
123 | return get_config(tag, NO_LENGTH_CHECK, 0, len); | |
124 | } | |
125 | EXPORT_SYMBOL(omap_get_var_config); | |
126 | ||
127 | static int __init omap_add_serial_console(void) | |
128 | { | |
1a8bfa1e TL |
129 | const struct omap_serial_console_config *con_info; |
130 | const struct omap_uart_config *uart_info; | |
131 | static char speed[11], *opt = NULL; | |
132 | int line, i, uart_idx; | |
133 | ||
134 | uart_info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config); | |
135 | con_info = omap_get_config(OMAP_TAG_SERIAL_CONSOLE, | |
136 | struct omap_serial_console_config); | |
137 | if (uart_info == NULL || con_info == NULL) | |
138 | return 0; | |
139 | ||
140 | if (con_info->console_uart == 0) | |
141 | return 0; | |
142 | ||
143 | if (con_info->console_speed) { | |
144 | snprintf(speed, sizeof(speed), "%u", con_info->console_speed); | |
145 | opt = speed; | |
146 | } | |
5e1c5ff4 | 147 | |
1a8bfa1e TL |
148 | uart_idx = con_info->console_uart - 1; |
149 | if (uart_idx >= OMAP_MAX_NR_PORTS) { | |
150 | printk(KERN_INFO "Console: external UART#%d. " | |
151 | "Not adding it as console this time.\n", | |
152 | uart_idx + 1); | |
153 | return 0; | |
154 | } | |
155 | if (!(uart_info->enabled_uarts & (1 << uart_idx))) { | |
156 | printk(KERN_ERR "Console: Selected UART#%d is " | |
157 | "not enabled for this platform\n", | |
158 | uart_idx + 1); | |
159 | return -1; | |
160 | } | |
161 | line = 0; | |
162 | for (i = 0; i < uart_idx; i++) { | |
163 | if (uart_info->enabled_uarts & (1 << i)) | |
164 | line++; | |
5e1c5ff4 | 165 | } |
1a8bfa1e | 166 | return add_preferred_console("ttyS", line, opt); |
5e1c5ff4 TL |
167 | } |
168 | console_initcall(omap_add_serial_console); | |
075192ae KH |
169 | |
170 | ||
171 | /* | |
172 | * 32KHz clocksource ... always available, on pretty most chips except | |
173 | * OMAP 730 and 1510. Other timers could be used as clocksources, with | |
174 | * higher resolution in free-running counter modes (e.g. 12 MHz xtal), | |
175 | * but systems won't necessarily want to spend resources that way. | |
176 | */ | |
177 | ||
178 | #if defined(CONFIG_ARCH_OMAP16XX) | |
179 | #define TIMER_32K_SYNCHRONIZED 0xfffbc410 | |
44595982 PW |
180 | #elif defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) |
181 | #define TIMER_32K_SYNCHRONIZED (OMAP2_32KSYNCT_BASE + 0x10) | |
075192ae KH |
182 | #endif |
183 | ||
184 | #ifdef TIMER_32K_SYNCHRONIZED | |
185 | ||
186 | #include <linux/clocksource.h> | |
187 | ||
188 | static cycle_t omap_32k_read(void) | |
189 | { | |
190 | return omap_readl(TIMER_32K_SYNCHRONIZED); | |
191 | } | |
192 | ||
193 | static struct clocksource clocksource_32k = { | |
194 | .name = "32k_counter", | |
195 | .rating = 250, | |
196 | .read = omap_32k_read, | |
197 | .mask = CLOCKSOURCE_MASK(32), | |
198 | .shift = 10, | |
199 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
200 | }; | |
201 | ||
f258b0c6 KH |
202 | /* |
203 | * Rounds down to nearest nsec. | |
204 | */ | |
205 | unsigned long long omap_32k_ticks_to_nsecs(unsigned long ticks_32k) | |
206 | { | |
207 | return cyc2ns(&clocksource_32k, ticks_32k); | |
208 | } | |
209 | ||
210 | /* | |
211 | * Returns current time from boot in nsecs. It's OK for this to wrap | |
212 | * around for now, as it's just a relative time stamp. | |
213 | */ | |
214 | unsigned long long sched_clock(void) | |
215 | { | |
216 | return omap_32k_ticks_to_nsecs(omap_32k_read()); | |
217 | } | |
218 | ||
075192ae KH |
219 | static int __init omap_init_clocksource_32k(void) |
220 | { | |
221 | static char err[] __initdata = KERN_ERR | |
222 | "%s: can't register clocksource!\n"; | |
223 | ||
44595982 PW |
224 | if (cpu_is_omap16xx() || cpu_class_is_omap2()) { |
225 | struct clk *sync_32k_ick; | |
226 | ||
227 | sync_32k_ick = clk_get(NULL, "omap_32ksync_ick"); | |
228 | if (sync_32k_ick) | |
229 | clk_enable(sync_32k_ick); | |
230 | ||
075192ae KH |
231 | clocksource_32k.mult = clocksource_hz2mult(32768, |
232 | clocksource_32k.shift); | |
233 | ||
234 | if (clocksource_register(&clocksource_32k)) | |
235 | printk(err, clocksource_32k.name); | |
236 | } | |
237 | return 0; | |
238 | } | |
239 | arch_initcall(omap_init_clocksource_32k); | |
240 | ||
241 | #endif /* TIMER_32K_SYNCHRONIZED */ | |
44595982 PW |
242 | |
243 | /* Global address base setup code */ | |
244 | ||
a58caad1 TL |
245 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
246 | ||
247 | static struct omap_globals *omap2_globals; | |
248 | ||
249 | static void __init __omap2_set_globals(void) | |
250 | { | |
251 | omap2_set_globals_memory(omap2_globals); | |
252 | omap2_set_globals_control(omap2_globals); | |
253 | omap2_set_globals_prcm(omap2_globals); | |
254 | } | |
255 | ||
256 | #endif | |
257 | ||
44595982 | 258 | #if defined(CONFIG_ARCH_OMAP2420) |
a58caad1 TL |
259 | |
260 | static struct omap_globals omap242x_globals = { | |
e8a91c95 RK |
261 | .tap = OMAP2_IO_ADDRESS(0x48014000), |
262 | .sdrc = OMAP2_IO_ADDRESS(OMAP2420_SDRC_BASE), | |
263 | .sms = OMAP2_IO_ADDRESS(OMAP2420_SMS_BASE), | |
264 | .ctrl = OMAP2_IO_ADDRESS(OMAP2420_CTRL_BASE), | |
265 | .prm = OMAP2_IO_ADDRESS(OMAP2420_PRM_BASE), | |
266 | .cm = OMAP2_IO_ADDRESS(OMAP2420_CM_BASE), | |
a58caad1 TL |
267 | }; |
268 | ||
44595982 PW |
269 | void __init omap2_set_globals_242x(void) |
270 | { | |
a58caad1 TL |
271 | omap2_globals = &omap242x_globals; |
272 | __omap2_set_globals(); | |
44595982 PW |
273 | } |
274 | #endif | |
275 | ||
276 | #if defined(CONFIG_ARCH_OMAP2430) | |
a58caad1 TL |
277 | |
278 | static struct omap_globals omap243x_globals = { | |
e8a91c95 RK |
279 | .tap = OMAP2_IO_ADDRESS(0x4900a000), |
280 | .sdrc = OMAP2_IO_ADDRESS(OMAP243X_SDRC_BASE), | |
281 | .sms = OMAP2_IO_ADDRESS(OMAP243X_SMS_BASE), | |
282 | .ctrl = OMAP2_IO_ADDRESS(OMAP243X_CTRL_BASE), | |
283 | .prm = OMAP2_IO_ADDRESS(OMAP2430_PRM_BASE), | |
284 | .cm = OMAP2_IO_ADDRESS(OMAP2430_CM_BASE), | |
a58caad1 TL |
285 | }; |
286 | ||
44595982 PW |
287 | void __init omap2_set_globals_243x(void) |
288 | { | |
a58caad1 TL |
289 | omap2_globals = &omap243x_globals; |
290 | __omap2_set_globals(); | |
44595982 PW |
291 | } |
292 | #endif | |
293 | ||
294 | #if defined(CONFIG_ARCH_OMAP3430) | |
a58caad1 TL |
295 | |
296 | static struct omap_globals omap343x_globals = { | |
e8a91c95 RK |
297 | .tap = OMAP2_IO_ADDRESS(0x4830A000), |
298 | .sdrc = OMAP2_IO_ADDRESS(OMAP343X_SDRC_BASE), | |
299 | .sms = OMAP2_IO_ADDRESS(OMAP343X_SMS_BASE), | |
300 | .ctrl = OMAP2_IO_ADDRESS(OMAP343X_CTRL_BASE), | |
301 | .prm = OMAP2_IO_ADDRESS(OMAP3430_PRM_BASE), | |
302 | .cm = OMAP2_IO_ADDRESS(OMAP3430_CM_BASE), | |
a58caad1 TL |
303 | }; |
304 | ||
44595982 PW |
305 | void __init omap2_set_globals_343x(void) |
306 | { | |
a58caad1 TL |
307 | omap2_globals = &omap343x_globals; |
308 | __omap2_set_globals(); | |
44595982 PW |
309 | } |
310 | #endif | |
311 |