mp_machdep.c revision 192532
1/*-
2 * Copyright (c) 2008 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/powerpc/powerpc/mp_machdep.c 192532 2009-05-21 11:43:37Z raj $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/ktr.h>
34#include <sys/bus.h>
35#include <sys/pcpu.h>
36#include <sys/proc.h>
37#include <sys/sched.h>
38#include <sys/smp.h>
39
40#include <machine/bus.h>
41#include <machine/cpu.h>
42#include <machine/intr_machdep.h>
43#include <machine/platform.h>
44#include <machine/md_var.h>
45#include <machine/smp.h>
46
47#include "pic_if.h"
48
49extern struct pcpu __pcpu[MAXCPU];
50
51volatile static int ap_awake;
52volatile static u_int ap_letgo;
53volatile static uint32_t ap_decr;
54volatile static u_quad_t ap_timebase;
55static u_int ipi_msg_cnt[32];
56
57void
58machdep_ap_bootstrap(void)
59{
60
61	PCPU_SET(pir, mfspr(SPR_PIR));
62	PCPU_SET(awake, 1);
63	__asm __volatile("msync; isync");
64
65	while (ap_letgo == 0)
66		;
67
68	/* Initialize DEC and TB, sync with the BSP values */
69	decr_ap_init();
70	mttb(ap_timebase);
71	__asm __volatile("mtdec %0" :: "r"(ap_decr));
72
73	atomic_add_int(&ap_awake, 1);
74	CTR1(KTR_SMP, "SMP: AP CPU%d launched", PCPU_GET(cpuid));
75
76	/* Initialize curthread */
77	PCPU_SET(curthread, PCPU_GET(idlethread));
78	PCPU_SET(curpcb, curthread->td_pcb);
79
80	/* Let the DEC and external interrupts go */
81	mtmsr(mfmsr() | PSL_EE);
82	sched_throw(NULL);
83}
84
85struct cpu_group *
86cpu_topo(void)
87{
88
89	return (smp_topo_none());
90}
91
92void
93cpu_mp_setmaxid(void)
94{
95	struct cpuref cpuref;
96	int error;
97
98	mp_ncpus = 0;
99	error = platform_smp_first_cpu(&cpuref);
100	while (!error) {
101		mp_ncpus++;
102		error = platform_smp_next_cpu(&cpuref);
103	}
104	/* Sanity. */
105	if (mp_ncpus == 0)
106		mp_ncpus = 1;
107
108	/*
109	 * Set the largest cpuid we're going to use. This is necessary
110	 * for VM initialization.
111	 */
112	mp_maxid = min(mp_ncpus, MAXCPU) - 1;
113}
114
115int
116cpu_mp_probe(void)
117{
118
119	/*
120	 * We're not going to enable SMP if there's only 1 processor.
121	 */
122	return (mp_ncpus > 1);
123}
124
125void
126cpu_mp_start(void)
127{
128	struct cpuref bsp, cpu;
129	struct pcpu *pc;
130	int error;
131
132	error = platform_smp_get_bsp(&bsp);
133	KASSERT(error == 0, ("Don't know BSP"));
134	KASSERT(bsp.cr_cpuid == 0, ("%s: cpuid != 0", __func__));
135
136	error = platform_smp_first_cpu(&cpu);
137	while (!error) {
138		if (cpu.cr_cpuid >= MAXCPU) {
139			printf("SMP: cpu%d: skipped -- ID out of range\n",
140			    cpu.cr_cpuid);
141			goto next;
142		}
143		if (all_cpus & (1 << cpu.cr_cpuid)) {
144			printf("SMP: cpu%d: skipped - duplicate ID\n",
145			    cpu.cr_cpuid);
146			goto next;
147		}
148		if (cpu.cr_cpuid != bsp.cr_cpuid) {
149			pc = &__pcpu[cpu.cr_cpuid];
150			pcpu_init(pc, cpu.cr_cpuid, sizeof(*pc));
151		} else {
152			pc = pcpup;
153			pc->pc_cpuid = bsp.cr_cpuid;
154			pc->pc_bsp = 1;
155		}
156		pc->pc_cpumask = 1 << pc->pc_cpuid;
157		pc->pc_hwref = cpu.cr_hwref;
158		all_cpus |= pc->pc_cpumask;
159next:
160		error = platform_smp_next_cpu(&cpu);
161	}
162}
163
164void
165cpu_mp_announce(void)
166{
167	struct pcpu *pc;
168	int i;
169
170	for (i = 0; i <= mp_maxid; i++) {
171		pc = pcpu_find(i);
172		if (pc == NULL)
173			continue;
174		printf("cpu%d: dev=%x", i, pc->pc_hwref);
175		if (pc->pc_bsp)
176			printf(" (BSP)");
177		printf("\n");
178	}
179}
180
181static void
182cpu_mp_unleash(void *dummy)
183{
184	struct pcpu *pc;
185	int cpus, timeout;
186
187	if (mp_ncpus <= 1)
188		return;
189
190	cpus = 0;
191	smp_cpus = 0;
192	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
193		cpus++;
194		pc->pc_other_cpus = all_cpus & ~pc->pc_cpumask;
195		if (!pc->pc_bsp) {
196			if (bootverbose)
197				printf("Waking up CPU %d (dev=%x)\n",
198				    pc->pc_cpuid, pc->pc_hwref);
199
200			platform_smp_start_cpu(pc);
201
202			timeout = 2000;	/* wait 2sec for the AP */
203			while (!pc->pc_awake && --timeout > 0)
204				DELAY(1000);
205
206		} else {
207			PCPU_SET(pir, mfspr(SPR_PIR));
208			pc->pc_awake = 1;
209		}
210		if (pc->pc_awake) {
211			if (bootverbose)
212				printf("Adding CPU %d, pir=%x, awake=%x\n",
213				    pc->pc_cpuid, pc->pc_pir, pc->pc_awake);
214			smp_cpus++;
215		} else
216			stopped_cpus |= (1 << pc->pc_cpuid);
217	}
218
219	ap_awake = 1;
220
221	/* Provide our current DEC and TB values for APs */
222	__asm __volatile("mfdec %0" : "=r"(ap_decr));
223	ap_timebase = mftb() + 10;
224	__asm __volatile("msync; isync");
225
226	/* Let APs continue */
227	atomic_store_rel_int(&ap_letgo, 1);
228
229	mttb(ap_timebase);
230
231	while (ap_awake < smp_cpus)
232		;
233
234	if (smp_cpus != cpus || cpus != mp_ncpus) {
235		printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n",
236		    mp_ncpus, cpus, smp_cpus);
237	}
238
239	smp_active = 1;
240	smp_started = 1;
241}
242
243SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL);
244
245int
246powerpc_ipi_handler(void *arg)
247{
248	cpumask_t self;
249	uint32_t ipimask;
250	int msg;
251
252	CTR2(KTR_SMP, "%s: MSR 0x%08x", __func__, mfmsr());
253
254	ipimask = atomic_readandclear_32(&(pcpup->pc_ipimask));
255	if (ipimask == 0)
256		return (FILTER_STRAY);
257	while ((msg = ffs(ipimask) - 1) != -1) {
258		ipimask &= ~(1u << msg);
259		ipi_msg_cnt[msg]++;
260		switch (msg) {
261		case IPI_AST:
262			CTR1(KTR_SMP, "%s: IPI_AST", __func__);
263			break;
264		case IPI_PREEMPT:
265			CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__);
266			sched_preempt(curthread);
267			break;
268		case IPI_RENDEZVOUS:
269			CTR1(KTR_SMP, "%s: IPI_RENDEZVOUS", __func__);
270			smp_rendezvous_action();
271			break;
272		case IPI_STOP:
273			CTR1(KTR_SMP, "%s: IPI_STOP (stop)", __func__);
274			self = PCPU_GET(cpumask);
275			savectx(PCPU_GET(curpcb));
276			atomic_set_int(&stopped_cpus, self);
277			while ((started_cpus & self) == 0)
278				cpu_spinwait();
279			atomic_clear_int(&started_cpus, self);
280			atomic_clear_int(&stopped_cpus, self);
281			CTR1(KTR_SMP, "%s: IPI_STOP (restart)", __func__);
282			break;
283		}
284	}
285
286	return (FILTER_HANDLED);
287}
288
289static void
290ipi_send(struct pcpu *pc, int ipi)
291{
292
293	CTR4(KTR_SMP, "%s: pc=%p, targetcpu=%d, IPI=%d", __func__,
294	    pc, pc->pc_cpuid, ipi);
295
296	atomic_set_32(&pc->pc_ipimask, (1 << ipi));
297	PIC_IPI(pic, pc->pc_cpuid);
298
299	CTR1(KTR_SMP, "%s: sent", __func__);
300}
301
302/* Send an IPI to a set of cpus. */
303void
304ipi_selected(cpumask_t cpus, int ipi)
305{
306	struct pcpu *pc;
307
308	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
309		if (cpus & pc->pc_cpumask)
310			ipi_send(pc, ipi);
311	}
312}
313
314/* Send an IPI to all CPUs EXCEPT myself. */
315void
316ipi_all_but_self(int ipi)
317{
318	struct pcpu *pc;
319
320	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
321		if (pc != pcpup)
322			ipi_send(pc, ipi);
323	}
324}
325