xref: /linux/arch/arm/mach-omap2/omap-smp.c (revision cc4589ebfae6f8dbb5cf880a0a67eedab3416492)
1 /*
2  * OMAP4 SMP source file. It contains platform specific fucntions
3  * needed for the linux smp kernel.
4  *
5  * Copyright (C) 2009 Texas Instruments, Inc.
6  *
7  * Author:
8  *      Santosh Shilimkar <santosh.shilimkar@ti.com>
9  *
10  * Platform file needed for the OMAP4 SMP. This file is based on arm
11  * realview smp platform.
12  * * Copyright (c) 2002 ARM Limited.
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17  */
18 #include <linux/init.h>
19 #include <linux/device.h>
20 #include <linux/smp.h>
21 #include <linux/io.h>
22 
23 #include <asm/cacheflush.h>
24 #include <asm/localtimer.h>
25 #include <asm/smp_scu.h>
26 #include <mach/hardware.h>
27 #include <mach/omap4-common.h>
28 
29 /* SCU base address */
30 static void __iomem *scu_base;
31 
32 /*
33  * Use SCU config register to count number of cores
34  */
35 static inline unsigned int get_core_count(void)
36 {
37 	if (scu_base)
38 		return scu_get_core_count(scu_base);
39 	return 1;
40 }
41 
42 static DEFINE_SPINLOCK(boot_lock);
43 
44 void __cpuinit platform_secondary_init(unsigned int cpu)
45 {
46 	trace_hardirqs_off();
47 
48 	/*
49 	 * If any interrupts are already enabled for the primary
50 	 * core (e.g. timer irq), then they will not have been enabled
51 	 * for us: do so
52 	 */
53 	gic_cpu_init(0, gic_cpu_base_addr);
54 
55 	/*
56 	 * Synchronise with the boot thread.
57 	 */
58 	spin_lock(&boot_lock);
59 	spin_unlock(&boot_lock);
60 }
61 
62 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
63 {
64 	/*
65 	 * Set synchronisation state between this boot processor
66 	 * and the secondary one
67 	 */
68 	spin_lock(&boot_lock);
69 
70 	/*
71 	 * Update the AuxCoreBoot0 with boot state for secondary core.
72 	 * omap_secondary_startup() routine will hold the secondary core till
73 	 * the AuxCoreBoot1 register is updated with cpu state
74 	 * A barrier is added to ensure that write buffer is drained
75 	 */
76 	omap_modify_auxcoreboot0(0x200, 0xfffffdff);
77 	flush_cache_all();
78 	smp_wmb();
79 	smp_cross_call(cpumask_of(cpu));
80 
81 	/*
82 	 * Now the secondary core is starting up let it run its
83 	 * calibrations, then wait for it to finish
84 	 */
85 	spin_unlock(&boot_lock);
86 
87 	return 0;
88 }
89 
90 static void __init wakeup_secondary(void)
91 {
92 	/*
93 	 * Write the address of secondary startup routine into the
94 	 * AuxCoreBoot1 where ROM code will jump and start executing
95 	 * on secondary core once out of WFE
96 	 * A barrier is added to ensure that write buffer is drained
97 	 */
98 	omap_auxcoreboot_addr(virt_to_phys(omap_secondary_startup));
99 	smp_wmb();
100 
101 	/*
102 	 * Send a 'sev' to wake the secondary core from WFE.
103 	 * Drain the outstanding writes to memory
104 	 */
105 	dsb();
106 	set_event();
107 	mb();
108 }
109 
110 /*
111  * Initialise the CPU possible map early - this describes the CPUs
112  * which may be present or become present in the system.
113  */
114 void __init smp_init_cpus(void)
115 {
116 	unsigned int i, ncores;
117 
118 	/* Never released */
119 	scu_base = ioremap(OMAP44XX_SCU_BASE, SZ_256);
120 	BUG_ON(!scu_base);
121 
122 	ncores = get_core_count();
123 
124 	for (i = 0; i < ncores; i++)
125 		set_cpu_possible(i, true);
126 }
127 
128 void __init smp_prepare_cpus(unsigned int max_cpus)
129 {
130 	unsigned int ncores = get_core_count();
131 	unsigned int cpu = smp_processor_id();
132 	int i;
133 
134 	/* sanity check */
135 	if (ncores == 0) {
136 		printk(KERN_ERR
137 		       "OMAP4: strange core count of 0? Default to 1\n");
138 		ncores = 1;
139 	}
140 
141 	if (ncores > NR_CPUS) {
142 		printk(KERN_WARNING
143 		       "OMAP4: no. of cores (%d) greater than configured "
144 		       "maximum of %d - clipping\n",
145 		       ncores, NR_CPUS);
146 		ncores = NR_CPUS;
147 	}
148 	smp_store_cpu_info(cpu);
149 
150 	/*
151 	 * are we trying to boot more cores than exist?
152 	 */
153 	if (max_cpus > ncores)
154 		max_cpus = ncores;
155 
156 	/*
157 	 * Initialise the present map, which describes the set of CPUs
158 	 * actually populated at the present time.
159 	 */
160 	for (i = 0; i < max_cpus; i++)
161 		set_cpu_present(i, true);
162 
163 	if (max_cpus > 1) {
164 		/*
165 		 * Enable the local timer or broadcast device for the
166 		 * boot CPU, but only if we have more than one CPU.
167 		 */
168 		percpu_timer_setup();
169 
170 		/*
171 		 * Initialise the SCU and wake up the secondary core using
172 		 * wakeup_secondary().
173 		 */
174 		scu_enable(scu_base);
175 		wakeup_secondary();
176 	}
177 }
178