Home | History | Annotate | Line # | Download | only in ibm4xx
      1 /*	$NetBSD: cpu.c,v 1.42 2026/09/28 20:41:00 rkujawa Exp $	*/
      2 
      3 /*
      4  * Copyright 2001 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc.
      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 for the NetBSD Project by
     20  *      Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.42 2026/09/28 20:41:00 rkujawa Exp $");
     40 
     41 #include "opt_ppcarch.h"
     42 #ifdef PPC_IBM440
     43 #include "opt_ppc4xx.h"
     44 #endif
     45 
     46 #include <sys/param.h>
     47 #include <sys/systm.h>
     48 #include <sys/device.h>
     49 #include <sys/evcnt.h>
     50 #include <sys/cpu.h>
     51 #include <sys/sysctl.h>
     52 
     53 #include <uvm/uvm_extern.h>
     54 
     55 #include <prop/proplib.h>
     56 
     57 #include <machine/psl.h>
     58 
     59 #include <powerpc/ibm4xx/cpu.h>
     60 #include <powerpc/ibm4xx/dev/plbvar.h>
     61 #ifdef PPC_IBM440
     62 #include <powerpc/ibm4xx/amcc460ex.h>
     63 #include <powerpc/ibm4xx/ibm4xx_460ex_pll.h>
     64 #ifdef PPC4XX_L2CACHE
     65 #include <powerpc/ibm4xx/ibm4xx_460ex_l2.h>
     66 #endif
     67 #endif
     68 
     69 struct cputab {
     70 	u_int version;
     71 	u_int mask;
     72 	const char *name;
     73 	struct cache_info ci;
     74 };
     75 
     76 static const struct cputab models[] = {
     77 	{
     78 		.version = PVR_401A1,
     79 		.mask = 0xffff0000,
     80 		.name = "401A1",
     81 		.ci = {
     82 			.dcache_size = 1024,
     83 			.dcache_line_size = 16,
     84 			.icache_size = 2048,
     85 			.icache_line_size = 16,
     86 		}
     87 	}, {
     88 		.version = PVR_401B2,
     89 		.mask = 0xffff0000,
     90 		.name = "401B21",
     91 		.ci = {
     92 			.dcache_size = 8192,
     93 			.dcache_line_size = 16,
     94 			.icache_size = 16384,
     95 			.icache_line_size = 16,
     96 		}
     97 	}, {
     98 		.version = PVR_401C2,
     99 		.mask = 0xffff0000,
    100 		.name = "401C2",
    101 		.ci = {
    102 			.dcache_size = 8192,
    103 			.dcache_line_size = 16,
    104 			.icache_size = 0,
    105 			.icache_line_size = 16,
    106 		}
    107 	}, {
    108 		.version = PVR_401D2,
    109 		.mask = 0xffff0000,
    110 		.name = "401D2",
    111 		.ci = {
    112 			.dcache_size = 2048,
    113 			.dcache_line_size = 16,
    114 			.icache_size = 4096,
    115 			.icache_line_size = 16,
    116 		}
    117 	}, {
    118 		.version = PVR_401E2,
    119 		.mask = 0xffff0000,
    120 		.name = "401E2",
    121 		.ci = {
    122 			.dcache_size = 0,
    123 			.dcache_line_size = 16,
    124 			.icache_size = 0,
    125 			.icache_line_size = 16,
    126 		}
    127 	}, {
    128 		.version = PVR_401F2,
    129 		.mask = 0xffff0000,
    130 		.name = "401F2",
    131 		.ci = {
    132 			.dcache_size = 2048,
    133 			.dcache_line_size = 16,
    134 			.icache_size = 2048,
    135 			.icache_line_size = 16,
    136 		}
    137 	}, {
    138 		.version = PVR_401G2,
    139 		.mask = 0xffff0000,
    140 		.name = "401G2",
    141 		.ci = {
    142 			.dcache_size = 2048,
    143 			.dcache_line_size = 16,
    144 			.icache_size = 8192,
    145 			.icache_line_size = 16,
    146 		}
    147 	}, {
    148 		.version = PVR_403GA,	/* XXX no MMU */
    149 		.mask = 0xffffff00,
    150 		.name = "403GA",
    151 		.ci = {
    152 			.dcache_size = 1024,
    153 			.dcache_line_size = 16,
    154 			.icache_size = 2048,
    155 			.icache_line_size = 16,
    156 		}
    157 	}, {
    158 		.version = PVR_403GB,	/* XXX no MMU */
    159 		.mask = 0xffffff00,
    160 		.name = "403GB",
    161 		.ci = {
    162 			.dcache_size = 1024,
    163 			.dcache_line_size = 16,
    164 			.icache_size = 2048,
    165 			.icache_line_size = 16,
    166 		}
    167 	}, {
    168 		.version = PVR_403GC,
    169 		.mask = 0xffffff00,
    170 		.name = "403GC",
    171 		.ci = {
    172 			.dcache_size = 1024,
    173 			.dcache_line_size = 16,
    174 			.icache_size = 2048,
    175 			.icache_line_size = 16,
    176 		}
    177 	}, {
    178 		.version = PVR_403GCX,
    179 		.mask = 0xffffff00,
    180 		.name = "403GCX",
    181 		.ci = {
    182 			.dcache_size = 8192,
    183 			.dcache_line_size = 16,
    184 			.icache_size = 16384,
    185 			.icache_line_size = 16,
    186 		}
    187 	}, {
    188 		.version = PVR_405GP,
    189 		.mask = 0xffff0000,
    190 		.name = "405GP",
    191 		.ci = {
    192 			.dcache_size = 8192,
    193 			.dcache_line_size = 32,
    194 			.icache_size = 16384,
    195 			.icache_line_size = 32,
    196 		}
    197 	}, {
    198 		.version = PVR_405GPR,
    199 		.mask = 0xffff0000,
    200 		.name = "405GPr",
    201 		.ci = {
    202 			.dcache_size = 16384,
    203 			.dcache_line_size = 32,
    204 			.icache_size = 16384,
    205 			.icache_line_size = 32,
    206 		}
    207 	}, {
    208 		.version = PVR_405D5X1,
    209 		.mask = 0xfffff000,
    210 		.name = "Xilinx Virtex II Pro",
    211 		.ci = {
    212 			.dcache_size = 16384,
    213 			.dcache_line_size = 32,
    214 			.icache_size = 16384,
    215 			.icache_line_size = 32,
    216 		}
    217 	}, {
    218 		.version = PVR_405D5X2,
    219 		.mask = 0xfffff000,
    220 		.name = "Xilinx Virtex 4 FX",
    221 		.ci = {
    222 			.dcache_size = 16384,
    223 			.dcache_line_size = 32,
    224 			.icache_size = 16384,
    225 			.icache_line_size = 32,
    226 		}
    227 	}, {
    228 		.version = PVR_405EX,
    229 		.mask = 0xffff0000,
    230 		.name = "405EX",
    231 		.ci = {
    232 			.dcache_size = 16384,
    233 			.dcache_line_size = 32,
    234 			.icache_size = 16384,
    235 			.icache_line_size = 32,
    236 		}
    237 	}, {
    238 		.version = PVR_460EX,
    239 		.mask = 0xffff0000,
    240 		.name = "460EX",
    241 		.ci = {
    242 			.dcache_size = 32768,
    243 			.dcache_line_size = 32,
    244 			.icache_size = 32768,
    245 			.icache_line_size = 32,
    246 		}
    247 	}, {
    248 		.version = 0,
    249 		.mask = 0,
    250 		.name = NULL,
    251 		.ci = {
    252 			/*
    253 			 * Unknown CPU type.  For safety we'll specify a
    254 			 * cache with a 4-byte line size.  That way cache
    255 			 * flush routines won't miss any lines.
    256 			 */
    257 			.dcache_line_size = 4,
    258 			.icache_line_size = 4,
    259 		},
    260 	},
    261 };
    262 
    263 static int	cpumatch(device_t, cfdata_t, void *);
    264 static void	cpuattach(device_t, device_t, void *);
    265 
    266 CFATTACH_DECL_NEW(cpu, 0, cpumatch, cpuattach, NULL, NULL);
    267 
    268 int ncpus;
    269 
    270 struct cpu_info cpu_info[1] = {
    271 	{
    272 		/* XXX add more ci_ev_* as we teach 4xx about them */
    273 		.ci_ev_clock = EVCNT_INITIALIZER(EVCNT_TYPE_INTR,
    274 		    NULL, "cpu0", "clock"),
    275 		.ci_ev_statclock = EVCNT_INITIALIZER(EVCNT_TYPE_INTR,
    276 		    NULL, "cpu0", "stat clock"),
    277 		.ci_curlwp = &lwp0,
    278 	}
    279 };
    280 
    281 bool cpufound;
    282 
    283 /*
    284  * Idle the CPU using the 4xx wait state.
    285  *
    286  * Note that... unlike classic PPC's MSR[POW], 4xx does NOT clear WE
    287  * in the saved SRR1 on interrupt
    288  */
    289 extern int powersave;
    290 
    291 static void
    292 ibm4xx_idlespin(void)
    293 {
    294 
    295 	KASSERT(mfmsr() & PSL_EE);
    296 
    297 	if (powersave > 0)
    298 		mtmsr(mfmsr() | PSL_WE);
    299 }
    300 
    301 SYSCTL_SETUP(sysctl_machdep_powersave_setup, "sysctl machdep.powersave setup")
    302 {
    303 
    304 	sysctl_createv(clog, 0, NULL, NULL,
    305 	    CTLFLAG_PERMANENT,
    306 	    CTLTYPE_NODE, "machdep", NULL,
    307 	    NULL, 0, NULL, 0,
    308 	    CTL_MACHDEP, CTL_EOL);
    309 	sysctl_createv(clog, 0, NULL, NULL,
    310 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    311 	    CTLTYPE_INT, "powersave",
    312 	    SYSCTL_DESCR("Idle CPU using the wait state (MSR[WE])"),
    313 	    NULL, 0, &powersave, 0,
    314 	    CTL_MACHDEP, CPU_POWERSAVE, CTL_EOL);
    315 }
    316 
    317 static int
    318 cpumatch(device_t parent, cfdata_t cf, void *aux)
    319 {
    320 	struct plb_attach_args *paa = aux;
    321 
    322 	/* make sure that we're looking for a CPU */
    323 	if (strcmp(paa->plb_name, cf->cf_name) != 0)
    324 		return (0);
    325 
    326 	return !cpufound;
    327 }
    328 
    329 static void
    330 cpuattach(device_t parent, device_t self, void *aux)
    331 {
    332 	struct cpu_info * const ci = curcpu();
    333 	const struct cputab *cp;
    334 	u_int processor_freq;
    335 	prop_number_t freq;
    336 
    337 	freq = prop_dictionary_get(board_properties, "processor-frequency");
    338 	KASSERT(freq != NULL);
    339 	processor_freq = (unsigned int) prop_number_integer_value(freq);
    340 
    341 	cpufound = true;
    342 	ncpus++;
    343 
    344 	/*
    345 	 * Use the wait-state idle loop, controllable via machdep.powersave.
    346 	 */
    347 	ci->ci_idlespin = ibm4xx_idlespin;
    348 	powersave = 0; /* off by default */
    349 
    350 	const u_int pvr = mfpvr();
    351 	for (cp = models; cp->name != NULL; cp++) {
    352 		if ((pvr & cp->mask) == cp->version) {
    353 			cpu_setmodel("%s", cp->name);
    354 			break;
    355 		}
    356 	}
    357 	if (__predict_false(cp->name == NULL))
    358 		cpu_setmodel("Version 0x%x", pvr);
    359 
    360 	aprint_normal(": %uMHz %s (PVR 0x%08x)\n",
    361 	    (processor_freq + 500000) / 1000000,
    362 	    (cp->name != NULL ? cpu_getmodel() : "unknown model"),
    363 	    pvr);
    364 
    365 	cpu_probe_cache();
    366 
    367 	/* We would crash later on anyway so just make the reason obvious */
    368 	if (ci->ci_ci.icache_size == 0 && ci->ci_ci.dcache_size == 0)
    369 		panic("%s: %s: could not detect cache size",
    370 		    __func__, device_xname(self));
    371 
    372 	aprint_normal_dev(self, "%uKB/%uB L1 instruction cache\n",
    373 	    ci->ci_ci.icache_size / 1024, ci->ci_ci.icache_line_size);
    374 	aprint_normal_dev(self, "%uKB/%uB L1 data cache\n",
    375 	    ci->ci_ci.dcache_size / 1024, ci->ci_ci.dcache_line_size);
    376 
    377 #ifdef PPC_IBM440
    378 	if ((pvr >> 16) == AMCC460EX) {
    379 		u_int base_mhz;
    380 
    381 #ifdef PPC4XX_L2CACHE
    382 		ibm4xx_460ex_l2cache_enable();
    383 		if (ibm4xx_460ex_l2_enabled) {
    384 			aprint_normal_dev(self, "256KB/32B L2 cache, "
    385 			    "write-through, snooping\n");
    386 			aprint_verbose_dev(self, "L2C0_CFG %#x SNP0 %#x SNP1 %#x\n",
    387 			    ibm4xx_460ex_l2_cfg, mfdcr(DCR_L2C0_SNP0),
    388 			    mfdcr(DCR_L2C0_SNP1));
    389 		} else
    390 			aprint_error_dev(self, "L2 cache not enabled "
    391 			    "(L2C0_CFG %#x)\n", ibm4xx_460ex_l2_cfg);
    392 #endif
    393 		base_mhz = ibm4xx_460ex_base_clock(processor_freq);
    394 		if (base_mhz != 0)
    395 			aprint_normal_dev(self, "%uMHz PLL clock\n",
    396 			    base_mhz);
    397 	}
    398 #endif
    399 }
    400 
    401 /*
    402  * This routine must be explicitly called to initialize the
    403  * CPU cache information so cache flushe and memcpy operation
    404  * work.
    405  */
    406 void
    407 cpu_probe_cache(void)
    408 {
    409 	struct cpu_info * const ci = curcpu();
    410 	const struct cputab *cp = models;
    411 
    412 	const u_int pvr = mfpvr();
    413 	for (cp = models; cp->name != NULL; cp++) {
    414 		if ((pvr & cp->mask) == cp->version)
    415 			break;
    416 	}
    417 
    418 	/*
    419 	 * Copy the cache from the cputab into cpu_info.
    420 	 */
    421 	ci->ci_ci = cp->ci;
    422 }
    423 
    424 /*
    425  * These small routines may have to be replaced,
    426  * if/when we support processors other that the 604.
    427  */
    428 
    429 void
    430 dcache_wbinv_page(vaddr_t va)
    431 {
    432 	const size_t dcache_line_size = curcpu()->ci_ci.dcache_line_size;
    433 
    434 	if (dcache_line_size) {
    435 		for (size_t i = 0; i < PAGE_SIZE; i += dcache_line_size) {
    436 			__asm volatile ("dcbf %0,%1" : : "b" (va), "r" (i));
    437 		}
    438 		__asm volatile ("sync; isync");
    439 	}
    440 }
    441