diff options
author | Andrew Isaacson <adi@broadcom.com> | 2005-10-20 02:57:40 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2005-10-29 14:32:48 -0400 |
commit | a4b5bd9abcf5b0586de68722ff8e9b91020279bf (patch) | |
tree | fe0f4014bf5e92d72d4e15d90cf3eab3713ceee5 /arch/mips/mm/cerr-sb1.c | |
parent | 9a6dcea10308df50ed54d6d5a43c9f6c3e927118 (diff) |
SB1 cache exception handling.
Expand SB1 cache error handling by adding SB1_CEX_ALWAYS_FATAL and
SB1_CEX_STALL, allowing configurable behavior on cache errors.
Signed-Off-By: Andy Isaacson <adi@broadcom.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm/cerr-sb1.c')
-rw-r--r-- | arch/mips/mm/cerr-sb1.c | 54 |
1 files changed, 46 insertions, 8 deletions
diff --git a/arch/mips/mm/cerr-sb1.c b/arch/mips/mm/cerr-sb1.c index 7166ffe63502..1cf3c6006ccd 100644 --- a/arch/mips/mm/cerr-sb1.c +++ b/arch/mips/mm/cerr-sb1.c | |||
@@ -19,13 +19,19 @@ | |||
19 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
20 | #include <asm/mipsregs.h> | 20 | #include <asm/mipsregs.h> |
21 | #include <asm/sibyte/sb1250.h> | 21 | #include <asm/sibyte/sb1250.h> |
22 | #include <asm/sibyte/sb1250_regs.h> | ||
22 | 23 | ||
23 | #ifndef CONFIG_SIBYTE_BUS_WATCHER | 24 | #if !defined(CONFIG_SIBYTE_BUS_WATCHER) || defined(CONFIG_SIBYTE_BW_TRACE) |
24 | #include <asm/io.h> | 25 | #include <asm/io.h> |
25 | #include <asm/sibyte/sb1250_regs.h> | ||
26 | #include <asm/sibyte/sb1250_scd.h> | 26 | #include <asm/sibyte/sb1250_scd.h> |
27 | #endif | 27 | #endif |
28 | 28 | ||
29 | /* | ||
30 | * We'd like to dump the L2_ECC_TAG register on errors, but errata make | ||
31 | * that unsafe... So for now we don't. (BCM1250/BCM112x erratum SOC-48.) | ||
32 | */ | ||
33 | #undef DUMP_L2_ECC_TAG_ON_ERROR | ||
34 | |||
29 | /* SB1 definitions */ | 35 | /* SB1 definitions */ |
30 | 36 | ||
31 | /* XXX should come from config1 XXX */ | 37 | /* XXX should come from config1 XXX */ |
@@ -139,12 +145,18 @@ static inline void breakout_cerrd(unsigned int val) | |||
139 | static void check_bus_watcher(void) | 145 | static void check_bus_watcher(void) |
140 | { | 146 | { |
141 | uint32_t status, l2_err, memio_err; | 147 | uint32_t status, l2_err, memio_err; |
148 | #ifdef DUMP_L2_ECC_TAG_ON_ERROR | ||
149 | uint64_t l2_tag; | ||
150 | #endif | ||
142 | 151 | ||
143 | /* Destructive read, clears register and interrupt */ | 152 | /* Destructive read, clears register and interrupt */ |
144 | status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS)); | 153 | status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS)); |
145 | /* Bit 31 is always on, but there's no #define for that */ | 154 | /* Bit 31 is always on, but there's no #define for that */ |
146 | if (status & ~(1UL << 31)) { | 155 | if (status & ~(1UL << 31)) { |
147 | l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS)); | 156 | l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS)); |
157 | #ifdef DUMP_L2_ECC_TAG_ON_ERROR | ||
158 | l2_tag = in64(IO_SPACE_BASE | A_L2_ECC_TAG); | ||
159 | #endif | ||
148 | memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS)); | 160 | memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS)); |
149 | prom_printf("Bus watcher error counters: %08x %08x\n", l2_err, memio_err); | 161 | prom_printf("Bus watcher error counters: %08x %08x\n", l2_err, memio_err); |
150 | prom_printf("\nLast recorded signature:\n"); | 162 | prom_printf("\nLast recorded signature:\n"); |
@@ -153,6 +165,9 @@ static void check_bus_watcher(void) | |||
153 | (int)(G_SCD_BERR_TID(status) >> 6), | 165 | (int)(G_SCD_BERR_TID(status) >> 6), |
154 | (int)G_SCD_BERR_RID(status), | 166 | (int)G_SCD_BERR_RID(status), |
155 | (int)G_SCD_BERR_DCODE(status)); | 167 | (int)G_SCD_BERR_DCODE(status)); |
168 | #ifdef DUMP_L2_ECC_TAG_ON_ERROR | ||
169 | prom_printf("Last L2 tag w/ bad ECC: %016llx\n", l2_tag); | ||
170 | #endif | ||
156 | } else { | 171 | } else { |
157 | prom_printf("Bus watcher indicates no error\n"); | 172 | prom_printf("Bus watcher indicates no error\n"); |
158 | } | 173 | } |
@@ -166,6 +181,16 @@ asmlinkage void sb1_cache_error(void) | |||
166 | uint64_t cerr_dpa; | 181 | uint64_t cerr_dpa; |
167 | uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res; | 182 | uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res; |
168 | 183 | ||
184 | #ifdef CONFIG_SIBYTE_BW_TRACE | ||
185 | /* Freeze the trace buffer now */ | ||
186 | #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) | ||
187 | csr_out32(M_BCM1480_SCD_TRACE_CFG_FREEZE, IO_SPACE_BASE | A_SCD_TRACE_CFG); | ||
188 | #else | ||
189 | csr_out32(M_SCD_TRACE_CFG_FREEZE, IO_SPACE_BASE | A_SCD_TRACE_CFG); | ||
190 | #endif | ||
191 | prom_printf("Trace buffer frozen\n"); | ||
192 | #endif | ||
193 | |||
169 | prom_printf("Cache error exception on CPU %x:\n", | 194 | prom_printf("Cache error exception on CPU %x:\n", |
170 | (read_c0_prid() >> 25) & 0x7); | 195 | (read_c0_prid() >> 25) & 0x7); |
171 | 196 | ||
@@ -229,11 +254,19 @@ asmlinkage void sb1_cache_error(void) | |||
229 | 254 | ||
230 | check_bus_watcher(); | 255 | check_bus_watcher(); |
231 | 256 | ||
232 | while (1); | ||
233 | /* | 257 | /* |
234 | * This tends to make things get really ugly; let's just stall instead. | 258 | * Calling panic() when a fatal cache error occurs scrambles the |
235 | * panic("Can't handle the cache error!"); | 259 | * state of the system (and the cache), making it difficult to |
260 | * investigate after the fact. However, if you just stall the CPU, | ||
261 | * the other CPU may keep on running, which is typically very | ||
262 | * undesirable. | ||
236 | */ | 263 | */ |
264 | #ifdef CONFIG_SB1_CERR_STALL | ||
265 | while (1) | ||
266 | ; | ||
267 | #else | ||
268 | panic("unhandled cache error"); | ||
269 | #endif | ||
237 | } | 270 | } |
238 | 271 | ||
239 | 272 | ||
@@ -434,7 +467,8 @@ static struct dc_state dc_states[] = { | |||
434 | }; | 467 | }; |
435 | 468 | ||
436 | #define DC_TAG_VALID(state) \ | 469 | #define DC_TAG_VALID(state) \ |
437 | (((state) == 0xf) || ((state) == 0x13) || ((state) == 0x19) || ((state == 0x16)) || ((state) == 0x1c)) | 470 | (((state) == 0x0) || ((state) == 0xf) || ((state) == 0x13) || \ |
471 | ((state) == 0x19) || ((state) == 0x16) || ((state) == 0x1c)) | ||
438 | 472 | ||
439 | static char *dc_state_str(unsigned char state) | 473 | static char *dc_state_str(unsigned char state) |
440 | { | 474 | { |
@@ -505,6 +539,7 @@ static uint32_t extract_dc(unsigned short addr, int data) | |||
505 | uint64_t datalo; | 539 | uint64_t datalo; |
506 | uint32_t datalohi, datalolo, datahi; | 540 | uint32_t datalohi, datalolo, datahi; |
507 | int offset; | 541 | int offset; |
542 | char bad_ecc = 0; | ||
508 | 543 | ||
509 | for (offset = 0; offset < 4; offset++) { | 544 | for (offset = 0; offset < 4; offset++) { |
510 | /* Index-load-data-D */ | 545 | /* Index-load-data-D */ |
@@ -525,8 +560,7 @@ static uint32_t extract_dc(unsigned short addr, int data) | |||
525 | ecc = dc_ecc(datalo); | 560 | ecc = dc_ecc(datalo); |
526 | if (ecc != datahi) { | 561 | if (ecc != datahi) { |
527 | int bits = 0; | 562 | int bits = 0; |
528 | prom_printf(" ** bad ECC (%02x %02x) ->", | 563 | bad_ecc |= 1 << (3-offset); |
529 | datahi, ecc); | ||
530 | ecc ^= datahi; | 564 | ecc ^= datahi; |
531 | while (ecc) { | 565 | while (ecc) { |
532 | if (ecc & 1) bits++; | 566 | if (ecc & 1) bits++; |
@@ -537,6 +571,10 @@ static uint32_t extract_dc(unsigned short addr, int data) | |||
537 | prom_printf(" %02X-%016llX", datahi, datalo); | 571 | prom_printf(" %02X-%016llX", datahi, datalo); |
538 | } | 572 | } |
539 | prom_printf("\n"); | 573 | prom_printf("\n"); |
574 | if (bad_ecc) | ||
575 | prom_printf(" dwords w/ bad ECC: %d %d %d %d\n", | ||
576 | !!(bad_ecc & 8), !!(bad_ecc & 4), | ||
577 | !!(bad_ecc & 2), !!(bad_ecc & 1)); | ||
540 | } | 578 | } |
541 | } | 579 | } |
542 | return res; | 580 | return res; |