diff options
Diffstat (limited to 'arch/arm/plat-s3c24xx/cpu.c')
-rw-r--r-- | arch/arm/plat-s3c24xx/cpu.c | 357 |
1 files changed, 357 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..2fbb74969379 --- /dev/null +++ b/arch/arm/plat-s3c24xx/cpu.c | |||
@@ -0,0 +1,357 @@ | |||
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 | |||
53 | struct cpu_table { | ||
54 | unsigned long idcode; | ||
55 | unsigned long idmask; | ||
56 | void (*map_io)(struct map_desc *mach_desc, int size); | ||
57 | void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); | ||
58 | void (*init_clocks)(int xtal); | ||
59 | int (*init)(void); | ||
60 | const char *name; | ||
61 | }; | ||
62 | |||
63 | /* table of supported CPUs */ | ||
64 | |||
65 | static const char name_s3c2400[] = "S3C2400"; | ||
66 | static const char name_s3c2410[] = "S3C2410"; | ||
67 | static const char name_s3c2412[] = "S3C2412"; | ||
68 | static const char name_s3c2440[] = "S3C2440"; | ||
69 | static const char name_s3c2442[] = "S3C2442"; | ||
70 | static const char name_s3c2410a[] = "S3C2410A"; | ||
71 | static const char name_s3c2440a[] = "S3C2440A"; | ||
72 | |||
73 | static struct cpu_table cpu_ids[] __initdata = { | ||
74 | { | ||
75 | .idcode = 0x32410000, | ||
76 | .idmask = 0xffffffff, | ||
77 | .map_io = s3c2410_map_io, | ||
78 | .init_clocks = s3c2410_init_clocks, | ||
79 | .init_uarts = s3c2410_init_uarts, | ||
80 | .init = s3c2410_init, | ||
81 | .name = name_s3c2410 | ||
82 | }, | ||
83 | { | ||
84 | .idcode = 0x32410002, | ||
85 | .idmask = 0xffffffff, | ||
86 | .map_io = s3c2410_map_io, | ||
87 | .init_clocks = s3c2410_init_clocks, | ||
88 | .init_uarts = s3c2410_init_uarts, | ||
89 | .init = s3c2410_init, | ||
90 | .name = name_s3c2410a | ||
91 | }, | ||
92 | { | ||
93 | .idcode = 0x32440000, | ||
94 | .idmask = 0xffffffff, | ||
95 | .map_io = s3c244x_map_io, | ||
96 | .init_clocks = s3c244x_init_clocks, | ||
97 | .init_uarts = s3c244x_init_uarts, | ||
98 | .init = s3c2440_init, | ||
99 | .name = name_s3c2440 | ||
100 | }, | ||
101 | { | ||
102 | .idcode = 0x32440001, | ||
103 | .idmask = 0xffffffff, | ||
104 | .map_io = s3c244x_map_io, | ||
105 | .init_clocks = s3c244x_init_clocks, | ||
106 | .init_uarts = s3c244x_init_uarts, | ||
107 | .init = s3c2440_init, | ||
108 | .name = name_s3c2440a | ||
109 | }, | ||
110 | { | ||
111 | .idcode = 0x32440aaa, | ||
112 | .idmask = 0xffffffff, | ||
113 | .map_io = s3c244x_map_io, | ||
114 | .init_clocks = s3c244x_init_clocks, | ||
115 | .init_uarts = s3c244x_init_uarts, | ||
116 | .init = s3c2442_init, | ||
117 | .name = name_s3c2442 | ||
118 | }, | ||
119 | { | ||
120 | .idcode = 0x32412001, | ||
121 | .idmask = 0xffffffff, | ||
122 | .map_io = s3c2412_map_io, | ||
123 | .init_clocks = s3c2412_init_clocks, | ||
124 | .init_uarts = s3c2412_init_uarts, | ||
125 | .init = s3c2412_init, | ||
126 | .name = name_s3c2412, | ||
127 | }, | ||
128 | { /* a newer version of the s3c2412 */ | ||
129 | .idcode = 0x32412003, | ||
130 | .idmask = 0xffffffff, | ||
131 | .map_io = s3c2412_map_io, | ||
132 | .init_clocks = s3c2412_init_clocks, | ||
133 | .init_uarts = s3c2412_init_uarts, | ||
134 | .init = s3c2412_init, | ||
135 | .name = name_s3c2412, | ||
136 | }, | ||
137 | { | ||
138 | .idcode = 0x0, /* S3C2400 doesn't have an idcode */ | ||
139 | .idmask = 0xffffffff, | ||
140 | .map_io = s3c2400_map_io, | ||
141 | .init_clocks = s3c2400_init_clocks, | ||
142 | .init_uarts = s3c2400_init_uarts, | ||
143 | .init = s3c2400_init, | ||
144 | .name = name_s3c2400 | ||
145 | }, | ||
146 | }; | ||
147 | |||
148 | /* minimal IO mapping */ | ||
149 | |||
150 | static struct map_desc s3c_iodesc[] __initdata = { | ||
151 | IODESC_ENT(GPIO), | ||
152 | IODESC_ENT(IRQ), | ||
153 | IODESC_ENT(MEMCTRL), | ||
154 | IODESC_ENT(UART) | ||
155 | }; | ||
156 | |||
157 | |||
158 | static struct cpu_table * | ||
159 | s3c_lookup_cpu(unsigned long idcode) | ||
160 | { | ||
161 | struct cpu_table *tab; | ||
162 | int count; | ||
163 | |||
164 | tab = cpu_ids; | ||
165 | for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { | ||
166 | if ((idcode & tab->idmask) == tab->idcode) | ||
167 | return tab; | ||
168 | } | ||
169 | |||
170 | return NULL; | ||
171 | } | ||
172 | |||
173 | /* board information */ | ||
174 | |||
175 | static struct s3c24xx_board *board; | ||
176 | |||
177 | void s3c24xx_set_board(struct s3c24xx_board *b) | ||
178 | { | ||
179 | int i; | ||
180 | |||
181 | board = b; | ||
182 | |||
183 | if (b->clocks_count != 0) { | ||
184 | struct clk **ptr = b->clocks; | ||
185 | |||
186 | for (i = b->clocks_count; i > 0; i--, ptr++) | ||
187 | s3c24xx_register_clock(*ptr); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /* cpu information */ | ||
192 | |||
193 | static struct cpu_table *cpu; | ||
194 | |||
195 | static unsigned long s3c24xx_read_idcode_v5(void) | ||
196 | { | ||
197 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) | ||
198 | return __raw_readl(S3C2412_GSTATUS1); | ||
199 | #else | ||
200 | return 1UL; /* don't look like an 2400 */ | ||
201 | #endif | ||
202 | } | ||
203 | |||
204 | static unsigned long s3c24xx_read_idcode_v4(void) | ||
205 | { | ||
206 | #ifndef CONFIG_CPU_S3C2400 | ||
207 | return __raw_readl(S3C2410_GSTATUS1); | ||
208 | #else | ||
209 | return 0UL; | ||
210 | #endif | ||
211 | } | ||
212 | |||
213 | void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) | ||
214 | { | ||
215 | unsigned long idcode = 0x0; | ||
216 | |||
217 | /* initialise the io descriptors we need for initialisation */ | ||
218 | iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); | ||
219 | |||
220 | if (cpu_architecture() >= CPU_ARCH_ARMv5) { | ||
221 | idcode = s3c24xx_read_idcode_v5(); | ||
222 | } else { | ||
223 | idcode = s3c24xx_read_idcode_v4(); | ||
224 | } | ||
225 | |||
226 | cpu = s3c_lookup_cpu(idcode); | ||
227 | |||
228 | if (cpu == NULL) { | ||
229 | printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); | ||
230 | panic("Unknown S3C24XX CPU"); | ||
231 | } | ||
232 | |||
233 | printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); | ||
234 | |||
235 | if (cpu->map_io == NULL || cpu->init == NULL) { | ||
236 | printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); | ||
237 | panic("Unsupported S3C24XX CPU"); | ||
238 | } | ||
239 | |||
240 | (cpu->map_io)(mach_desc, size); | ||
241 | } | ||
242 | |||
243 | /* s3c24xx_init_clocks | ||
244 | * | ||
245 | * Initialise the clock subsystem and associated information from the | ||
246 | * given master crystal value. | ||
247 | * | ||
248 | * xtal = 0 -> use default PLL crystal value (normally 12MHz) | ||
249 | * != 0 -> PLL crystal value in Hz | ||
250 | */ | ||
251 | |||
252 | void __init s3c24xx_init_clocks(int xtal) | ||
253 | { | ||
254 | if (xtal == 0) | ||
255 | xtal = 12*1000*1000; | ||
256 | |||
257 | if (cpu == NULL) | ||
258 | panic("s3c24xx_init_clocks: no cpu setup?\n"); | ||
259 | |||
260 | if (cpu->init_clocks == NULL) | ||
261 | panic("s3c24xx_init_clocks: cpu has no clock init\n"); | ||
262 | else | ||
263 | (cpu->init_clocks)(xtal); | ||
264 | } | ||
265 | |||
266 | /* uart management */ | ||
267 | |||
268 | static int nr_uarts __initdata = 0; | ||
269 | |||
270 | static struct s3c2410_uartcfg uart_cfgs[3]; | ||
271 | |||
272 | /* s3c24xx_init_uartdevs | ||
273 | * | ||
274 | * copy the specified platform data and configuration into our central | ||
275 | * set of devices, before the data is thrown away after the init process. | ||
276 | * | ||
277 | * This also fills in the array passed to the serial driver for the | ||
278 | * early initialisation of the console. | ||
279 | */ | ||
280 | |||
281 | void __init s3c24xx_init_uartdevs(char *name, | ||
282 | struct s3c24xx_uart_resources *res, | ||
283 | struct s3c2410_uartcfg *cfg, int no) | ||
284 | { | ||
285 | struct platform_device *platdev; | ||
286 | struct s3c2410_uartcfg *cfgptr = uart_cfgs; | ||
287 | struct s3c24xx_uart_resources *resp; | ||
288 | int uart; | ||
289 | |||
290 | memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); | ||
291 | |||
292 | for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { | ||
293 | platdev = s3c24xx_uart_src[cfgptr->hwport]; | ||
294 | |||
295 | resp = res + cfgptr->hwport; | ||
296 | |||
297 | s3c24xx_uart_devs[uart] = platdev; | ||
298 | |||
299 | platdev->name = name; | ||
300 | platdev->resource = resp->resources; | ||
301 | platdev->num_resources = resp->nr_resources; | ||
302 | |||
303 | platdev->dev.platform_data = cfgptr; | ||
304 | } | ||
305 | |||
306 | nr_uarts = no; | ||
307 | } | ||
308 | |||
309 | void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
310 | { | ||
311 | if (cpu == NULL) | ||
312 | return; | ||
313 | |||
314 | if (cpu->init_uarts == NULL) { | ||
315 | printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); | ||
316 | } else | ||
317 | (cpu->init_uarts)(cfg, no); | ||
318 | } | ||
319 | |||
320 | static int __init s3c_arch_init(void) | ||
321 | { | ||
322 | int ret; | ||
323 | |||
324 | // do the correct init for cpu | ||
325 | |||
326 | if (cpu == NULL) | ||
327 | panic("s3c_arch_init: NULL cpu\n"); | ||
328 | |||
329 | ret = (cpu->init)(); | ||
330 | if (ret != 0) | ||
331 | return ret; | ||
332 | |||
333 | ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); | ||
334 | if (ret != 0) | ||
335 | return ret; | ||
336 | |||
337 | if (board != NULL) { | ||
338 | struct platform_device **ptr = board->devices; | ||
339 | int i; | ||
340 | |||
341 | for (i = 0; i < board->devices_count; i++, ptr++) { | ||
342 | ret = platform_device_register(*ptr); | ||
343 | |||
344 | if (ret) { | ||
345 | printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)->name, ret, *ptr); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /* mask any error, we may not need all these board | ||
350 | * devices */ | ||
351 | ret = 0; | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | arch_initcall(s3c_arch_init); | ||