1/* 2 * Copyright (C) 2001 Broadcom Corporation 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 */ 18 19#include <linux/sched.h> 20#include <asm/mipsregs.h> 21 22/* SB1 definitions */ 23 24#define SB1_CACHE_INDEX_MASK 0x1fe0 25 26#define CP0_ERRCTL_RECOVERABLE (1 << 31) 27#define CP0_ERRCTL_DCACHE (1 << 30) 28#define CP0_ERRCTL_ICACHE (1 << 29) 29#define CP0_ERRCTL_MULTIBUS (1 << 23) 30#define CP0_ERRCTL_MC_TLB (1 << 15) 31#define CP0_ERRCTL_MC_TIMEOUT (1 << 14) 32 33#define CP0_CERRI_TAG_PARITY (1 << 29) 34#define CP0_CERRI_DATA_PARITY (1 << 28) 35#define CP0_CERRI_EXTERNAL (1 << 26) 36 37#define CP0_CERRI_IDX_VALID(c) (!((c) & CP0_CERRI_EXTERNAL)) 38#define CP0_CERRI_DATA (CP0_CERRI_DATA_PARITY) 39 40#define CP0_CERRD_MULTIPLE (1 << 31) 41#define CP0_CERRD_TAG_STATE (1 << 30) 42#define CP0_CERRD_TAG_ADDRESS (1 << 29) 43#define CP0_CERRD_DATA_SBE (1 << 28) 44#define CP0_CERRD_DATA_DBE (1 << 27) 45#define CP0_CERRD_EXTERNAL (1 << 26) 46#define CP0_CERRD_LOAD (1 << 25) 47#define CP0_CERRD_STORE (1 << 24) 48#define CP0_CERRD_FILLWB (1 << 23) 49#define CP0_CERRD_COHERENCY (1 << 22) 50#define CP0_CERRD_DUPTAG (1 << 21) 51 52#define CP0_CERRD_DPA_VALID(c) (!((c) & CP0_CERRD_EXTERNAL)) 53#define CP0_CERRD_IDX_VALID(c) \ 54 (((c) & (CP0_CERRD_LOAD | CP0_CERRD_STORE)) ? (!((c) & CP0_CERRD_EXTERNAL)) : 0) 55#define CP0_CERRD_CAUSES \ 56 (CP0_CERRD_LOAD | CP0_CERRD_STORE | CP0_CERRD_FILLWB | CP0_CERRD_COHERENCY | CP0_CERRD_DUPTAG) 57#define CP0_CERRD_TYPES \ 58 (CP0_CERRD_TAG_STATE | CP0_CERRD_TAG_ADDRESS | CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE | CP0_CERRD_EXTERNAL) 59#define CP0_CERRD_DATA (CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE) 60 61static uint32_t extract_ic(unsigned short addr, int data); 62static uint32_t extract_dc(unsigned short addr, int data); 63 64static inline void breakout_errctl(unsigned int val) 65{ 66 if (val & CP0_ERRCTL_RECOVERABLE) 67 prom_printf(" recoverable"); 68 if (val & CP0_ERRCTL_DCACHE) 69 prom_printf(" dcache"); 70 if (val & CP0_ERRCTL_ICACHE) 71 prom_printf(" icache"); 72 if (val & CP0_ERRCTL_MULTIBUS) 73 prom_printf(" multiple-buserr"); 74 prom_printf("\n"); 75} 76 77static inline void breakout_cerri(unsigned int val) 78{ 79 if (val & CP0_CERRI_TAG_PARITY) 80 prom_printf(" tag-parity"); 81 if (val & CP0_CERRI_DATA_PARITY) 82 prom_printf(" data-parity"); 83 if (val & CP0_CERRI_EXTERNAL) 84 prom_printf(" external"); 85 prom_printf("\n"); 86} 87 88static inline void breakout_cerrd(unsigned int val) 89{ 90 switch (val & CP0_CERRD_CAUSES) { 91 case CP0_CERRD_LOAD: 92 prom_printf(" load,"); 93 break; 94 case CP0_CERRD_STORE: 95 prom_printf(" store,"); 96 break; 97 case CP0_CERRD_FILLWB: 98 prom_printf(" fill/wb,"); 99 break; 100 case CP0_CERRD_COHERENCY: 101 prom_printf(" coherency,"); 102 break; 103 case CP0_CERRD_DUPTAG: 104 prom_printf(" duptags,"); 105 break; 106 default: 107 prom_printf(" NO CAUSE,"); 108 break; 109 } 110 if (!(val & CP0_CERRD_TYPES)) 111 prom_printf(" NO TYPE"); 112 else { 113 if (val & CP0_CERRD_MULTIPLE) 114 prom_printf(" multi-err"); 115 if (val & CP0_CERRD_TAG_STATE) 116 prom_printf(" tag-state"); 117 if (val & CP0_CERRD_TAG_ADDRESS) 118 prom_printf(" tag-address"); 119 if (val & CP0_CERRD_DATA_SBE) 120 prom_printf(" data-SBE"); 121 if (val & CP0_CERRD_DATA_DBE) 122 prom_printf(" data-DBE"); 123 if (val & CP0_CERRD_EXTERNAL) 124 prom_printf(" external"); 125 } 126 prom_printf("\n"); 127} 128 129asmlinkage void sb1_cache_error(void) 130{ 131 uint64_t cerr_dpa; 132 uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res; 133 134 prom_printf("Cache error exception on CPU %x:\n", 135 (read_c0_prid() >> 25) & 0x7); 136 137 __asm__ __volatile__ ( 138 " .set push\n\t" 139 " .set mips64\n\t" 140 " .set noat\n\t" 141 " mfc0 %0, $26\n\t" 142 " mfc0 %1, $27\n\t" 143 " mfc0 %2, $27, 1\n\t" 144 " dmfc0 $1, $27, 3\n\t" 145 " dsrl32 %3, $1, 0 \n\t" 146 " sll %4, $1, 0 \n\t" 147 " mfc0 %5, $30\n\t" 148 " .set pop" 149 : "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d), 150 "=r" (dpahi), "=r" (dpalo), "=r" (eepc)); 151 152 cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo; 153 prom_printf(" c0_errorepc == %08x\n", eepc); 154 prom_printf(" c0_errctl == %08x", errctl); 155 breakout_errctl(errctl); 156 if (errctl & CP0_ERRCTL_ICACHE) { 157 prom_printf(" c0_cerr_i == %08x", cerr_i); 158 breakout_cerri(cerr_i); 159 if (CP0_CERRI_IDX_VALID(cerr_i)) { 160 if ((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) 161 prom_printf(" cerr_i idx doesn't match eepc\n"); 162 else { 163 res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK, 164 (cerr_i & CP0_CERRI_DATA) != 0); 165 if (!(res & cerr_i)) 166 prom_printf("...didn't see indicated icache problem\n"); 167 } 168 } 169 } 170 if (errctl & CP0_ERRCTL_DCACHE) { 171 prom_printf(" c0_cerr_d == %08x", cerr_d); 172 breakout_cerrd(cerr_d); 173 if (CP0_CERRD_DPA_VALID(cerr_d)) { 174 prom_printf(" c0_cerr_dpa == %010llx\n", cerr_dpa); 175 if (!CP0_CERRD_IDX_VALID(cerr_d)) { 176 res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK, 177 (cerr_d & CP0_CERRD_DATA) != 0); 178 if (!(res & cerr_d)) 179 prom_printf("...didn't see indicated dcache problem\n"); 180 } else { 181 if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK)) 182 prom_printf(" cerr_d idx doesn't match cerr_dpa\n"); 183 else { 184 res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK, 185 (cerr_d & CP0_CERRD_DATA) != 0); 186 if (!(res & cerr_d)) 187 prom_printf("...didn't see indicated problem\n"); 188 } 189 } 190 } 191 } 192 193 while (1); 194 /* 195 * This tends to make things get really ugly; let's just stall instead. 196 * panic("Can't handle the cache error!"); 197 */ 198} 199 200 201/* Parity lookup table. */ 202static const uint8_t parity[256] = { 203 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 204 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 205 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 206 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 207 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 208 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 209 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 210 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 211}; 212 213/* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */ 214static const uint64_t mask_72_64[8] = { 215 0x0738C808099264FFL, 216 0x38C808099264FF07L, 217 0xC808099264FF0738L, 218 0x08099264FF0738C8L, 219 0x099264FF0738C808L, 220 0x9264FF0738C80809L, 221 0x64FF0738C8080992L, 222 0xFF0738C808099264L 223}; 224 225/* Calculate the parity on a range of bits */ 226static char range_parity(uint64_t dword, int max, int min) 227{ 228 char parity = 0; 229 int i; 230 dword >>= min; 231 for (i=max-min; i>=0; i--) { 232 if (dword & 0x1) 233 parity = !parity; 234 dword >>= 1; 235 } 236 return parity; 237} 238 239/* Calculate the 4-bit even byte-parity for an instruction */ 240static unsigned char inst_parity(uint32_t word) 241{ 242 int i, j; 243 char parity = 0; 244 for (j=0; j<4; j++) { 245 char byte_parity = 0; 246 for (i=0; i<8; i++) { 247 if (word & 0x80000000) 248 byte_parity = !byte_parity; 249 word <<= 1; 250 } 251 parity <<= 1; 252 parity |= byte_parity; 253 } 254 return parity; 255} 256 257static uint32_t extract_ic(unsigned short addr, int data) 258{ 259 unsigned short way; 260 int valid; 261 uint64_t taglo, va, tlo_tmp; 262 uint32_t taghi, taglolo, taglohi; 263 uint8_t lru; 264 int res = 0; 265 266 prom_printf("Icache index 0x%04x ", addr); 267 for (way = 0; way < 4; way++) { 268 /* Index-load-tag-I */ 269 __asm__ __volatile__ ( 270 " .set push \n\t" 271 " .set noreorder \n\t" 272 " .set mips64 \n\t" 273 " .set noat \n\t" 274 " cache 4, 0(%3) \n\t" 275 " mfc0 %0, $29 \n\t" 276 " dmfc0 $1, $28 \n\t" 277 " dsrl32 %1, $1, 0 \n\t" 278 " sll %2, $1, 0 \n\t" 279 " .set pop" 280 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) 281 : "r" ((way << 13) | addr)); 282 283 taglo = ((unsigned long long)taglohi << 32) | taglolo; 284 if (way == 0) { 285 lru = (taghi >> 14) & 0xff; 286 prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", 287 ((addr >> 5) & 0x3), /* bank */ 288 ((addr >> 7) & 0x3f), /* index */ 289 (lru & 0x3), 290 ((lru >> 2) & 0x3), 291 ((lru >> 4) & 0x3), 292 ((lru >> 6) & 0x3)); 293 } 294 va = (taglo & 0xC0000FFFFFFFE000) | addr; 295 if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3)) 296 va |= 0x3FFFF00000000000; 297 valid = ((taghi >> 29) & 1); 298 if (valid) { 299 tlo_tmp = taglo & 0xfff3ff; 300 if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) { 301 prom_printf(" ** bad parity in VTag0/G/ASID\n"); 302 res |= CP0_CERRI_TAG_PARITY; 303 } 304 if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) { 305 prom_printf(" ** bad parity in R/VTag1\n"); 306 res |= CP0_CERRI_TAG_PARITY; 307 } 308 } 309 if (valid ^ ((taghi >> 27) & 1)) { 310 prom_printf(" ** bad parity for valid bit\n"); 311 res |= CP0_CERRI_TAG_PARITY; 312 } 313 prom_printf(" %d [VA %016llx] [Vld? %d] raw tags: %08X-%016llX\n", 314 way, va, valid, taghi, taglo); 315 316 if (data) { 317 uint32_t datahi, insta, instb; 318 uint8_t predecode; 319 int offset; 320 321 /* (hit all banks and ways) */ 322 for (offset = 0; offset < 4; offset++) { 323 /* Index-load-data-I */ 324 __asm__ __volatile__ ( 325 " .set push\n\t" 326 " .set noreorder\n\t" 327 " .set mips64\n\t" 328 " .set noat\n\t" 329 " cache 6, 0(%3) \n\t" 330 " mfc0 %0, $29, 1\n\t" 331 " dmfc0 $1, $28, 1\n\t" 332 " dsrl32 %1, $1, 0 \n\t" 333 " sll %2, $1, 0 \n\t" 334 " .set pop \n" 335 : "=r" (datahi), "=r" (insta), "=r" (instb) 336 : "r" ((way << 13) | addr | (offset << 3))); 337 predecode = (datahi >> 8) & 0xff; 338 if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) { 339 prom_printf(" ** bad parity in predecode\n"); 340 res |= CP0_CERRI_DATA_PARITY; 341 } 342 /* XXXKW should/could check predecode bits themselves */ 343 if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) { 344 prom_printf(" ** bad parity in instruction a\n"); 345 res |= CP0_CERRI_DATA_PARITY; 346 } 347 if ((datahi & 0xf) ^ inst_parity(instb)) { 348 prom_printf(" ** bad parity in instruction b\n"); 349 res |= CP0_CERRI_DATA_PARITY; 350 } 351 prom_printf(" %05X-%08X%08X", datahi, insta, instb); 352 } 353 prom_printf("\n"); 354 } 355 } 356 return res; 357} 358 359/* Compute the ECC for a data doubleword */ 360static uint8_t dc_ecc(uint64_t dword) 361{ 362 uint64_t t; 363 uint32_t w; 364 uint8_t p; 365 int i; 366 367 p = 0; 368 for (i = 7; i >= 0; i--) 369 { 370 p <<= 1; 371 t = dword & mask_72_64[i]; 372 w = (uint32_t)(t >> 32); 373 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] 374 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); 375 w = (uint32_t)(t & 0xFFFFFFFF); 376 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] 377 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); 378 } 379 return p; 380} 381 382struct dc_state { 383 unsigned char val; 384 char *name; 385}; 386 387static struct dc_state dc_states[] = { 388 { 0x00, "INVALID" }, 389 { 0x0f, "COH-SHD" }, 390 { 0x13, "NCO-E-C" }, 391 { 0x19, "NCO-E-D" }, 392 { 0x16, "COH-E-C" }, 393 { 0x1c, "COH-E-D" }, 394 { 0xff, "*ERROR*" } 395}; 396 397#define DC_TAG_VALID(state) \ 398 (((state) == 0xf) || ((state) == 0x13) || ((state) == 0x19) || ((state == 0x16)) || ((state) == 0x1c)) 399 400static char *dc_state_str(unsigned char state) 401{ 402 struct dc_state *dsc = dc_states; 403 while (dsc->val != 0xff) { 404 if (dsc->val == state) 405 break; 406 dsc++; 407 } 408 return dsc->name; 409} 410 411static uint32_t extract_dc(unsigned short addr, int data) 412{ 413 int valid, way; 414 unsigned char state; 415 uint64_t taglo, pa; 416 uint32_t taghi, taglolo, taglohi; 417 uint8_t ecc, lru; 418 int res = 0; 419 420 prom_printf("Dcache index 0x%04x ", addr); 421 for (way = 0; way < 4; way++) { 422 __asm__ __volatile__ ( 423 " .set push\n\t" 424 " .set noreorder\n\t" 425 " .set mips64\n\t" 426 " .set noat\n\t" 427 " cache 5, 0(%3)\n\t" /* Index-load-tag-D */ 428 " mfc0 %0, $29, 2\n\t" 429 " dmfc0 $1, $28, 2\n\t" 430 " dsrl32 %1, $1, 0\n\t" 431 " sll %2, $1, 0\n\t" 432 " .set pop" 433 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) 434 : "r" ((way << 13) | addr)); 435 436 taglo = ((unsigned long long)taglohi << 32) | taglolo; 437 pa = (taglo & 0xFFFFFFE000) | addr; 438 if (way == 0) { 439 lru = (taghi >> 14) & 0xff; 440 prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", 441 ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */ 442 ((addr >> 6) & 0x3f), /* index */ 443 (lru & 0x3), 444 ((lru >> 2) & 0x3), 445 ((lru >> 4) & 0x3), 446 ((lru >> 6) & 0x3)); 447 } 448 state = (taghi >> 25) & 0x1f; 449 valid = DC_TAG_VALID(state); 450 prom_printf(" %d [PA %010llx] [state %s (%02x)] raw tags: %08X-%016llX\n", 451 way, pa, dc_state_str(state), state, taghi, taglo); 452 if (valid) { 453 if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) { 454 prom_printf(" ** bad parity in PTag1\n"); 455 res |= CP0_CERRD_TAG_ADDRESS; 456 } 457 if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) { 458 prom_printf(" ** bad parity in PTag0\n"); 459 res |= CP0_CERRD_TAG_ADDRESS; 460 } 461 } else { 462 res |= CP0_CERRD_TAG_STATE; 463 } 464 465 if (data) { 466 uint64_t datalo; 467 uint32_t datalohi, datalolo, datahi; 468 int offset; 469 470 for (offset = 0; offset < 4; offset++) { 471 /* Index-load-data-D */ 472 __asm__ __volatile__ ( 473 " .set push\n\t" 474 " .set noreorder\n\t" 475 " .set mips64\n\t" 476 " .set noat\n\t" 477 " cache 7, 0(%3)\n\t" /* Index-load-data-D */ 478 " mfc0 %0, $29, 3\n\t" 479 " dmfc0 $1, $28, 3\n\t" 480 " dsrl32 %1, $1, 0 \n\t" 481 " sll %2, $1, 0 \n\t" 482 " .set pop" 483 : "=r" (datahi), "=r" (datalohi), "=r" (datalolo) 484 : "r" ((way << 13) | addr | (offset << 3))); 485 datalo = ((unsigned long long)datalohi << 32) | datalolo; 486 ecc = dc_ecc(datalo); 487 if (ecc != datahi) { 488 int bits = 0; 489 prom_printf(" ** bad ECC (%02x %02x) ->", 490 datahi, ecc); 491 ecc ^= datahi; 492 while (ecc) { 493 if (ecc & 1) bits++; 494 ecc >>= 1; 495 } 496 res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE; 497 } 498 prom_printf(" %02X-%016llX", datahi, datalo); 499 } 500 prom_printf("\n"); 501 } 502 } 503 return res; 504} 505