aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAurelien Jacquiot <a-jacquiot@ti.com>2011-10-04 11:00:02 -0400
committerMark Salter <msalter@redhat.com>2011-10-06 19:47:28 -0400
commitc1a144d77a6ca3a14ba3c0fec30bc4fd20b3d817 (patch)
treee5a761211a22a90ca2eb1c933d2b3539c6888831 /arch
parentc278400c52c14203894c5dc0d63cf385239d8329 (diff)
C6X: early boot code
Original port to early 2.6 kernel using TI COFF toolchain. Brought up to date by Mark Salter <msalter@redhat.com> This patch provides the early boot code for C6X architecture. There is a 16 entry vector table which is used to direct reset and interrupt events. The vector table entries contain a small amount of code (maximum of 8 opcodes) which simply branches to the actual event handling code. The head.S code simply clears BSS, setups up a few control registers, and calls machine_init followed by start_kernel. The machine_init code in setup.c does the early flat tree parsing (memory, commandline, etc). At setup_arch time, the code does the usual memory setup and minimally scans the devicetree for any needed information. Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com> Signed-off-by: Mark Salter <msalter@redhat.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/c6x/kernel/head.S84
-rw-r--r--arch/c6x/kernel/setup.c498
-rw-r--r--arch/c6x/kernel/vectors.S81
3 files changed, 663 insertions, 0 deletions
diff --git a/arch/c6x/kernel/head.S b/arch/c6x/kernel/head.S
new file mode 100644
index 000000000000..133eab6edf6b
--- /dev/null
+++ b/arch/c6x/kernel/head.S
@@ -0,0 +1,84 @@
1;
2; Port on Texas Instruments TMS320C6x architecture
3;
4; Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
5; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.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/linkage.h>
12#include <linux/of_fdt.h>
13#include <asm/asm-offsets.h>
14
15 __HEAD
16ENTRY(_c_int00)
17 ;; Save magic and pointer
18 MV .S1 A4,A10
19 MV .S2 B4,B10
20 MVKL .S2 __bss_start,B5
21 MVKH .S2 __bss_start,B5
22 MVKL .S2 __bss_stop,B6
23 MVKH .S2 __bss_stop,B6
24 SUB .L2 B6,B5,B6 ; bss size
25
26 ;; Set the stack pointer
27 MVKL .S2 current_ksp,B0
28 MVKH .S2 current_ksp,B0
29 LDW .D2T2 *B0,B15
30
31 ;; clear bss
32 SHR .S2 B6,3,B0 ; number of dwords to clear
33 ZERO .L2 B13
34 ZERO .L2 B12
35bss_loop:
36 BDEC .S2 bss_loop,B0
37 NOP 3
38 CMPLT .L2 B0,0,B1
39 [!B1] STDW .D2T2 B13:B12,*B5++[1]
40
41 NOP 4
42 AND .D2 ~7,B15,B15
43
44 ;; Clear GIE and PGIE
45 MVC .S2 CSR,B2
46 CLR .S2 B2,0,1,B2
47 MVC .S2 B2,CSR
48 MVC .S2 TSR,B2
49 CLR .S2 B2,0,1,B2
50 MVC .S2 B2,TSR
51 MVC .S2 ITSR,B2
52 CLR .S2 B2,0,1,B2
53 MVC .S2 B2,ITSR
54 MVC .S2 NTSR,B2
55 CLR .S2 B2,0,1,B2
56 MVC .S2 B2,NTSR
57
58 ;; pass DTB pointer to machine_init (or zero if none)
59 MVKL .S1 OF_DT_HEADER,A0
60 MVKH .S1 OF_DT_HEADER,A0
61 CMPEQ .L1 A10,A0,A0
62 [A0] MV .S1X B10,A4
63 [!A0] MVK .S1 0,A4
64
65#ifdef CONFIG_C6X_BIG_KERNEL
66 MVKL .S1 machine_init,A0
67 MVKH .S1 machine_init,A0
68 B .S2X A0
69 ADDKPC .S2 0f,B3,4
700:
71#else
72 CALLP .S2 machine_init,B3
73#endif
74
75 ;; Jump to Linux init
76#ifdef CONFIG_C6X_BIG_KERNEL
77 MVKL .S1 start_kernel,A0
78 MVKH .S1 start_kernel,A0
79 B .S2X A0
80#else
81 B .S2 start_kernel
82#endif
83 NOP 5
84L1: BNOP .S2 L1,5
diff --git a/arch/c6x/kernel/setup.c b/arch/c6x/kernel/setup.c
new file mode 100644
index 000000000000..3c2858f0d91e
--- /dev/null
+++ b/arch/c6x/kernel/setup.c
@@ -0,0 +1,498 @@
1/*
2 * Port on Texas Instruments TMS320C6x architecture
3 *
4 * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated
5 * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.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/dma-mapping.h>
12#include <linux/memblock.h>
13#include <linux/seq_file.h>
14#include <linux/bootmem.h>
15#include <linux/clkdev.h>
16#include <linux/initrd.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of_fdt.h>
20#include <linux/string.h>
21#include <linux/errno.h>
22#include <linux/cache.h>
23#include <linux/delay.h>
24#include <linux/sched.h>
25#include <linux/clk.h>
26#include <linux/fs.h>
27#include <linux/of.h>
28
29
30#include <asm/sections.h>
31#include <asm/div64.h>
32#include <asm/setup.h>
33#include <asm/dscr.h>
34#include <asm/clock.h>
35#include <asm/soc.h>
36
37static const char *c6x_soc_name;
38
39int c6x_num_cores;
40EXPORT_SYMBOL_GPL(c6x_num_cores);
41
42unsigned int c6x_silicon_rev;
43EXPORT_SYMBOL_GPL(c6x_silicon_rev);
44
45/*
46 * Device status register. This holds information
47 * about device configuration needed by some drivers.
48 */
49unsigned int c6x_devstat;
50EXPORT_SYMBOL_GPL(c6x_devstat);
51
52/*
53 * Some SoCs have fuse registers holding a unique MAC
54 * address. This is parsed out of the device tree with
55 * the resulting MAC being held here.
56 */
57unsigned char c6x_fuse_mac[6];
58
59unsigned long memory_start;
60unsigned long memory_end;
61
62unsigned long ram_start;
63unsigned long ram_end;
64
65/* Uncached memory for DMA consistent use (memdma=) */
66static unsigned long dma_start __initdata;
67static unsigned long dma_size __initdata;
68
69char c6x_command_line[COMMAND_LINE_SIZE];
70
71#if defined(CONFIG_CMDLINE_BOOL)
72static const char default_command_line[COMMAND_LINE_SIZE] __section(.cmdline) =
73 CONFIG_CMDLINE;
74#endif
75
76struct cpuinfo_c6x {
77 const char *cpu_name;
78 const char *cpu_voltage;
79 const char *mmu;
80 const char *fpu;
81 char *cpu_rev;
82 unsigned int core_id;
83 char __cpu_rev[5];
84};
85
86static DEFINE_PER_CPU(struct cpuinfo_c6x, cpu_data);
87
88unsigned int ticks_per_ns_scaled;
89EXPORT_SYMBOL(ticks_per_ns_scaled);
90
91unsigned int c6x_core_freq;
92
93static void __init get_cpuinfo(void)
94{
95 unsigned cpu_id, rev_id, csr;
96 struct clk *coreclk = clk_get_sys(NULL, "core");
97 unsigned long core_khz;
98 u64 tmp;
99 struct cpuinfo_c6x *p;
100 struct device_node *node, *np;
101
102 p = &per_cpu(cpu_data, smp_processor_id());
103
104 if (!IS_ERR(coreclk))
105 c6x_core_freq = clk_get_rate(coreclk);
106 else {
107 printk(KERN_WARNING
108 "Cannot find core clock frequency. Using 700MHz\n");
109 c6x_core_freq = 700000000;
110 }
111
112 core_khz = c6x_core_freq / 1000;
113
114 tmp = (uint64_t)core_khz << C6X_NDELAY_SCALE;
115 do_div(tmp, 1000000);
116 ticks_per_ns_scaled = tmp;
117
118 csr = get_creg(CSR);
119 cpu_id = csr >> 24;
120 rev_id = (csr >> 16) & 0xff;
121
122 p->mmu = "none";
123 p->fpu = "none";
124 p->cpu_voltage = "unknown";
125
126 switch (cpu_id) {
127 case 0:
128 p->cpu_name = "C67x";
129 p->fpu = "yes";
130 break;
131 case 2:
132 p->cpu_name = "C62x";
133 break;
134 case 8:
135 p->cpu_name = "C64x";
136 break;
137 case 12:
138 p->cpu_name = "C64x";
139 break;
140 case 16:
141 p->cpu_name = "C64x+";
142 p->cpu_voltage = "1.2";
143 break;
144 default:
145 p->cpu_name = "unknown";
146 break;
147 }
148
149 if (cpu_id < 16) {
150 switch (rev_id) {
151 case 0x1:
152 if (cpu_id > 8) {
153 p->cpu_rev = "DM640/DM641/DM642/DM643";
154 p->cpu_voltage = "1.2 - 1.4";
155 } else {
156 p->cpu_rev = "C6201";
157 p->cpu_voltage = "2.5";
158 }
159 break;
160 case 0x2:
161 p->cpu_rev = "C6201B/C6202/C6211";
162 p->cpu_voltage = "1.8";
163 break;
164 case 0x3:
165 p->cpu_rev = "C6202B/C6203/C6204/C6205";
166 p->cpu_voltage = "1.5";
167 break;
168 case 0x201:
169 p->cpu_rev = "C6701 revision 0 (early CPU)";
170 p->cpu_voltage = "1.8";
171 break;
172 case 0x202:
173 p->cpu_rev = "C6701/C6711/C6712";
174 p->cpu_voltage = "1.8";
175 break;
176 case 0x801:
177 p->cpu_rev = "C64x";
178 p->cpu_voltage = "1.5";
179 break;
180 default:
181 p->cpu_rev = "unknown";
182 }
183 } else {
184 p->cpu_rev = p->__cpu_rev;
185 snprintf(p->__cpu_rev, sizeof(p->__cpu_rev), "0x%x", cpu_id);
186 }
187
188 p->core_id = get_coreid();
189
190 node = of_find_node_by_name(NULL, "cpus");
191 if (node) {
192 for_each_child_of_node(node, np)
193 if (!strcmp("cpu", np->name))
194 ++c6x_num_cores;
195 of_node_put(node);
196 }
197
198 node = of_find_node_by_name(NULL, "soc");
199 if (node) {
200 if (of_property_read_string(node, "model", &c6x_soc_name))
201 c6x_soc_name = "unknown";
202 of_node_put(node);
203 } else
204 c6x_soc_name = "unknown";
205
206 printk(KERN_INFO "CPU%d: %s rev %s, %s volts, %uMHz\n",
207 p->core_id, p->cpu_name, p->cpu_rev,
208 p->cpu_voltage, c6x_core_freq / 1000000);
209}
210
211/*
212 * Early parsing of the command line
213 */
214static u32 mem_size __initdata;
215
216/* "mem=" parsing. */
217static int __init early_mem(char *p)
218{
219 if (!p)
220 return -EINVAL;
221
222 mem_size = memparse(p, &p);
223 /* don't remove all of memory when handling "mem={invalid}" */
224 if (mem_size == 0)
225 return -EINVAL;
226
227 return 0;
228}
229early_param("mem", early_mem);
230
231/* "memdma=<size>[@<address>]" parsing. */
232static int __init early_memdma(char *p)
233{
234 if (!p)
235 return -EINVAL;
236
237 dma_size = memparse(p, &p);
238 if (*p == '@')
239 dma_start = memparse(p, &p);
240
241 return 0;
242}
243early_param("memdma", early_memdma);
244
245int __init c6x_add_memory(phys_addr_t start, unsigned long size)
246{
247 static int ram_found __initdata;
248
249 /* We only handle one bank (the one with PAGE_OFFSET) for now */
250 if (ram_found)
251 return -EINVAL;
252
253 if (start > PAGE_OFFSET || PAGE_OFFSET >= (start + size))
254 return 0;
255
256 ram_start = start;
257 ram_end = start + size;
258
259 ram_found = 1;
260 return 0;
261}
262
263/*
264 * Do early machine setup and device tree parsing. This is called very
265 * early on the boot process.
266 */
267notrace void __init machine_init(unsigned long dt_ptr)
268{
269 struct boot_param_header *dtb = __va(dt_ptr);
270 struct boot_param_header *fdt = (struct boot_param_header *)_fdt_start;
271
272 /* interrupts must be masked */
273 set_creg(IER, 2);
274
275 /*
276 * Set the Interrupt Service Table (IST) to the beginning of the
277 * vector table.
278 */
279 set_ist(_vectors_start);
280
281 lockdep_init();
282
283 /*
284 * dtb is passed in from bootloader.
285 * fdt is linked in blob.
286 */
287 if (dtb && dtb != fdt)
288 fdt = dtb;
289
290 /* Do some early initialization based on the flat device tree */
291 early_init_devtree(fdt);
292
293 /* parse_early_param needs a boot_command_line */
294 strlcpy(boot_command_line, c6x_command_line, COMMAND_LINE_SIZE);
295 parse_early_param();
296}
297
298void __init setup_arch(char **cmdline_p)
299{
300 int bootmap_size;
301 struct memblock_region *reg;
302
303 printk(KERN_INFO "Initializing kernel\n");
304
305 /* Initialize command line */
306 *cmdline_p = c6x_command_line;
307
308 memblock_init();
309
310 memory_end = ram_end;
311 memory_end &= ~(PAGE_SIZE - 1);
312
313 if (mem_size && (PAGE_OFFSET + PAGE_ALIGN(mem_size)) < memory_end)
314 memory_end = PAGE_OFFSET + PAGE_ALIGN(mem_size);
315
316 /* add block that this kernel can use */
317 memblock_add(PAGE_OFFSET, memory_end - PAGE_OFFSET);
318
319 /* reserve kernel text/data/bss */
320 memblock_reserve(PAGE_OFFSET,
321 PAGE_ALIGN((unsigned long)&_end - PAGE_OFFSET));
322
323 if (dma_size) {
324 /* align to cacheability granularity */
325 dma_size = CACHE_REGION_END(dma_size);
326
327 if (!dma_start)
328 dma_start = memory_end - dma_size;
329
330 /* align to cacheability granularity */
331 dma_start = CACHE_REGION_START(dma_start);
332
333 /* reserve DMA memory taken from kernel memory */
334 if (memblock_is_region_memory(dma_start, dma_size))
335 memblock_reserve(dma_start, dma_size);
336 }
337
338 memory_start = PAGE_ALIGN((unsigned int) &_end);
339
340 printk(KERN_INFO "Memory Start=%08lx, Memory End=%08lx\n",
341 memory_start, memory_end);
342
343#ifdef CONFIG_BLK_DEV_INITRD
344 /*
345 * Reserve initrd memory if in kernel memory.
346 */
347 if (initrd_start < initrd_end)
348 if (memblock_is_region_memory(initrd_start,
349 initrd_end - initrd_start))
350 memblock_reserve(initrd_start,
351 initrd_end - initrd_start);
352#endif
353
354 init_mm.start_code = (unsigned long) &_stext;
355 init_mm.end_code = (unsigned long) &_etext;
356 init_mm.end_data = memory_start;
357 init_mm.brk = memory_start;
358
359 /*
360 * Give all the memory to the bootmap allocator, tell it to put the
361 * boot mem_map at the start of memory
362 */
363 bootmap_size = init_bootmem_node(NODE_DATA(0),
364 memory_start >> PAGE_SHIFT,
365 PAGE_OFFSET >> PAGE_SHIFT,
366 memory_end >> PAGE_SHIFT);
367 memblock_reserve(memory_start, bootmap_size);
368
369 memblock_analyze();
370 unflatten_device_tree();
371
372 c6x_cache_init();
373
374 /* Set the whole external memory as non-cacheable */
375 disable_caching(ram_start, ram_end - 1);
376
377 /* Set caching of external RAM used by Linux */
378 for_each_memblock(memory, reg)
379 enable_caching(CACHE_REGION_START(reg->base),
380 CACHE_REGION_START(reg->base + reg->size - 1));
381
382#ifdef CONFIG_BLK_DEV_INITRD
383 /*
384 * Enable caching for initrd which falls outside kernel memory.
385 */
386 if (initrd_start < initrd_end) {
387 if (!memblock_is_region_memory(initrd_start,
388 initrd_end - initrd_start))
389 enable_caching(CACHE_REGION_START(initrd_start),
390 CACHE_REGION_START(initrd_end - 1));
391 }
392#endif
393
394 /*
395 * Disable caching for dma coherent memory taken from kernel memory.
396 */
397 if (dma_size && memblock_is_region_memory(dma_start, dma_size))
398 disable_caching(dma_start,
399 CACHE_REGION_START(dma_start + dma_size - 1));
400
401 /* Initialize the coherent memory allocator */
402 coherent_mem_init(dma_start, dma_size);
403
404 /*
405 * Free all memory as a starting point.
406 */
407 free_bootmem(PAGE_OFFSET, memory_end - PAGE_OFFSET);
408
409 /*
410 * Then reserve memory which is already being used.
411 */
412 for_each_memblock(reserved, reg) {
413 pr_debug("reserved - 0x%08x-0x%08x\n",
414 (u32) reg->base, (u32) reg->size);
415 reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT);
416 }
417
418 max_low_pfn = PFN_DOWN(memory_end);
419 min_low_pfn = PFN_UP(memory_start);
420 max_mapnr = max_low_pfn - min_low_pfn;
421
422 /* Get kmalloc into gear */
423 paging_init();
424
425 /*
426 * Probe for Device State Configuration Registers.
427 * We have to do this early in case timer needs to be enabled
428 * through DSCR.
429 */
430 dscr_probe();
431
432 /* We do this early for timer and core clock frequency */
433 c64x_setup_clocks();
434
435 /* Get CPU info */
436 get_cpuinfo();
437
438#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
439 conswitchp = &dummy_con;
440#endif
441}
442
443#define cpu_to_ptr(n) ((void *)((long)(n)+1))
444#define ptr_to_cpu(p) ((long)(p) - 1)
445
446static int show_cpuinfo(struct seq_file *m, void *v)
447{
448 int n = ptr_to_cpu(v);
449 struct cpuinfo_c6x *p = &per_cpu(cpu_data, n);
450
451 if (n == 0) {
452 seq_printf(m,
453 "soc\t\t: %s\n"
454 "soc revision\t: 0x%x\n"
455 "soc cores\t: %d\n",
456 c6x_soc_name, c6x_silicon_rev, c6x_num_cores);
457 }
458
459 seq_printf(m,
460 "\n"
461 "processor\t: %d\n"
462 "cpu\t\t: %s\n"
463 "core revision\t: %s\n"
464 "core voltage\t: %s\n"
465 "core id\t\t: %d\n"
466 "mmu\t\t: %s\n"
467 "fpu\t\t: %s\n"
468 "cpu MHz\t\t: %u\n"
469 "bogomips\t: %lu.%02lu\n\n",
470 n,
471 p->cpu_name, p->cpu_rev, p->cpu_voltage,
472 p->core_id, p->mmu, p->fpu,
473 (c6x_core_freq + 500000) / 1000000,
474 (loops_per_jiffy/(500000/HZ)),
475 (loops_per_jiffy/(5000/HZ))%100);
476
477 return 0;
478}
479
480static void *c_start(struct seq_file *m, loff_t *pos)
481{
482 return *pos < nr_cpu_ids ? cpu_to_ptr(*pos) : NULL;
483}
484static void *c_next(struct seq_file *m, void *v, loff_t *pos)
485{
486 ++*pos;
487 return NULL;
488}
489static void c_stop(struct seq_file *m, void *v)
490{
491}
492
493const struct seq_operations cpuinfo_op = {
494 c_start,
495 c_stop,
496 c_next,
497 show_cpuinfo
498};
diff --git a/arch/c6x/kernel/vectors.S b/arch/c6x/kernel/vectors.S
new file mode 100644
index 000000000000..c95c66fc71e8
--- /dev/null
+++ b/arch/c6x/kernel/vectors.S
@@ -0,0 +1,81 @@
1;
2; Port on Texas Instruments TMS320C6x architecture
3;
4; Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated
5; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.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; This section handles all the interrupt vector routines.
12; At RESET the processor sets up the DRAM timing parameters and
13; branches to the label _c_int00 which handles initialization for the C code.
14;
15
16#define ALIGNMENT 5
17
18 .macro IRQVEC name, handler
19 .align ALIGNMENT
20 .hidden \name
21 .global \name
22\name:
23#ifdef CONFIG_C6X_BIG_KERNEL
24 STW .D2T1 A0,*B15--[2]
25 || MVKL .S1 \handler,A0
26 MVKH .S1 \handler,A0
27 B .S2X A0
28 LDW .D2T1 *++B15[2],A0
29 NOP 4
30 NOP
31 NOP
32 .endm
33#else /* CONFIG_C6X_BIG_KERNEL */
34 B .S2 \handler
35 NOP
36 NOP
37 NOP
38 NOP
39 NOP
40 NOP
41 NOP
42 .endm
43#endif /* CONFIG_C6X_BIG_KERNEL */
44
45 .sect ".vectors","ax"
46 .align ALIGNMENT
47 .global RESET
48 .hidden RESET
49RESET:
50#ifdef CONFIG_C6X_BIG_KERNEL
51 MVKL .S1 _c_int00,A0 ; branch to _c_int00
52 MVKH .S1 _c_int00,A0
53 B .S2X A0
54#else
55 B .S2 _c_int00
56 NOP
57 NOP
58#endif
59 NOP
60 NOP
61 NOP
62 NOP
63 NOP
64
65
66 IRQVEC NMI,_nmi_handler ; NMI interrupt
67 IRQVEC AINT,_bad_interrupt ; reserved
68 IRQVEC MSGINT,_bad_interrupt ; reserved
69
70 IRQVEC INT4,_int4_handler
71 IRQVEC INT5,_int5_handler
72 IRQVEC INT6,_int6_handler
73 IRQVEC INT7,_int7_handler
74 IRQVEC INT8,_int8_handler
75 IRQVEC INT9,_int9_handler
76 IRQVEC INT10,_int10_handler
77 IRQVEC INT11,_int11_handler
78 IRQVEC INT12,_int12_handler
79 IRQVEC INT13,_int13_handler
80 IRQVEC INT14,_int14_handler
81 IRQVEC INT15,_int15_handler