1 /* $NetBSD: cpu.c,v 1.2 2002/03/15 21:12:07 eeh 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/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/properties.h> 42 43 #include <machine/autoconf.h> 44 #include <machine/dcr.h> 45 #include <machine/cpu.h> 46 47 struct cputab { 48 int version; 49 char *name; 50 }; 51 static struct cputab models[] = { 52 { PVR_401A1 >> 16, "401A1" }, 53 { PVR_401B2 >> 16, "401B21" }, 54 { PVR_401C2 >> 16, "401C2" }, 55 { PVR_401D2 >> 16, "401D2" }, 56 { PVR_401E2 >> 16, "401E2" }, 57 { PVR_401F2 >> 16, "401F2" }, 58 { PVR_401G2 >> 16, "401G2" }, 59 { PVR_403 >> 16, "403" }, 60 { PVR_405GP >> 16, "405GP" }, 61 { 0, NULL } 62 }; 63 64 static int cpumatch(struct device *, struct cfdata *, void *); 65 static void cpuattach(struct device *, struct device *, void *); 66 67 /* 68 * Arguably the ECC stuff belongs somewhere else.... 69 */ 70 int intr_ecc(void *); 71 72 u_quad_t intr_ecc_tb; 73 u_quad_t intr_ecc_iv; /* Interval */ 74 u_int32_t intr_ecc_cnt; 75 76 struct cfattach cpu_ca = { 77 sizeof(struct device), cpumatch, cpuattach 78 }; 79 80 int ncpus; 81 82 struct cpu_info cpu_info_store; 83 84 int cpufound = 0; 85 86 static int 87 cpumatch(struct device *parent, struct cfdata *cf, void *aux) 88 { 89 struct mainbus_attach_args *maa = aux; 90 91 /* make sure that we're looking for a CPU */ 92 if (strcmp(maa->mb_name, cf->cf_driver->cd_name) != 0) 93 return (0); 94 95 return !cpufound; 96 } 97 98 static void 99 cpuattach(struct device *parent, struct device *self, void *aux) 100 { 101 int pvr, cpu; 102 int own, pcf, cas, pcl, aid; 103 struct cputab *cp = models; 104 unsigned int processor_freq; 105 106 if (board_info_get("processor-frequency", 107 &processor_freq, sizeof(processor_freq)) == -1) 108 panic("no processor-frequency"); 109 110 cpufound++; 111 ncpus++; 112 113 asm ("mfpvr %0" : "=r"(pvr)); 114 cpu = pvr >> 16; 115 116 /* Break PVR up into separate fields and print them out. */ 117 own = (pvr >> 20) & 0xfff; 118 pcf = (pvr >> 16) & 0xf; 119 cas = (pvr >> 10) & 0x3f; 120 pcl = (pvr >> 6) & 0xf; 121 aid = pvr & 0x3f; 122 123 while (cp->name) { 124 if (cp->version == cpu) 125 break; 126 cp++; 127 } 128 if (cp->name) 129 strcpy(cpu_model, cp->name); 130 else 131 sprintf(cpu_model, "Version 0x%x", cpu); 132 sprintf(cpu_model + strlen(cpu_model), " (Revision %d.%d)", 133 (pvr >> 8) & 0xff, pvr & 0xff); 134 135 #if 1 136 printf(": %dMHz %s\n", processor_freq / 1000 / 1000, 137 cpu_model); 138 #endif 139 140 cpu_probe_cache(); 141 142 printf("Instruction cache size %d line size %d\n", 143 curcpu()->ci_ci.icache_size, curcpu()->ci_ci.icache_line_size); 144 printf("Data cache size %d line size %d\n", 145 curcpu()->ci_ci.dcache_size, curcpu()->ci_ci.dcache_line_size); 146 147 #ifdef DEBUG 148 /* It sux that the cache info here is useless. */ 149 printf("PVR: owner %x core family %x cache %x version %x asic %x\n", 150 own, pcf, cas, pcl, aid); 151 #endif 152 153 /* Initialize ECC error-logging handler. This is always enabled, 154 * but it will never be called on systems that do not have ECC 155 * enabled by POST code in the bootloader. 156 */ 157 158 printf("Enabling ecc handler\n"); 159 intr_ecc_tb = 0; 160 intr_ecc_iv = processor_freq; /* Set interval */ 161 intr_ecc_cnt = 0; 162 163 intr_establish(16, IST_LEVEL, IPL_SERIAL, intr_ecc, NULL); 164 } 165 166 /* 167 * This routine must be explicitly called to initialize the 168 * CPU cache information so cache flushe and memcpy operation 169 * work. 170 */ 171 void 172 cpu_probe_cache() 173 { 174 int version; 175 176 /* 177 * First we need to identify the cpu and determine the 178 * cache line size, or things like memset/memcpy may lose 179 * badly. 180 */ 181 __asm __volatile("mfpvr %0" : "=r" (version)); 182 switch (version & 0xffff0000) { 183 case PVR_401A1: 184 curcpu()->ci_ci.dcache_size = 1024; 185 curcpu()->ci_ci.dcache_line_size = 16; 186 curcpu()->ci_ci.icache_size = 2848; 187 curcpu()->ci_ci.icache_line_size = 16; 188 break; 189 case PVR_401B2: 190 curcpu()->ci_ci.dcache_size = 8192; 191 curcpu()->ci_ci.dcache_line_size = 16; 192 curcpu()->ci_ci.icache_size = 16384; 193 curcpu()->ci_ci.icache_line_size = 16; 194 break; 195 case PVR_401C2: 196 curcpu()->ci_ci.dcache_size = 8192; 197 curcpu()->ci_ci.dcache_line_size = 16; 198 curcpu()->ci_ci.icache_size = 0; 199 curcpu()->ci_ci.icache_line_size = 16; 200 break; 201 case PVR_401D2: 202 curcpu()->ci_ci.dcache_size = 2848; 203 curcpu()->ci_ci.dcache_line_size = 16; 204 curcpu()->ci_ci.icache_size = 4096; 205 curcpu()->ci_ci.icache_line_size = 16; 206 break; 207 case PVR_401E2: 208 curcpu()->ci_ci.dcache_size = 0; 209 curcpu()->ci_ci.dcache_line_size = 16; 210 curcpu()->ci_ci.icache_size = 0; 211 curcpu()->ci_ci.icache_line_size = 16; 212 break; 213 case PVR_401F2: 214 curcpu()->ci_ci.dcache_size = 2048; 215 curcpu()->ci_ci.dcache_line_size = 16; 216 curcpu()->ci_ci.icache_size = 2848; 217 curcpu()->ci_ci.icache_line_size = 16; 218 break; 219 case PVR_401G2: 220 curcpu()->ci_ci.dcache_size = 2848; 221 curcpu()->ci_ci.dcache_line_size = 16; 222 curcpu()->ci_ci.icache_size = 8192; 223 curcpu()->ci_ci.icache_line_size = 16; 224 break; 225 case PVR_403: 226 curcpu()->ci_ci.dcache_line_size = 16; 227 curcpu()->ci_ci.icache_line_size = 16; 228 break; 229 case PVR_405GP: 230 curcpu()->ci_ci.dcache_size = 8192; 231 curcpu()->ci_ci.dcache_line_size = 32; 232 curcpu()->ci_ci.icache_size = 8192; 233 curcpu()->ci_ci.icache_line_size = 32; 234 break; 235 default: 236 /* 237 * Unknown CPU type. For safety we'll specify a 238 * cache with a 4-byte line size. That way cache 239 * flush routines won't miss any lines. 240 */ 241 curcpu()->ci_ci.dcache_line_size = 4; 242 curcpu()->ci_ci.icache_line_size = 4; 243 break; 244 } 245 246 } 247 248 /* 249 * These small routines may have to be replaced, 250 * if/when we support processors other that the 604. 251 */ 252 253 void 254 dcache_flush_page(vaddr_t va) 255 { 256 int i; 257 258 if (curcpu()->ci_ci.dcache_line_size) 259 for (i = 0; i < NBPG; i += curcpu()->ci_ci.dcache_line_size) 260 asm volatile("dcbf %0,%1" : : "r" (va), "r" (i)); 261 asm volatile("sync;isync" : : ); 262 } 263 264 void 265 icache_flush_page(vaddr_t va) 266 { 267 int i; 268 269 if (curcpu()->ci_ci.icache_line_size) 270 for (i = 0; i < NBPG; i += curcpu()->ci_ci.icache_line_size) 271 asm volatile("icbi %0,%1" : : "r" (va), "r" (i)); 272 asm volatile("sync;isync" : : ); 273 } 274 275 void 276 dcache_flush(vaddr_t va, vsize_t len) 277 { 278 int i; 279 280 if (len == 0) 281 return; 282 283 /* Make sure we flush all cache lines */ 284 len += va & (curcpu()->ci_ci.dcache_line_size-1); 285 if (curcpu()->ci_ci.dcache_line_size) 286 for (i = 0; i < len; i += curcpu()->ci_ci.dcache_line_size) 287 asm volatile("dcbf %0,%1" : : "r" (va), "r" (i)); 288 asm volatile("sync;isync" : : ); 289 } 290 291 void 292 icache_flush(vaddr_t va, vsize_t len) 293 { 294 int i; 295 296 if (len == 0) 297 return; 298 299 /* Make sure we flush all cache lines */ 300 len += va & (curcpu()->ci_ci.icache_line_size-1); 301 if (curcpu()->ci_ci.icache_line_size) 302 for (i = 0; i < len; i += curcpu()->ci_ci.icache_line_size) 303 asm volatile("icbi %0,%1" : : "r" (va), "r" (i)); 304 asm volatile("sync;isync" : : ); 305 } 306 307 /* 308 * ECC fault handler. 309 */ 310 int 311 intr_ecc(void * arg) 312 { 313 u_int32_t esr, ear; 314 int ce, ue; 315 u_quad_t tb; 316 u_long tmp, msr, dat; 317 unsigned int memsiz; 318 319 if (board_info_get("mem-size", &memsiz, sizeof(memsiz)) == -1) 320 panic("no mem-size"); 321 322 /* This code needs to be improved to handle double-bit errors */ 323 /* in some intelligent fashion. */ 324 325 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 326 esr = mfdcr(DCR_SDRAM0_CFGDATA); 327 328 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_BEAR); 329 ear = mfdcr(DCR_SDRAM0_CFGDATA); 330 331 /* Always clear the error to stop the intr ASAP. */ 332 333 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 334 mtdcr(DCR_SDRAM0_CFGDATA, 0xffffffff); 335 336 if (esr == 0x00) { 337 /* No current error. Could happen due to intr. nesting */ 338 return(1); 339 }; 340 341 /* Only report errors every once per second max. Do this using the TB, */ 342 /* because the system time (via microtime) may be adjusted when the date is set */ 343 /* and can't reliably be used to measure intervals. */ 344 345 asm ("1: mftbu %0; mftb %0+1; mftbu %1; cmpw %0,%1; bne 1b" 346 : "=r"(tb), "=r"(tmp)); 347 intr_ecc_cnt++; 348 349 if ((tb - intr_ecc_tb) < intr_ecc_iv) { 350 return(1); 351 }; 352 353 ce = (esr & SDRAM0_ECCESR_CE) != 0x00; 354 ue = (esr & SDRAM0_ECCESR_UE) != 0x00; 355 356 printf("ECC: Error CNT=%d ESR=%x EAR=%x %s BKNE=%d%d%d%d " 357 "BLCE=%d%d%d%d CBE=%d%d.\n", 358 intr_ecc_cnt, esr, ear, 359 (ue) ? "Uncorrectable" : "Correctable", 360 ((esr & SDRAM0_ECCESR_BKEN(0)) != 0x00), 361 ((esr & SDRAM0_ECCESR_BKEN(1)) != 0x00), 362 ((esr & SDRAM0_ECCESR_BKEN(2)) != 0x00), 363 ((esr & SDRAM0_ECCESR_BKEN(3)) != 0x00), 364 ((esr & SDRAM0_ECCESR_BLCEN(0)) != 0x00), 365 ((esr & SDRAM0_ECCESR_BLCEN(1)) != 0x00), 366 ((esr & SDRAM0_ECCESR_BLCEN(2)) != 0x00), 367 ((esr & SDRAM0_ECCESR_BLCEN(3)) != 0x00), 368 ((esr & SDRAM0_ECCESR_CBEN(0)) != 0x00), 369 ((esr & SDRAM0_ECCESR_CBEN(1)) != 0x00)); 370 371 /* Should check for uncorrectable errors and panic... */ 372 373 if (intr_ecc_cnt > 1000) { 374 printf("ECC: Too many errors, recycling entire " 375 "SDRAM (size = %d).\n", memsiz); 376 377 /* Can this code be changed to run without disabling data MMU and disabling intrs? */ 378 /* Does kernel always map all of physical RAM VA=PA? If so, just loop over lowmem. */ 379 380 asm volatile( 381 "mfmsr %0;" 382 "li %1, 0x00;" 383 "ori %1, %1, 0x8010;" 384 "andc %1, %0, %1;" 385 "mtmsr %1;" 386 "sync;isync;" 387 "li %1, 0x00;" 388 "1:" 389 "dcbt 0, %1;" 390 "sync;isync;" 391 "lwz %2, 0(%1);" 392 "stw %2, 0(%1);" 393 "sync;isync;" 394 "dcbf 0, %1;" 395 "sync;isync;" 396 "addi %1, %1, 0x20;" 397 "addic. %3, %3, -0x20;" 398 "bge 1b;" 399 "mtmsr %0;" 400 "sync;isync;" 401 : "=&r" (msr), "=&r" (tmp), "=&r" (dat) 402 : "r" (memsiz) : "0" ); 403 404 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 405 esr = mfdcr(DCR_SDRAM0_CFGDATA); 406 407 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 408 mtdcr(DCR_SDRAM0_CFGDATA, 0xffffffff); 409 410 /* Correctable errors here are OK, mem should be clean now. */ 411 /* Should check for uncorrectable errors and panic... */ 412 printf("ECC: Recycling complete, ESR=%x. " 413 "Checking for persistent errors.\n", esr); 414 415 asm volatile( 416 "mfmsr %0;" 417 "li %1, 0x00;" 418 "ori %1, %1, 0x8010;" 419 "andc %1, %0, %1;" 420 "mtmsr %1;" 421 "sync;isync;" 422 "li %1, 0x00;" 423 "1:" 424 "dcbt 0, %1;" 425 "sync;isync;" 426 "lwz %2, 0(%1);" 427 "stw %2, 0(%1);" 428 "sync;isync;" 429 "dcbf 0, %1;" 430 "sync;isync;" 431 "addi %1, %1, 0x20;" 432 "addic. %3, %3, -0x20;" 433 "bge 1b;" 434 "mtmsr %0;" 435 "sync;isync;" 436 : "=&r" (msr), "=&r" (tmp), "=&r" (dat) 437 : "r" (memsiz) : "0" ); 438 439 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 440 esr = mfdcr(DCR_SDRAM0_CFGDATA); 441 442 /* If esr is non zero here, we're screwed. Should check this and panic. */ 443 printf("ECC: Persistent error check complete, " 444 "final ESR=%x.\n", esr); 445 }; 446 447 intr_ecc_tb = tb; 448 intr_ecc_cnt = 0; 449 450 return(1); 451 }; 452