diff options
Diffstat (limited to 'arch/arm/plat-s3c24xx/cpu.c')
-rw-r--r-- | arch/arm/plat-s3c24xx/cpu.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c new file mode 100644 index 000000000000..6a2d1070e5a0 --- /dev/null +++ b/arch/arm/plat-s3c24xx/cpu.c | |||
@@ -0,0 +1,368 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/cpu.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * http://www.simtec.co.uk/products/SWLINUX/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * S3C24XX CPU Support | ||
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 as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/serial_core.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | |||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <asm/delay.h> | ||
36 | |||
37 | #include <asm/mach/arch.h> | ||
38 | #include <asm/mach/map.h> | ||
39 | |||
40 | #include <asm/arch/regs-gpio.h> | ||
41 | #include <asm/arch/regs-serial.h> | ||
42 | |||
43 | #include <asm/plat-s3c24xx/cpu.h> | ||
44 | #include <asm/plat-s3c24xx/devs.h> | ||
45 | #include <asm/plat-s3c24xx/clock.h> | ||
46 | #include <asm/plat-s3c24xx/s3c2400.h> | ||
47 | #include <asm/plat-s3c24xx/s3c2410.h> | ||
48 | #include <asm/plat-s3c24xx/s3c2412.h> | ||
49 | #include "s3c244x.h" | ||
50 | #include <asm/plat-s3c24xx/s3c2440.h> | ||
51 | #include <asm/plat-s3c24xx/s3c2442.h> | ||
52 | #include <asm/plat-s3c24xx/s3c2443.h> | ||
53 | |||
54 | struct cpu_table { | ||
55 | unsigned long idcode; | ||
56 | unsigned long idmask; | ||
57 | void (*map_io)(struct map_desc *mach_desc, int size); | ||
58 | void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); | ||
59 | void (*init_clocks)(int xtal); | ||
60 | int (*init)(void); | ||
61 | const char *name; | ||
62 | }; | ||
63 | |||
64 | /* table of supported CPUs */ | ||
65 | |||
66 | static const char name_s3c2400[] = "S3C2400"; | ||
67 | static const char name_s3c2410[] = "S3C2410"; | ||
68 | static const char name_s3c2412[] = "S3C2412"; | ||
69 | static const char name_s3c2440[] = "S3C2440"; | ||
70 | static const char name_s3c2442[] = "S3C2442"; | ||
71 | static const char name_s3c2443[] = "S3C2443"; | ||
72 | static const char name_s3c2410a[] = "S3C2410A"; | ||
73 | static const char name_s3c2440a[] = "S3C2440A"; | ||
74 | |||
75 | static struct cpu_table cpu_ids[] __initdata = { | ||
76 | { | ||
77 | .idcode = 0x32410000, | ||
78 | .idmask = 0xffffffff, | ||
79 | .map_io = s3c2410_map_io, | ||
80 | .init_clocks = s3c2410_init_clocks, | ||
81 | .init_uarts = s3c2410_init_uarts, | ||
82 | .init = s3c2410_init, | ||
83 | .name = name_s3c2410 | ||
84 | }, | ||
85 | { | ||
86 | .idcode = 0x32410002, | ||
87 | .idmask = 0xffffffff, | ||
88 | .map_io = s3c2410_map_io, | ||
89 | .init_clocks = s3c2410_init_clocks, | ||
90 | .init_uarts = s3c2410_init_uarts, | ||
91 | .init = s3c2410_init, | ||
92 | .name = name_s3c2410a | ||
93 | }, | ||
94 | { | ||
95 | .idcode = 0x32440000, | ||
96 | .idmask = 0xffffffff, | ||
97 | .map_io = s3c244x_map_io, | ||
98 | .init_clocks = s3c244x_init_clocks, | ||
99 | .init_uarts = s3c244x_init_uarts, | ||
100 | .init = s3c2440_init, | ||
101 | .name = name_s3c2440 | ||
102 | }, | ||
103 | { | ||
104 | .idcode = 0x32440001, | ||
105 | .idmask = 0xffffffff, | ||
106 | .map_io = s3c244x_map_io, | ||
107 | .init_clocks = s3c244x_init_clocks, | ||
108 | .init_uarts = s3c244x_init_uarts, | ||
109 | .init = s3c2440_init, | ||
110 | .name = name_s3c2440a | ||
111 | }, | ||
112 | { | ||
113 | .idcode = 0x32440aaa, | ||
114 | .idmask = 0xffffffff, | ||
115 | .map_io = s3c244x_map_io, | ||
116 | .init_clocks = s3c244x_init_clocks, | ||
117 | .init_uarts = s3c244x_init_uarts, | ||
118 | .init = s3c2442_init, | ||
119 | .name = name_s3c2442 | ||
120 | }, | ||
121 | { | ||
122 | .idcode = 0x32412001, | ||
123 | .idmask = 0xffffffff, | ||
124 | .map_io = s3c2412_map_io, | ||
125 | .init_clocks = s3c2412_init_clocks, | ||
126 | .init_uarts = s3c2412_init_uarts, | ||
127 | .init = s3c2412_init, | ||
128 | .name = name_s3c2412, | ||
129 | }, | ||
130 | { /* a newer version of the s3c2412 */ | ||
131 | .idcode = 0x32412003, | ||
132 | .idmask = 0xffffffff, | ||
133 | .map_io = s3c2412_map_io, | ||
134 | .init_clocks = s3c2412_init_clocks, | ||
135 | .init_uarts = s3c2412_init_uarts, | ||
136 | .init = s3c2412_init, | ||
137 | .name = name_s3c2412, | ||
138 | }, | ||
139 | { | ||
140 | .idcode = 0x32443001, | ||
141 | .idmask = 0xffffffff, | ||
142 | .map_io = s3c2443_map_io, | ||
143 | .init_clocks = s3c2443_init_clocks, | ||
144 | .init_uarts = s3c2443_init_uarts, | ||
145 | .init = s3c2443_init, | ||
146 | .name = name_s3c2443, | ||
147 | }, | ||
148 | { | ||
149 | .idcode = 0x0, /* S3C2400 doesn't have an idcode */ | ||
150 | .idmask = 0xffffffff, | ||
151 | .map_io = s3c2400_map_io, | ||
152 | .init_clocks = s3c2400_init_clocks, | ||
153 | .init_uarts = s3c2400_init_uarts, | ||
154 | .init = s3c2400_init, | ||
155 | .name = name_s3c2400 | ||
156 | }, | ||
157 | }; | ||
158 | |||
159 | /* minimal IO mapping */ | ||
160 | |||
161 | static struct map_desc s3c_iodesc[] __initdata = { | ||
162 | IODESC_ENT(GPIO), | ||
163 | IODESC_ENT(IRQ), | ||
164 | IODESC_ENT(MEMCTRL), | ||
165 | IODESC_ENT(UART) | ||
166 | }; | ||
167 | |||
168 | |||
169 | static struct cpu_table * | ||
170 | s3c_lookup_cpu(unsigned long idcode) | ||
171 | { | ||
172 | struct cpu_table *tab; | ||
173 | int count; | ||
174 | |||
175 | tab = cpu_ids; | ||
176 | for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { | ||
177 | if ((idcode & tab->idmask) == tab->idcode) | ||
178 | return tab; | ||
179 | } | ||
180 | |||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* board information */ | ||
185 | |||
186 | static struct s3c24xx_board *board; | ||
187 | |||
188 | void s3c24xx_set_board(struct s3c24xx_board *b) | ||
189 | { | ||
190 | int i; | ||
191 | |||
192 | board = b; | ||
193 | |||
194 | if (b->clocks_count != 0) { | ||
195 | struct clk **ptr = b->clocks; | ||
196 | |||
197 | for (i = b->clocks_count; i > 0; i--, ptr++) | ||
198 | s3c24xx_register_clock(*ptr); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | /* cpu information */ | ||
203 | |||
204 | static struct cpu_table *cpu; | ||
205 | |||
206 | static unsigned long s3c24xx_read_idcode_v5(void) | ||
207 | { | ||
208 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) | ||
209 | return __raw_readl(S3C2412_GSTATUS1); | ||
210 | #else | ||
211 | return 1UL; /* don't look like an 2400 */ | ||
212 | #endif | ||
213 | } | ||
214 | |||
215 | static unsigned long s3c24xx_read_idcode_v4(void) | ||
216 | { | ||
217 | #ifndef CONFIG_CPU_S3C2400 | ||
218 | return __raw_readl(S3C2410_GSTATUS1); | ||
219 | #else | ||
220 | return 0UL; | ||
221 | #endif | ||
222 | } | ||
223 | |||
224 | void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) | ||
225 | { | ||
226 | unsigned long idcode = 0x0; | ||
227 | |||
228 | /* initialise the io descriptors we need for initialisation */ | ||
229 | iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); | ||
230 | |||
231 | if (cpu_architecture() >= CPU_ARCH_ARMv5) { | ||
232 | idcode = s3c24xx_read_idcode_v5(); | ||
233 | } else { | ||
234 | idcode = s3c24xx_read_idcode_v4(); | ||
235 | } | ||
236 | |||
237 | cpu = s3c_lookup_cpu(idcode); | ||
238 | |||
239 | if (cpu == NULL) { | ||
240 | printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); | ||
241 | panic("Unknown S3C24XX CPU"); | ||
242 | } | ||
243 | |||
244 | printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); | ||
245 | |||
246 | if (cpu->map_io == NULL || cpu->init == NULL) { | ||
247 | printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); | ||
248 | panic("Unsupported S3C24XX CPU"); | ||
249 | } | ||
250 | |||
251 | (cpu->map_io)(mach_desc, size); | ||
252 | } | ||
253 | |||
254 | /* s3c24xx_init_clocks | ||
255 | * | ||
256 | * Initialise the clock subsystem and associated information from the | ||
257 | * given master crystal value. | ||
258 | * | ||
259 | * xtal = 0 -> use default PLL crystal value (normally 12MHz) | ||
260 | * != 0 -> PLL crystal value in Hz | ||
261 | */ | ||
262 | |||
263 | void __init s3c24xx_init_clocks(int xtal) | ||
264 | { | ||
265 | if (xtal == 0) | ||
266 | xtal = 12*1000*1000; | ||
267 | |||
268 | if (cpu == NULL) | ||
269 | panic("s3c24xx_init_clocks: no cpu setup?\n"); | ||
270 | |||
271 | if (cpu->init_clocks == NULL) | ||
272 | panic("s3c24xx_init_clocks: cpu has no clock init\n"); | ||
273 | else | ||
274 | (cpu->init_clocks)(xtal); | ||
275 | } | ||
276 | |||
277 | /* uart management */ | ||
278 | |||
279 | static int nr_uarts __initdata = 0; | ||
280 | |||
281 | static struct s3c2410_uartcfg uart_cfgs[3]; | ||
282 | |||
283 | /* s3c24xx_init_uartdevs | ||
284 | * | ||
285 | * copy the specified platform data and configuration into our central | ||
286 | * set of devices, before the data is thrown away after the init process. | ||
287 | * | ||
288 | * This also fills in the array passed to the serial driver for the | ||
289 | * early initialisation of the console. | ||
290 | */ | ||
291 | |||
292 | void __init s3c24xx_init_uartdevs(char *name, | ||
293 | struct s3c24xx_uart_resources *res, | ||
294 | struct s3c2410_uartcfg *cfg, int no) | ||
295 | { | ||
296 | struct platform_device *platdev; | ||
297 | struct s3c2410_uartcfg *cfgptr = uart_cfgs; | ||
298 | struct s3c24xx_uart_resources *resp; | ||
299 | int uart; | ||
300 | |||
301 | memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); | ||
302 | |||
303 | for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { | ||
304 | platdev = s3c24xx_uart_src[cfgptr->hwport]; | ||
305 | |||
306 | resp = res + cfgptr->hwport; | ||
307 | |||
308 | s3c24xx_uart_devs[uart] = platdev; | ||
309 | |||
310 | platdev->name = name; | ||
311 | platdev->resource = resp->resources; | ||
312 | platdev->num_resources = resp->nr_resources; | ||
313 | |||
314 | platdev->dev.platform_data = cfgptr; | ||
315 | } | ||
316 | |||
317 | nr_uarts = no; | ||
318 | } | ||
319 | |||
320 | void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
321 | { | ||
322 | if (cpu == NULL) | ||
323 | return; | ||
324 | |||
325 | if (cpu->init_uarts == NULL) { | ||
326 | printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); | ||
327 | } else | ||
328 | (cpu->init_uarts)(cfg, no); | ||
329 | } | ||
330 | |||
331 | static int __init s3c_arch_init(void) | ||
332 | { | ||
333 | int ret; | ||
334 | |||
335 | // do the correct init for cpu | ||
336 | |||
337 | if (cpu == NULL) | ||
338 | panic("s3c_arch_init: NULL cpu\n"); | ||
339 | |||
340 | ret = (cpu->init)(); | ||
341 | if (ret != 0) | ||
342 | return ret; | ||
343 | |||
344 | ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); | ||
345 | if (ret != 0) | ||
346 | return ret; | ||
347 | |||
348 | if (board != NULL) { | ||
349 | struct platform_device **ptr = board->devices; | ||
350 | int i; | ||
351 | |||
352 | for (i = 0; i < board->devices_count; i++, ptr++) { | ||
353 | ret = platform_device_register(*ptr); | ||
354 | |||
355 | if (ret) { | ||
356 | printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)->name, ret, *ptr); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | /* mask any error, we may not need all these board | ||
361 | * devices */ | ||
362 | ret = 0; | ||
363 | } | ||
364 | |||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | arch_initcall(s3c_arch_init); | ||