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