xref: /freebsd/sys/powerpc/aim/mp_cpudep.c (revision 4ef1c6f75d25ba0c264ab274a1b449467a6ee817)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Marcel Moolenaar
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/bus.h>
36 #include <sys/pcpu.h>
37 #include <sys/proc.h>
38 #include <sys/sched.h>
39 #include <sys/smp.h>
40 
41 #include <machine/bus.h>
42 #include <machine/cpu.h>
43 #include <machine/hid.h>
44 #include <machine/intr_machdep.h>
45 #include <machine/pcb.h>
46 #include <machine/psl.h>
47 #include <machine/smp.h>
48 #include <machine/spr.h>
49 #include <machine/trap.h>
50 
51 #include <dev/ofw/openfirm.h>
52 #include <machine/ofw_machdep.h>
53 
54 void *ap_pcpu;
55 
56 static register_t bsp_state[8] __aligned(8);
57 
58 static void cpudep_save_config(void *dummy);
59 SYSINIT(cpu_save_config, SI_SUB_CPU, SI_ORDER_ANY, cpudep_save_config, NULL);
60 
61 void
62 cpudep_ap_early_bootstrap(void)
63 {
64 #ifndef __powerpc64__
65 	register_t reg;
66 #endif
67 
68 	switch (mfpvr() >> 16) {
69 	case IBM970:
70 	case IBM970FX:
71 	case IBM970MP:
72 		/* Set HIOR to 0 */
73 		__asm __volatile("mtspr 311,%0" :: "r"(0));
74 		powerpc_sync();
75 
76 		/* Restore HID4 and HID5, which are necessary for the MMU */
77 
78 #ifdef __powerpc64__
79 		mtspr(SPR_HID4, bsp_state[2]); powerpc_sync(); isync();
80 		mtspr(SPR_HID5, bsp_state[3]); powerpc_sync(); isync();
81 #else
82 		__asm __volatile("ld %0, 16(%2); sync; isync;	\
83 		    mtspr %1, %0; sync; isync;"
84 		    : "=r"(reg) : "K"(SPR_HID4), "b"(bsp_state));
85 		__asm __volatile("ld %0, 24(%2); sync; isync;	\
86 		    mtspr %1, %0; sync; isync;"
87 		    : "=r"(reg) : "K"(SPR_HID5), "b"(bsp_state));
88 #endif
89 		powerpc_sync();
90 		break;
91 	case IBMPOWER8:
92 	case IBMPOWER8E:
93 	case IBMPOWER8NVL:
94 	case IBMPOWER9:
95 #ifdef __powerpc64__
96 		if (mfmsr() & PSL_HV) {
97 			isync();
98 			/*
99 			 * Direct interrupts to SRR instead of HSRR and
100 			 * reset LPCR otherwise
101 			 */
102 			mtspr(SPR_LPID, 0);
103 			isync();
104 
105 			mtspr(SPR_LPCR, lpcr);
106 			isync();
107 
108 			/*
109 			 * Nuke FSCR, to be managed on a per-process basis
110 			 * later.
111 			 */
112 			mtspr(SPR_FSCR, 0);
113 		}
114 #endif
115 		break;
116 	}
117 
118 	__asm __volatile("mtsprg 0, %0" :: "r"(ap_pcpu));
119 	powerpc_sync();
120 }
121 
122 uintptr_t
123 cpudep_ap_bootstrap(void)
124 {
125 	register_t msr, sp;
126 
127 	msr = psl_kernset & ~PSL_EE;
128 	mtmsr(msr);
129 
130 	pcpup->pc_curthread = pcpup->pc_idlethread;
131 #ifdef __powerpc64__
132 	__asm __volatile("mr 13,%0" :: "r"(pcpup->pc_curthread));
133 #else
134 	__asm __volatile("mr 2,%0" :: "r"(pcpup->pc_curthread));
135 #endif
136 	pcpup->pc_curpcb = pcpup->pc_curthread->td_pcb;
137 	sp = pcpup->pc_curpcb->pcb_sp;
138 	schedinit_ap();
139 
140 	return (sp);
141 }
142 
143 static register_t
144 mpc74xx_l2_enable(register_t l2cr_config)
145 {
146 	register_t ccr, bit;
147 	uint16_t	vers;
148 
149 	vers = mfpvr() >> 16;
150 	switch (vers) {
151 	case MPC7400:
152 	case MPC7410:
153 		bit = L2CR_L2IP;
154 		break;
155 	default:
156 		bit = L2CR_L2I;
157 		break;
158 	}
159 
160 	ccr = mfspr(SPR_L2CR);
161 	if (ccr & L2CR_L2E)
162 		return (ccr);
163 
164 	/* Configure L2 cache. */
165 	ccr = l2cr_config & ~L2CR_L2E;
166 	mtspr(SPR_L2CR, ccr | L2CR_L2I);
167 	do {
168 		ccr = mfspr(SPR_L2CR);
169 	} while (ccr & bit);
170 	powerpc_sync();
171 	mtspr(SPR_L2CR, l2cr_config);
172 	powerpc_sync();
173 
174 	return (l2cr_config);
175 }
176 
177 static register_t
178 mpc745x_l3_enable(register_t l3cr_config)
179 {
180 	register_t ccr;
181 
182 	ccr = mfspr(SPR_L3CR);
183 	if (ccr & L3CR_L3E)
184 		return (ccr);
185 
186 	/* Configure L3 cache. */
187 	ccr = l3cr_config & ~(L3CR_L3E | L3CR_L3I | L3CR_L3PE | L3CR_L3CLKEN);
188 	mtspr(SPR_L3CR, ccr);
189 	ccr |= 0x4000000;       /* Magic, but documented. */
190 	mtspr(SPR_L3CR, ccr);
191 	ccr |= L3CR_L3CLKEN;
192 	mtspr(SPR_L3CR, ccr);
193 	mtspr(SPR_L3CR, ccr | L3CR_L3I);
194 	while (mfspr(SPR_L3CR) & L3CR_L3I)
195 		;
196 	mtspr(SPR_L3CR, ccr & ~L3CR_L3CLKEN);
197 	powerpc_sync();
198 	DELAY(100);
199 	mtspr(SPR_L3CR, ccr);
200 	powerpc_sync();
201 	DELAY(100);
202 	ccr |= L3CR_L3E;
203 	mtspr(SPR_L3CR, ccr);
204 	powerpc_sync();
205 
206 	return(ccr);
207 }
208 
209 static register_t
210 mpc74xx_l1d_enable(void)
211 {
212 	register_t hid;
213 
214 	hid = mfspr(SPR_HID0);
215 	if (hid & HID0_DCE)
216 		return (hid);
217 
218 	/* Enable L1 D-cache */
219 	hid |= HID0_DCE;
220 	powerpc_sync();
221 	mtspr(SPR_HID0, hid | HID0_DCFI);
222 	powerpc_sync();
223 
224 	return (hid);
225 }
226 
227 static register_t
228 mpc74xx_l1i_enable(void)
229 {
230 	register_t hid;
231 
232 	hid = mfspr(SPR_HID0);
233 	if (hid & HID0_ICE)
234 		return (hid);
235 
236 	/* Enable L1 I-cache */
237 	hid |= HID0_ICE;
238 	isync();
239 	mtspr(SPR_HID0, hid | HID0_ICFI);
240 	isync();
241 
242 	return (hid);
243 }
244 
245 static void
246 cpudep_save_config(void *dummy)
247 {
248 	uint16_t	vers;
249 
250 	vers = mfpvr() >> 16;
251 
252 	switch(vers) {
253 	case IBM970:
254 	case IBM970FX:
255 	case IBM970MP:
256 		#ifdef __powerpc64__
257 		bsp_state[0] = mfspr(SPR_HID0);
258 		bsp_state[1] = mfspr(SPR_HID1);
259 		bsp_state[2] = mfspr(SPR_HID4);
260 		bsp_state[3] = mfspr(SPR_HID5);
261 		#else
262 		__asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32"
263 		    : "=r" (bsp_state[0]),"=r" (bsp_state[1]) : "K" (SPR_HID0));
264 		__asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32"
265 		    : "=r" (bsp_state[2]),"=r" (bsp_state[3]) : "K" (SPR_HID1));
266 		__asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32"
267 		    : "=r" (bsp_state[4]),"=r" (bsp_state[5]) : "K" (SPR_HID4));
268 		__asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32"
269 		    : "=r" (bsp_state[6]),"=r" (bsp_state[7]) : "K" (SPR_HID5));
270 		#endif
271 
272 		powerpc_sync();
273 
274 		break;
275 	case IBMCELLBE:
276 		#ifdef NOTYET /* Causes problems if in instruction stream on 970 */
277 		if (mfmsr() & PSL_HV) {
278 			bsp_state[0] = mfspr(SPR_HID0);
279 			bsp_state[1] = mfspr(SPR_HID1);
280 			bsp_state[2] = mfspr(SPR_HID4);
281 			bsp_state[3] = mfspr(SPR_HID6);
282 
283 			bsp_state[4] = mfspr(SPR_CELL_TSCR);
284 		}
285 		#endif
286 
287 		bsp_state[5] = mfspr(SPR_CELL_TSRL);
288 
289 		break;
290 	case MPC7450:
291 	case MPC7455:
292 	case MPC7457:
293 		/* Only MPC745x CPUs have an L3 cache. */
294 		bsp_state[3] = mfspr(SPR_L3CR);
295 
296 		/* Fallthrough */
297 	case MPC7400:
298 	case MPC7410:
299 	case MPC7447A:
300 	case MPC7448:
301 		bsp_state[2] = mfspr(SPR_L2CR);
302 		bsp_state[1] = mfspr(SPR_HID1);
303 		bsp_state[0] = mfspr(SPR_HID0);
304 		break;
305 	}
306 }
307 
308 void
309 cpudep_ap_setup(void)
310 {
311 #ifndef __powerpc64__
312 	register_t	reg;
313 #endif
314 	uint16_t	vers;
315 
316 	vers = mfpvr() >> 16;
317 
318 	switch(vers) {
319 	case IBM970:
320 	case IBM970FX:
321 	case IBM970MP:
322 		/*
323 		 * The 970 has strange rules about how to update HID registers.
324 		 * See Table 2-3, 970MP manual
325 		 *
326 		 * Note: HID4 and HID5 restored already in
327 		 * cpudep_ap_early_bootstrap()
328 		 */
329 
330 		__asm __volatile("mtasr %0; sync" :: "r"(0));
331 	#ifdef __powerpc64__
332 		__asm __volatile(" \
333 			sync; isync;					\
334 			mtspr	%1, %0;					\
335 			mfspr	%0, %1;	mfspr	%0, %1;	mfspr	%0, %1;	\
336 			mfspr	%0, %1;	mfspr	%0, %1;	mfspr	%0, %1; \
337 			sync; isync"
338 		    :: "r"(bsp_state[0]), "K"(SPR_HID0));
339 		__asm __volatile("sync; isync;	\
340 		    mtspr %1, %0; mtspr %1, %0; sync; isync"
341 		    :: "r"(bsp_state[1]), "K"(SPR_HID1));
342 	#else
343 		__asm __volatile(" \
344 			ld	%0,0(%2);				\
345 			sync; isync;					\
346 			mtspr	%1, %0;					\
347 			mfspr	%0, %1;	mfspr	%0, %1;	mfspr	%0, %1;	\
348 			mfspr	%0, %1;	mfspr	%0, %1;	mfspr	%0, %1; \
349 			sync; isync"
350 		    : "=r"(reg) : "K"(SPR_HID0), "b"(bsp_state));
351 		__asm __volatile("ld %0, 8(%2); sync; isync;	\
352 		    mtspr %1, %0; mtspr %1, %0; sync; isync"
353 		    : "=r"(reg) : "K"(SPR_HID1), "b"(bsp_state));
354 	#endif
355 
356 		powerpc_sync();
357 		break;
358 	case IBMCELLBE:
359 		#ifdef NOTYET /* Causes problems if in instruction stream on 970 */
360 		if (mfmsr() & PSL_HV) {
361 			mtspr(SPR_HID0, bsp_state[0]);
362 			mtspr(SPR_HID1, bsp_state[1]);
363 			mtspr(SPR_HID4, bsp_state[2]);
364 			mtspr(SPR_HID6, bsp_state[3]);
365 
366 			mtspr(SPR_CELL_TSCR, bsp_state[4]);
367 		}
368 		#endif
369 
370 		mtspr(SPR_CELL_TSRL, bsp_state[5]);
371 
372 		break;
373 	case MPC7400:
374 	case MPC7410:
375 	case MPC7447A:
376 	case MPC7448:
377 	case MPC7450:
378 	case MPC7455:
379 	case MPC7457:
380 		/* XXX: Program the CPU ID into PIR */
381 		__asm __volatile("mtspr 1023,%0" :: "r"(PCPU_GET(cpuid)));
382 
383 		powerpc_sync();
384 		isync();
385 
386 		mtspr(SPR_HID0, bsp_state[0]); isync();
387 		mtspr(SPR_HID1, bsp_state[1]); isync();
388 
389 		/* Now enable the L3 cache. */
390 		switch (vers) {
391 		case MPC7450:
392 		case MPC7455:
393 		case MPC7457:
394 			/* Only MPC745x CPUs have an L3 cache. */
395 			mpc745x_l3_enable(bsp_state[3]);
396 		default:
397 			break;
398 		}
399 
400 		mpc74xx_l2_enable(bsp_state[2]);
401 		mpc74xx_l1d_enable();
402 		mpc74xx_l1i_enable();
403 
404 		break;
405 	case IBMPOWER7:
406 	case IBMPOWER7PLUS:
407 	case IBMPOWER8:
408 	case IBMPOWER8E:
409 	case IBMPOWER8NVL:
410 	case IBMPOWER9:
411 #ifdef __powerpc64__
412 		if (mfmsr() & PSL_HV) {
413 			mtspr(SPR_LPCR, mfspr(SPR_LPCR) | lpcr |
414 			    LPCR_PECE_WAKESET);
415 			isync();
416 		}
417 #endif
418 		break;
419 	default:
420 #ifdef __powerpc64__
421 		if (!(mfmsr() & PSL_HV)) /* Rely on HV to have set things up */
422 			break;
423 #endif
424 		printf("WARNING: Unknown CPU type. Cache performace may be "
425 		    "suboptimal.\n");
426 		break;
427 	}
428 }
429