• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/arm/plat-brcm/
1/* Modified by Broadcom Corp. Portions Copyright (c) Broadcom Corp, 2012. */
2/*
3 *  Copyright (C) 2002 ARM Ltd.
4 *  All Rights Reserved
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 *
10 *  based on linux/arch/arm/mach-realview/platsmp.c
11 */
12#ifdef	CONFIG_SMP
13#include <linux/init.h>
14#include <linux/errno.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/jiffies.h>
18#include <linux/smp.h>
19#include <linux/io.h>
20
21#include <asm/cacheflush.h>
22#include <asm/mach-types.h>
23#include <asm/localtimer.h>
24#include <asm/unified.h>
25#include <asm/smp.h>
26#include <asm/smp_scu.h>
27#include <asm/cacheflush.h>
28
29#include <plat/mpcore.h>
30#include <mach/hardware.h>
31#include <mach/smp.h>
32
33/*
34 * control for which core is the next to come out of the secondary
35 * boot "holding pen"
36 */
37volatile int __cpuinitdata pen_release = -1;
38
39static inline unsigned int get_core_count(void)
40{
41	void __iomem *scu_base = scu_base_addr();
42	if (scu_base)
43		return scu_get_core_count(scu_base);
44	return 1;
45}
46
47static DEFINE_SPINLOCK(boot_lock);
48
49void __cpuinit platform_secondary_init(unsigned int cpu)
50{
51	trace_hardirqs_off();
52
53	/*
54	 * if any interrupts are already enabled for the primary
55	 * core (e.g. timer irq), then they will not have been enabled
56	 * for us: do so
57	 */
58	mpcore_cpu_init();
59
60	/*
61	 * let the primary processor know we're out of the
62	 * pen, then head off into the C entry point
63	 */
64	pen_release = -1;
65	smp_wmb();
66	clean_dcache_area((void *)  &pen_release, sizeof(pen_release));
67
68	/*
69	 * Synchronise with the boot thread.
70	 */
71	spin_lock(&boot_lock);
72	spin_unlock(&boot_lock);
73}
74
75int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
76{
77	unsigned long timeout;
78
79	/*
80	 * set synchronisation state between this boot processor
81	 * and the secondary one
82	 */
83	spin_lock(&boot_lock);
84
85	/*
86	 * The secondary processor is waiting to be released from
87	 * the holding pen - release it, then wait for it to flag
88	 * that it has been released by resetting pen_release.
89	 *
90	 * Note that "pen_release" is the hardware CPU ID, whereas
91	 * "cpu" is Linux's internal ID.
92	 */
93	pen_release = cpu;
94	smp_wmb();
95
96	clean_dcache_area((void *)  &pen_release, sizeof(pen_release));
97	outer_clean_range(__pa(&pen_release), __pa(&pen_release +
98			sizeof(pen_release)));
99
100	dsb_sev();
101
102	/*
103	 * Timeout set on purpose in jiffies so that on slow processors
104	 * that must also have low HZ it will wait longer.
105	 */
106	timeout = jiffies + 128;
107
108	udelay(100);
109
110	/*
111	 * If the secondary CPU was waiting on WFE, it should
112	 * be already watching <pen_release>, or it could be
113	 * waiting in WFI, send it an IPI to be sure it wakes.
114	 */
115
116	if( pen_release != -1 )
117		{
118		smp_cross_call(cpumask_of(cpu));
119		}
120
121	while (time_before(jiffies, timeout)) {
122		smp_rmb();
123		if (pen_release == -1)
124			break;
125
126		udelay(10);
127	}
128
129	if (arch_is_coherent()) {
130		outer_cache.inv_range = NULL;
131		outer_cache.clean_range = NULL;
132		outer_cache.flush_range = NULL;
133		outer_cache.sync = NULL;
134	}
135
136	/*
137	 * now the secondary core is starting up let it run its
138	 * calibrations, then wait for it to finish
139	 */
140	spin_unlock(&boot_lock);
141
142	return pen_release != -1 ? -ENOSYS : 0;
143}
144
145/*
146 * Initialise the CPU possible map early - this describes the CPUs
147 * which may be present or become present in the system.
148 */
149void __init smp_init_cpus(void)
150{
151	unsigned int i, ncores = get_core_count();
152
153	for (i = 0; i < ncores; i++)
154		set_cpu_possible(i, true);
155}
156
157void __init smp_prepare_cpus(unsigned int max_cpus)
158{
159	unsigned int ncores = get_core_count();
160	unsigned int cpu = smp_processor_id();
161	int i;
162
163	/* sanity check */
164	if (ncores == 0) {
165		printk(KERN_ERR
166		       "MPCORE: strange CPU count of 0? Default to 1\n");
167
168		ncores = 1;
169	}
170
171	if (ncores > NR_CPUS) {
172		printk(KERN_WARNING
173		       "MPCORE: no. of cores (%d) greater than configured "
174		       "maximum of %d - clipping\n",
175		       ncores, NR_CPUS);
176		ncores = NR_CPUS;
177	}
178
179	smp_store_cpu_info(cpu);
180
181	/*
182	 * are we trying to boot more cores than exist?
183	 */
184	if (max_cpus > ncores)
185		max_cpus = ncores;
186
187	/*
188	 * Initialise the present map, which describes the set of CPUs
189	 * actually populated at the present time.
190	 */
191	for (i = 0; i < max_cpus; i++)
192		set_cpu_present(i, true);
193
194	/*
195	 * Initialise the SCU if there are more than one CPU and let
196	 * them know where to start. Note that, on modern versions of
197	 * MILO, the "poke" doesn't actually do anything until each
198	 * individual core is sent a soft interrupt to get it out of
199	 * WFI
200	 */
201	if (max_cpus > 1) {
202		/* nobody is to be released from the pen yet */
203		pen_release = -1;
204
205		/*
206		 * Enable the local timer or broadcast device for the
207		 * boot CPU, but only if we have more than one CPU.
208		 */
209		percpu_timer_setup();
210
211		scu_enable(scu_base_addr());
212
213		/* Wakeup other cores in an SoC-specific manner */
214		plat_wake_secondary_cpu( max_cpus, platform_secondary_startup );
215
216	}
217}
218#endif	/* CONFIG_SMP */
219