1/*-
2 * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause
3 *
4 * Copyright (c) 2001 Matt Thomas.
5 * Copyright (c) 2001 Tsubai Masanari.
6 * Copyright (c) 1998, 1999, 2001 Internet Research Institute, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by
20 *	Internet Research Institute, Inc.
21 * 4. The name of the author may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*-
36 * Copyright (C) 2003 Benno Rice.
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
49 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
50 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
51 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
53 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
54 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
55 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
56 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
57 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 *
59 * from $NetBSD: cpu_subr.c,v 1.1 2003/02/03 17:10:09 matt Exp $
60 */
61
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/bus.h>
65#include <sys/conf.h>
66#include <sys/cpu.h>
67#include <sys/kernel.h>
68#include <sys/ktr.h>
69#include <sys/lock.h>
70#include <sys/proc.h>
71#include <sys/sysctl.h>
72#include <sys/sched.h>
73#include <sys/smp.h>
74#include <sys/endian.h>
75
76#include <machine/bus.h>
77#include <machine/cpu.h>
78#include <machine/hid.h>
79#include <machine/md_var.h>
80#include <machine/smp.h>
81#include <machine/spr.h>
82
83#include <dev/ofw/openfirm.h>
84
85static void	cpu_6xx_setup(int cpuid, uint16_t vers);
86static void	cpu_970_setup(int cpuid, uint16_t vers);
87static void	cpu_booke_setup(int cpuid, uint16_t vers);
88static void	cpu_powerx_setup(int cpuid, uint16_t vers);
89
90int powerpc_pow_enabled;
91void (*cpu_idle_hook)(sbintime_t) = NULL;
92static void	cpu_idle_60x(sbintime_t);
93static void	cpu_idle_booke(sbintime_t);
94#ifdef BOOKE_E500
95static void	cpu_idle_e500mc(sbintime_t sbt);
96#endif
97#if defined(__powerpc64__) && defined(AIM)
98static void	cpu_idle_powerx(sbintime_t);
99static void	cpu_idle_power9(sbintime_t);
100#endif
101
102struct cputab {
103	const char	*name;
104	uint16_t	version;
105	uint16_t	revfmt;
106	int		features;	/* Do not include PPC_FEATURE_32 or
107					 * PPC_FEATURE_HAS_MMU */
108	int		features2;
109	void		(*cpu_setup)(int cpuid, uint16_t vers);
110};
111#define	REVFMT_MAJMIN	1	/* %u.%u */
112#define	REVFMT_HEX	2	/* 0x%04x */
113#define	REVFMT_DEC	3	/* %u */
114static const struct cputab models[] = {
115        { "Motorola PowerPC 601",	MPC601,		REVFMT_DEC,
116	   PPC_FEATURE_HAS_FPU | PPC_FEATURE_UNIFIED_CACHE, 0, cpu_6xx_setup },
117        { "Motorola PowerPC 602",	MPC602,		REVFMT_DEC,
118	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
119        { "Motorola PowerPC 603",	MPC603,		REVFMT_MAJMIN,
120	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
121        { "Motorola PowerPC 603e",	MPC603e,	REVFMT_MAJMIN,
122	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
123        { "Motorola PowerPC 603ev",	MPC603ev,	REVFMT_MAJMIN,
124	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
125        { "Motorola PowerPC 604",	MPC604,		REVFMT_MAJMIN,
126	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
127        { "Motorola PowerPC 604ev",	MPC604ev,	REVFMT_MAJMIN,
128	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
129        { "Motorola PowerPC 620",	MPC620,		REVFMT_HEX,
130	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, 0, NULL },
131        { "Motorola PowerPC 750",	MPC750,		REVFMT_MAJMIN,
132	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
133        { "IBM PowerPC 750FX",		IBM750FX,	REVFMT_MAJMIN,
134	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
135        { "IBM PowerPC 970",		IBM970,		REVFMT_MAJMIN,
136	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU,
137	   0, cpu_970_setup },
138        { "IBM PowerPC 970FX",		IBM970FX,	REVFMT_MAJMIN,
139	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU,
140	   0, cpu_970_setup },
141        { "IBM PowerPC 970GX",		IBM970GX,	REVFMT_MAJMIN,
142	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU,
143	   0, cpu_970_setup },
144        { "IBM PowerPC 970MP",		IBM970MP,	REVFMT_MAJMIN,
145	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU,
146	   0, cpu_970_setup },
147        { "IBM POWER4",		IBMPOWER4,	REVFMT_MAJMIN,
148	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_POWER4, 0, NULL },
149        { "IBM POWER4+",	IBMPOWER4PLUS,	REVFMT_MAJMIN,
150	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_POWER4, 0, NULL },
151        { "IBM POWER5",		IBMPOWER5,	REVFMT_MAJMIN,
152	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_POWER4 |
153	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP, 0, NULL },
154        { "IBM POWER5+",	IBMPOWER5PLUS,	REVFMT_MAJMIN,
155	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_POWER5_PLUS |
156	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP, 0, NULL },
157        { "IBM POWER6",		IBMPOWER6,	REVFMT_MAJMIN,
158	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
159	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | PPC_FEATURE_ARCH_2_05 |
160	   PPC_FEATURE_TRUE_LE, 0, NULL },
161        { "IBM POWER7",		IBMPOWER7,	REVFMT_MAJMIN,
162	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
163	   PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 |
164	   PPC_FEATURE_HAS_VSX | PPC_FEATURE_TRUE_LE, PPC_FEATURE2_DSCR, NULL },
165        { "IBM POWER7+",	IBMPOWER7PLUS,	REVFMT_MAJMIN,
166	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
167	   PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 |
168	   PPC_FEATURE_HAS_VSX, PPC_FEATURE2_DSCR, NULL },
169        { "IBM POWER8E",	IBMPOWER8E,	REVFMT_MAJMIN,
170	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
171	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | PPC_FEATURE_ARCH_2_05 |
172	   PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX | PPC_FEATURE_TRUE_LE,
173	   PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_DSCR | PPC_FEATURE2_ISEL |
174	   PPC_FEATURE2_TAR | PPC_FEATURE2_HAS_VEC_CRYPTO, cpu_powerx_setup },
175        { "IBM POWER8NVL",	IBMPOWER8NVL,	REVFMT_MAJMIN,
176	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
177	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | PPC_FEATURE_ARCH_2_05 |
178	   PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX | PPC_FEATURE_TRUE_LE,
179	   PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_DSCR | PPC_FEATURE2_ISEL |
180	   PPC_FEATURE2_TAR | PPC_FEATURE2_HAS_VEC_CRYPTO, cpu_powerx_setup },
181        { "IBM POWER8",		IBMPOWER8,	REVFMT_MAJMIN,
182	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
183	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | PPC_FEATURE_ARCH_2_05 |
184	   PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX | PPC_FEATURE_TRUE_LE,
185	   PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_DSCR | PPC_FEATURE2_ISEL |
186	   PPC_FEATURE2_TAR | PPC_FEATURE2_HAS_VEC_CRYPTO, cpu_powerx_setup },
187        { "IBM POWER9",		IBMPOWER9,	REVFMT_MAJMIN,
188	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
189	   PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | PPC_FEATURE_ARCH_2_05 |
190	   PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX | PPC_FEATURE_TRUE_LE,
191	   PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_DSCR | PPC_FEATURE2_EBB |
192	   PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR | PPC_FEATURE2_HAS_VEC_CRYPTO |
193	   PPC_FEATURE2_ARCH_3_00 | PPC_FEATURE2_HAS_IEEE128 |
194	   PPC_FEATURE2_DARN, cpu_powerx_setup },
195        { "Motorola PowerPC 7400",	MPC7400,	REVFMT_MAJMIN,
196	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
197        { "Motorola PowerPC 7410",	MPC7410,	REVFMT_MAJMIN,
198	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
199        { "Motorola PowerPC 7450",	MPC7450,	REVFMT_MAJMIN,
200	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
201        { "Motorola PowerPC 7455",	MPC7455,	REVFMT_MAJMIN,
202	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
203        { "Motorola PowerPC 7457",	MPC7457,	REVFMT_MAJMIN,
204	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
205        { "Motorola PowerPC 7447A",	MPC7447A,	REVFMT_MAJMIN,
206	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
207        { "Motorola PowerPC 7448",	MPC7448,	REVFMT_MAJMIN,
208	   PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
209        { "Motorola PowerPC 8240",	MPC8240,	REVFMT_MAJMIN,
210	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
211        { "Motorola PowerPC 8245",	MPC8245,	REVFMT_MAJMIN,
212	   PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup },
213        { "Freescale e500v1 core",	FSL_E500v1,	REVFMT_MAJMIN,
214	   PPC_FEATURE_HAS_SPE | PPC_FEATURE_HAS_EFP_SINGLE | PPC_FEATURE_BOOKE,
215	   PPC_FEATURE2_ISEL, cpu_booke_setup },
216        { "Freescale e500v2 core",	FSL_E500v2,	REVFMT_MAJMIN,
217	   PPC_FEATURE_HAS_SPE | PPC_FEATURE_BOOKE |
218	   PPC_FEATURE_HAS_EFP_SINGLE | PPC_FEATURE_HAS_EFP_DOUBLE,
219	   PPC_FEATURE2_ISEL, cpu_booke_setup },
220	{ "Freescale e500mc core",	FSL_E500mc,	REVFMT_MAJMIN,
221	   PPC_FEATURE_HAS_FPU | PPC_FEATURE_BOOKE | PPC_FEATURE_ARCH_2_05 |
222	   PPC_FEATURE_ARCH_2_06, PPC_FEATURE2_ISEL,
223	   cpu_booke_setup },
224	{ "Freescale e5500 core",	FSL_E5500,	REVFMT_MAJMIN,
225	   PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_BOOKE |
226	   PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06,
227	   PPC_FEATURE2_ISEL, cpu_booke_setup },
228	{ "Freescale e6500 core",	FSL_E6500,	REVFMT_MAJMIN,
229	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
230	   PPC_FEATURE_BOOKE | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06,
231	   PPC_FEATURE2_ISEL, cpu_booke_setup },
232        { "IBM Cell Broadband Engine",	IBMCELLBE,	REVFMT_MAJMIN,
233	   PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU |
234	   PPC_FEATURE_CELL | PPC_FEATURE_SMT, 0, NULL},
235        { "Unknown PowerPC CPU",	0,		REVFMT_HEX, 0, 0, NULL },
236};
237
238static void	cpu_6xx_print_cacheinfo(u_int, uint16_t);
239static int	cpu_feature_bit(SYSCTL_HANDLER_ARGS);
240
241static char model[64];
242SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD | CTLFLAG_CAPRD, model, 0, "");
243
244static const struct cputab	*cput;
245
246u_long cpu_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU;
247u_long cpu_features2 = 0;
248SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features, CTLFLAG_RD,
249    &cpu_features, sizeof(cpu_features), "LX", "PowerPC CPU features");
250SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features2, CTLFLAG_RD,
251    &cpu_features2, sizeof(cpu_features2), "LX", "PowerPC CPU features 2");
252
253#ifdef __powerpc64__
254register_t	lpcr = LPCR_LPES;
255#endif
256
257/* Provide some user-friendly aliases for bits in cpu_features */
258SYSCTL_PROC(_hw, OID_AUTO, floatingpoint,
259    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, PPC_FEATURE_HAS_FPU,
260    cpu_feature_bit, "I", "Floating point instructions executed in hardware");
261SYSCTL_PROC(_hw, OID_AUTO, altivec, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
262    0, PPC_FEATURE_HAS_ALTIVEC, cpu_feature_bit, "I", "CPU supports Altivec");
263
264/*
265 * Phase 1 (early) CPU setup.  Setup the cpu_features/cpu_features2 variables,
266 * so they can be used during platform and MMU bringup.
267 */
268void
269cpu_feature_setup(void)
270{
271	u_int		pvr;
272	uint16_t	vers;
273	const struct	cputab *cp;
274
275	pvr = mfpvr();
276	vers = pvr >> 16;
277	for (cp = models; cp->version != 0; cp++) {
278		if (cp->version == vers)
279			break;
280	}
281
282	cput = cp;
283	cpu_features |= cp->features;
284	cpu_features2 |= cp->features2;
285}
286
287void
288cpu_setup(u_int cpuid)
289{
290	uint64_t	cps;
291	const char	*name;
292	u_int		maj, min, pvr;
293	uint16_t	rev, revfmt, vers;
294
295	pvr = mfpvr();
296	vers = pvr >> 16;
297	rev = pvr;
298	switch (vers) {
299		case MPC7410:
300			min = (pvr >> 0) & 0xff;
301			maj = min <= 4 ? 1 : 2;
302			break;
303		case FSL_E500v1:
304		case FSL_E500v2:
305		case FSL_E500mc:
306		case FSL_E5500:
307			maj = (pvr >>  4) & 0xf;
308			min = (pvr >>  0) & 0xf;
309			break;
310		default:
311			maj = (pvr >>  8) & 0xf;
312			min = (pvr >>  0) & 0xf;
313	}
314
315	revfmt = cput->revfmt;
316	name = cput->name;
317	if (rev == MPC750 && pvr == 15) {
318		name = "Motorola MPC755";
319		revfmt = REVFMT_HEX;
320	}
321	strncpy(model, name, sizeof(model) - 1);
322
323	printf("cpu%d: %s revision ", cpuid, name);
324
325	switch (revfmt) {
326		case REVFMT_MAJMIN:
327			printf("%u.%u", maj, min);
328			break;
329		case REVFMT_HEX:
330			printf("0x%04x", rev);
331			break;
332		case REVFMT_DEC:
333			printf("%u", rev);
334			break;
335	}
336
337	if (cpu_est_clockrate(0, &cps) == 0)
338		printf(", %jd.%02jd MHz", cps / 1000000, (cps / 10000) % 100);
339	printf("\n");
340
341	printf("cpu%d: Features %b\n", cpuid, (int)cpu_features,
342	    PPC_FEATURE_BITMASK);
343	if (cpu_features2 != 0)
344		printf("cpu%d: Features2 %b\n", cpuid, (int)cpu_features2,
345		    PPC_FEATURE2_BITMASK);
346
347	/*
348	 * Configure CPU
349	 */
350	if (cput->cpu_setup != NULL)
351		cput->cpu_setup(cpuid, vers);
352}
353
354/* Get current clock frequency for the given cpu id. */
355int
356cpu_est_clockrate(int cpu_id, uint64_t *cps)
357{
358	uint16_t	vers;
359	register_t	msr;
360	phandle_t	cpu, dev, root;
361	uint32_t	freq32;
362	int		res  = 0;
363	char		buf[8];
364
365	vers = mfpvr() >> 16;
366	msr = mfmsr();
367	mtmsr(msr & ~PSL_EE);
368
369	switch (vers) {
370		case MPC7450:
371		case MPC7455:
372		case MPC7457:
373		case MPC750:
374		case IBM750FX:
375		case MPC7400:
376		case MPC7410:
377		case MPC7447A:
378		case MPC7448:
379			mtspr(SPR_MMCR0_74XX, SPR_MMCR0_FC);
380			mtspr(SPR_PMC1_74XX, 0);
381			mtspr(SPR_MMCR0_74XX,
382			    SPR_MMCR0_74XX_PMC1SEL(PMCN_CYCLES));
383			DELAY(1000);
384			*cps = (mfspr(SPR_PMC1_74XX) * 1000) + 4999;
385			mtspr(SPR_MMCR0_74XX, SPR_MMCR0_FC);
386
387			mtmsr(msr);
388			return (0);
389		case IBM970:
390		case IBM970FX:
391		case IBM970MP:
392			isync();
393			mtspr(SPR_MMCR0, SPR_MMCR0_FC);
394			isync();
395			mtspr(SPR_MMCR1, 0);
396			mtspr(SPR_MMCRA, 0);
397			mtspr(SPR_PMC1, 0);
398			mtspr(SPR_MMCR0, SPR_MMCR0_PMC1SEL(PMC970N_CYCLES));
399			isync();
400			DELAY(1000);
401			powerpc_sync();
402			mtspr(SPR_MMCR0, SPR_MMCR0_FC);
403			*cps = (mfspr(SPR_PMC1) * 1000) + 4999;
404
405			mtmsr(msr);
406			return (0);
407
408		default:
409			root = OF_peer(0);
410			if (root == 0)
411				return (ENXIO);
412
413			dev = OF_child(root);
414			while (dev != 0) {
415				res = OF_getprop(dev, "name", buf, sizeof(buf));
416				if (res > 0 && strcmp(buf, "cpus") == 0)
417					break;
418				dev = OF_peer(dev);
419			}
420			cpu = OF_child(dev);
421			while (cpu != 0) {
422				res = OF_getprop(cpu, "device_type", buf,
423						sizeof(buf));
424				if (res > 0 && strcmp(buf, "cpu") == 0)
425					break;
426				cpu = OF_peer(cpu);
427			}
428			if (cpu == 0)
429				return (ENOENT);
430			if (OF_getprop(cpu, "ibm,extended-clock-frequency",
431			    cps, sizeof(*cps)) >= 0) {
432				*cps = be64toh(*cps);
433				return (0);
434			} else if (OF_getencprop(cpu, "clock-frequency",
435			    &freq32, sizeof(freq32)) >= 0) {
436				*cps = freq32;
437				return (0);
438			} else {
439				return (ENOENT);
440			}
441	}
442}
443
444void
445cpu_6xx_setup(int cpuid, uint16_t vers)
446{
447	register_t hid0, pvr;
448	const char *bitmask;
449
450	hid0 = mfspr(SPR_HID0);
451	pvr = mfpvr();
452
453	/*
454	 * Configure power-saving mode.
455	 */
456	switch (vers) {
457		case MPC603:
458		case MPC603e:
459		case MPC603ev:
460		case MPC604ev:
461		case MPC750:
462		case IBM750FX:
463		case MPC7400:
464		case MPC7410:
465		case MPC8240:
466		case MPC8245:
467			/* Select DOZE mode. */
468			hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
469			hid0 |= HID0_DOZE | HID0_DPM;
470			powerpc_pow_enabled = 1;
471			break;
472
473		case MPC7448:
474		case MPC7447A:
475		case MPC7457:
476		case MPC7455:
477		case MPC7450:
478			/* Enable the 7450 branch caches */
479			hid0 |= HID0_SGE | HID0_BTIC;
480			hid0 |= HID0_LRSTK | HID0_FOLD | HID0_BHT;
481			/* Disable BTIC on 7450 Rev 2.0 or earlier and on 7457 */
482			if (((pvr >> 16) == MPC7450 && (pvr & 0xFFFF) <= 0x0200)
483					|| (pvr >> 16) == MPC7457)
484				hid0 &= ~HID0_BTIC;
485			/* Select NAP mode. */
486			hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
487			hid0 |= HID0_NAP | HID0_DPM;
488			powerpc_pow_enabled = 1;
489			break;
490
491		default:
492			/* No power-saving mode is available. */ ;
493	}
494
495	switch (vers) {
496		case IBM750FX:
497		case MPC750:
498			hid0 &= ~HID0_DBP;		/* XXX correct? */
499			hid0 |= HID0_EMCP | HID0_BTIC | HID0_SGE | HID0_BHT;
500			break;
501
502		case MPC7400:
503		case MPC7410:
504			hid0 &= ~HID0_SPD;
505			hid0 |= HID0_EMCP | HID0_BTIC | HID0_SGE | HID0_BHT;
506			hid0 |= HID0_EIEC;
507			break;
508	}
509
510	mtspr(SPR_HID0, hid0);
511
512	if (bootverbose)
513		cpu_6xx_print_cacheinfo(cpuid, vers);
514
515	switch (vers) {
516		case MPC7447A:
517		case MPC7448:
518		case MPC7450:
519		case MPC7455:
520		case MPC7457:
521			bitmask = HID0_7450_BITMASK;
522			break;
523		default:
524			bitmask = HID0_BITMASK;
525			break;
526	}
527
528	printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, bitmask);
529
530	if (cpu_idle_hook == NULL)
531		cpu_idle_hook = cpu_idle_60x;
532}
533
534static void
535cpu_6xx_print_cacheinfo(u_int cpuid, uint16_t vers)
536{
537	register_t hid;
538
539	hid = mfspr(SPR_HID0);
540	printf("cpu%u: ", cpuid);
541	printf("L1 I-cache %sabled, ", (hid & HID0_ICE) ? "en" : "dis");
542	printf("L1 D-cache %sabled\n", (hid & HID0_DCE) ? "en" : "dis");
543
544	printf("cpu%u: ", cpuid);
545  	if (mfspr(SPR_L2CR) & L2CR_L2E) {
546		switch (vers) {
547		case MPC7450:
548		case MPC7455:
549		case MPC7457:
550			printf("256KB L2 cache, ");
551			if (mfspr(SPR_L3CR) & L3CR_L3E)
552				printf("%cMB L3 backside cache",
553				    mfspr(SPR_L3CR) & L3CR_L3SIZ ? '2' : '1');
554			else
555				printf("L3 cache disabled");
556			printf("\n");
557			break;
558		case IBM750FX:
559			printf("512KB L2 cache\n");
560			break;
561		default:
562			switch (mfspr(SPR_L2CR) & L2CR_L2SIZ) {
563			case L2SIZ_256K:
564				printf("256KB ");
565				break;
566			case L2SIZ_512K:
567				printf("512KB ");
568				break;
569			case L2SIZ_1M:
570				printf("1MB ");
571				break;
572			}
573			printf("write-%s", (mfspr(SPR_L2CR) & L2CR_L2WT)
574			    ? "through" : "back");
575			if (mfspr(SPR_L2CR) & L2CR_L2PE)
576				printf(", with parity");
577			printf(" backside cache\n");
578			break;
579		}
580	} else
581		printf("L2 cache disabled\n");
582}
583
584static void
585cpu_booke_setup(int cpuid, uint16_t vers)
586{
587#ifdef BOOKE_E500
588	register_t hid0;
589	const char *bitmask;
590
591	hid0 = mfspr(SPR_HID0);
592
593	switch (vers) {
594	case FSL_E500mc:
595		bitmask = HID0_E500MC_BITMASK;
596		cpu_idle_hook = cpu_idle_e500mc;
597		break;
598	case FSL_E5500:
599	case FSL_E6500:
600		bitmask = HID0_E5500_BITMASK;
601		cpu_idle_hook = cpu_idle_e500mc;
602		break;
603	case FSL_E500v1:
604	case FSL_E500v2:
605		/* Only e500v1/v2 support HID0 power management setup. */
606
607		/* Program power-management mode. */
608		hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
609		hid0 |= HID0_DOZE;
610
611		mtspr(SPR_HID0, hid0);
612	default:
613		bitmask = HID0_E500_BITMASK;
614		break;
615	}
616	printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, bitmask);
617#endif
618
619	if (cpu_idle_hook == NULL)
620		cpu_idle_hook = cpu_idle_booke;
621}
622
623static void
624cpu_970_setup(int cpuid, uint16_t vers)
625{
626#ifdef AIM
627	uint32_t hid0_hi, hid0_lo;
628
629	__asm __volatile ("mfspr %0,%2; clrldi %1,%0,32; srdi %0,%0,32;"
630	    : "=r" (hid0_hi), "=r" (hid0_lo) : "K" (SPR_HID0));
631
632	/* Configure power-saving mode */
633	switch (vers) {
634	case IBM970MP:
635		hid0_hi |= (HID0_DEEPNAP | HID0_NAP | HID0_DPM);
636		hid0_hi &= ~HID0_DOZE;
637		break;
638	default:
639		hid0_hi |= (HID0_NAP | HID0_DPM);
640		hid0_hi &= ~(HID0_DOZE | HID0_DEEPNAP);
641		break;
642	}
643	powerpc_pow_enabled = 1;
644
645	__asm __volatile (" \
646		sync; isync;					\
647		sldi	%0,%0,32; or %0,%0,%1;			\
648		mtspr	%2, %0;					\
649		mfspr   %0, %2; mfspr   %0, %2; mfspr   %0, %2; \
650		mfspr   %0, %2; mfspr   %0, %2; mfspr   %0, %2; \
651		sync; isync"
652	    :: "r" (hid0_hi), "r"(hid0_lo), "K" (SPR_HID0));
653
654	__asm __volatile ("mfspr %0,%1; srdi %0,%0,32;"
655	    : "=r" (hid0_hi) : "K" (SPR_HID0));
656	printf("cpu%d: HID0 %b\n", cpuid, (int)(hid0_hi), HID0_970_BITMASK);
657#endif
658
659	cpu_idle_hook = cpu_idle_60x;
660}
661
662static void
663cpu_powerx_setup(int cpuid, uint16_t vers)
664{
665
666#if defined(__powerpc64__) && defined(AIM)
667	if ((mfmsr() & PSL_HV) == 0)
668		return;
669
670	/* Nuke the FSCR, to disable all facilities. */
671	mtspr(SPR_FSCR, 0);
672
673	/* Configure power-saving */
674	switch (vers) {
675	case IBMPOWER8:
676	case IBMPOWER8E:
677	case IBMPOWER8NVL:
678		cpu_idle_hook = cpu_idle_powerx;
679		mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_PECE_WAKESET);
680		isync();
681		break;
682	case IBMPOWER9:
683		cpu_idle_hook = cpu_idle_power9;
684		mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_PECE_WAKESET);
685		isync();
686		break;
687	default:
688		return;
689	}
690
691#endif
692}
693
694static int
695cpu_feature_bit(SYSCTL_HANDLER_ARGS)
696{
697	int result;
698
699	result = (cpu_features & arg2) ? 1 : 0;
700
701	return (sysctl_handle_int(oidp, &result, 0, req));
702}
703
704void
705cpu_idle(int busy)
706{
707	sbintime_t sbt = -1;
708
709#ifdef INVARIANTS
710	if ((mfmsr() & PSL_EE) != PSL_EE) {
711		struct thread *td = curthread;
712		printf("td msr %#lx\n", (u_long)td->td_md.md_saved_msr);
713		panic("ints disabled in idleproc!");
714	}
715#endif
716
717	CTR1(KTR_SPARE2, "cpu_idle(%d)", busy);
718
719	if (cpu_idle_hook != NULL) {
720		if (!busy) {
721			critical_enter();
722			sbt = cpu_idleclock();
723		}
724		cpu_idle_hook(sbt);
725		if (!busy) {
726			cpu_activeclock();
727			critical_exit();
728		}
729	}
730
731	CTR1(KTR_SPARE2, "cpu_idle(%d) done", busy);
732}
733
734static void
735cpu_idle_60x(sbintime_t sbt)
736{
737#ifdef AIM
738	register_t msr;
739	uint16_t vers;
740#endif
741
742	if (!powerpc_pow_enabled)
743		return;
744
745#ifdef AIM
746	msr = mfmsr();
747	vers = mfpvr() >> 16;
748
749	switch (vers) {
750	case IBM970:
751	case IBM970FX:
752	case IBM970MP:
753	case MPC7447A:
754	case MPC7448:
755	case MPC7450:
756	case MPC7455:
757	case MPC7457:
758		/* 0x7e00066c: dssall */
759		__asm __volatile("\
760			    .long 0x7e00066c; sync; mtmsr %0; isync"
761			    :: "r"(msr | PSL_POW));
762		break;
763	default:
764		powerpc_sync();
765		mtmsr(msr | PSL_POW);
766		break;
767	}
768#endif
769}
770
771#ifdef BOOKE_E500
772static void
773cpu_idle_e500mc(sbintime_t sbt)
774{
775	/*
776	 * Base binutils doesn't know what the 'wait' instruction is, so
777	 * use the opcode encoding here.
778	 */
779	__asm __volatile(".long 0x7c00007c");
780}
781#endif
782
783static void
784cpu_idle_booke(sbintime_t sbt)
785{
786#ifdef BOOKE_E500
787	register_t msr;
788
789	msr = mfmsr();
790
791	powerpc_sync();
792	mtmsr(msr | PSL_WE);
793#endif
794}
795
796#if defined(__powerpc64__) && defined(AIM)
797static void
798cpu_idle_powerx(sbintime_t sbt)
799{
800	/* Sleeping when running on one cpu gives no advantages - avoid it */
801	if (smp_started == 0)
802		return;
803
804	spinlock_enter();
805	if (sched_runnable()) {
806		spinlock_exit();
807		return;
808	}
809
810	if (can_wakeup == 0)
811		can_wakeup = 1;
812	mb();
813
814	enter_idle_powerx();
815	spinlock_exit();
816}
817
818static void
819cpu_idle_power9(sbintime_t sbt)
820{
821	register_t msr;
822
823	msr = mfmsr();
824
825	/* Suspend external interrupts until stop instruction completes. */
826	mtmsr(msr &  ~PSL_EE);
827	/* Set the stop state to lowest latency, wake up to next instruction */
828	/* Set maximum transition level to 2, for deepest lossless sleep. */
829	mtspr(SPR_PSSCR, (2 << PSSCR_MTL_S) | (0 << PSSCR_RL_S));
830	/* "stop" instruction (PowerISA 3.0) */
831	__asm __volatile (".long 0x4c0002e4");
832	/*
833	 * Re-enable external interrupts to capture the interrupt that caused
834	 * the wake up.
835	 */
836	mtmsr(msr);
837
838}
839#endif
840
841int
842cpu_idle_wakeup(int cpu)
843{
844
845	return (0);
846}
847