blob: ee5c1ff861ae2e4d4766666ee1867c44add0eda1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001,2002,2003 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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/sched.h>
19#include <asm/mipsregs.h>
20#include <asm/sibyte/sb1250.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/sibyte/sb1250_regs.h>
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -070022
23#if !defined(CONFIG_SIBYTE_BUS_WATCHER) || defined(CONFIG_SIBYTE_BW_TRACE)
24#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <asm/sibyte/sb1250_scd.h>
26#endif
Ralf Baechle42a3b4f2005-09-03 15:56:17 -070027
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -070028/*
29 * We'd like to dump the L2_ECC_TAG register on errors, but errata make
Ralf Baechle70342282013-01-22 12:59:30 +010030 * that unsafe... So for now we don't. (BCM1250/BCM112x erratum SOC-48.)
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -070031 */
32#undef DUMP_L2_ECC_TAG_ON_ERROR
33
Linus Torvalds1da177e2005-04-16 15:20:36 -070034/* SB1 definitions */
35
36/* XXX should come from config1 XXX */
37#define SB1_CACHE_INDEX_MASK 0x1fe0
38
39#define CP0_ERRCTL_RECOVERABLE (1 << 31)
40#define CP0_ERRCTL_DCACHE (1 << 30)
41#define CP0_ERRCTL_ICACHE (1 << 29)
42#define CP0_ERRCTL_MULTIBUS (1 << 23)
43#define CP0_ERRCTL_MC_TLB (1 << 15)
44#define CP0_ERRCTL_MC_TIMEOUT (1 << 14)
45
46#define CP0_CERRI_TAG_PARITY (1 << 29)
47#define CP0_CERRI_DATA_PARITY (1 << 28)
48#define CP0_CERRI_EXTERNAL (1 << 26)
49
50#define CP0_CERRI_IDX_VALID(c) (!((c) & CP0_CERRI_EXTERNAL))
Ralf Baechle70342282013-01-22 12:59:30 +010051#define CP0_CERRI_DATA (CP0_CERRI_DATA_PARITY)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53#define CP0_CERRD_MULTIPLE (1 << 31)
54#define CP0_CERRD_TAG_STATE (1 << 30)
55#define CP0_CERRD_TAG_ADDRESS (1 << 29)
56#define CP0_CERRD_DATA_SBE (1 << 28)
57#define CP0_CERRD_DATA_DBE (1 << 27)
58#define CP0_CERRD_EXTERNAL (1 << 26)
Ralf Baechle70342282013-01-22 12:59:30 +010059#define CP0_CERRD_LOAD (1 << 25)
60#define CP0_CERRD_STORE (1 << 24)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define CP0_CERRD_FILLWB (1 << 23)
62#define CP0_CERRD_COHERENCY (1 << 22)
63#define CP0_CERRD_DUPTAG (1 << 21)
64
65#define CP0_CERRD_DPA_VALID(c) (!((c) & CP0_CERRD_EXTERNAL))
66#define CP0_CERRD_IDX_VALID(c) \
67 (((c) & (CP0_CERRD_LOAD | CP0_CERRD_STORE)) ? (!((c) & CP0_CERRD_EXTERNAL)) : 0)
68#define CP0_CERRD_CAUSES \
69 (CP0_CERRD_LOAD | CP0_CERRD_STORE | CP0_CERRD_FILLWB | CP0_CERRD_COHERENCY | CP0_CERRD_DUPTAG)
70#define CP0_CERRD_TYPES \
71 (CP0_CERRD_TAG_STATE | CP0_CERRD_TAG_ADDRESS | CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE | CP0_CERRD_EXTERNAL)
Ralf Baechle70342282013-01-22 12:59:30 +010072#define CP0_CERRD_DATA (CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Ralf Baechle70342282013-01-22 12:59:30 +010074static uint32_t extract_ic(unsigned short addr, int data);
75static uint32_t extract_dc(unsigned short addr, int data);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77static inline void breakout_errctl(unsigned int val)
78{
79 if (val & CP0_ERRCTL_RECOVERABLE)
Ralf Baechle36a88532007-03-01 11:56:43 +000080 printk(" recoverable");
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 if (val & CP0_ERRCTL_DCACHE)
Ralf Baechle36a88532007-03-01 11:56:43 +000082 printk(" dcache");
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 if (val & CP0_ERRCTL_ICACHE)
Ralf Baechle36a88532007-03-01 11:56:43 +000084 printk(" icache");
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 if (val & CP0_ERRCTL_MULTIBUS)
Ralf Baechle36a88532007-03-01 11:56:43 +000086 printk(" multiple-buserr");
87 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
90static inline void breakout_cerri(unsigned int val)
91{
92 if (val & CP0_CERRI_TAG_PARITY)
Ralf Baechle36a88532007-03-01 11:56:43 +000093 printk(" tag-parity");
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 if (val & CP0_CERRI_DATA_PARITY)
Ralf Baechle36a88532007-03-01 11:56:43 +000095 printk(" data-parity");
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 if (val & CP0_CERRI_EXTERNAL)
Ralf Baechle36a88532007-03-01 11:56:43 +000097 printk(" external");
98 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070099}
100
101static inline void breakout_cerrd(unsigned int val)
102{
103 switch (val & CP0_CERRD_CAUSES) {
104 case CP0_CERRD_LOAD:
Ralf Baechle36a88532007-03-01 11:56:43 +0000105 printk(" load,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 break;
107 case CP0_CERRD_STORE:
Ralf Baechle36a88532007-03-01 11:56:43 +0000108 printk(" store,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 break;
110 case CP0_CERRD_FILLWB:
Ralf Baechle36a88532007-03-01 11:56:43 +0000111 printk(" fill/wb,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 break;
113 case CP0_CERRD_COHERENCY:
Ralf Baechle36a88532007-03-01 11:56:43 +0000114 printk(" coherency,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 break;
116 case CP0_CERRD_DUPTAG:
Ralf Baechle36a88532007-03-01 11:56:43 +0000117 printk(" duptags,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 break;
119 default:
Ralf Baechle36a88532007-03-01 11:56:43 +0000120 printk(" NO CAUSE,");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 break;
122 }
123 if (!(val & CP0_CERRD_TYPES))
Ralf Baechle36a88532007-03-01 11:56:43 +0000124 printk(" NO TYPE");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 else {
126 if (val & CP0_CERRD_MULTIPLE)
Ralf Baechle36a88532007-03-01 11:56:43 +0000127 printk(" multi-err");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 if (val & CP0_CERRD_TAG_STATE)
Ralf Baechle36a88532007-03-01 11:56:43 +0000129 printk(" tag-state");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 if (val & CP0_CERRD_TAG_ADDRESS)
Ralf Baechle36a88532007-03-01 11:56:43 +0000131 printk(" tag-address");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 if (val & CP0_CERRD_DATA_SBE)
Ralf Baechle36a88532007-03-01 11:56:43 +0000133 printk(" data-SBE");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 if (val & CP0_CERRD_DATA_DBE)
Ralf Baechle36a88532007-03-01 11:56:43 +0000135 printk(" data-DBE");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 if (val & CP0_CERRD_EXTERNAL)
Ralf Baechle36a88532007-03-01 11:56:43 +0000137 printk(" external");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000139 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
142#ifndef CONFIG_SIBYTE_BUS_WATCHER
143
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700144static void check_bus_watcher(void)
145{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 uint32_t status, l2_err, memio_err;
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700147#ifdef DUMP_L2_ECC_TAG_ON_ERROR
148 uint64_t l2_tag;
149#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151 /* Destructive read, clears register and interrupt */
152 status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
153 /* Bit 31 is always on, but there's no #define for that */
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700154 if (status & ~(1UL << 31)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700156#ifdef DUMP_L2_ECC_TAG_ON_ERROR
Ralf Baechle33b75e5c2007-11-06 00:43:51 +0000157 l2_tag = in64(IOADDR(A_L2_ECC_TAG));
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700158#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
Ralf Baechle36a88532007-03-01 11:56:43 +0000160 printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
161 printk("\nLast recorded signature:\n");
162 printk("Request %02x from %d, answered by %d with Dcode %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 (unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
164 (int)(G_SCD_BERR_TID(status) >> 6),
165 (int)G_SCD_BERR_RID(status),
166 (int)G_SCD_BERR_DCODE(status));
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700167#ifdef DUMP_L2_ECC_TAG_ON_ERROR
Ralf Baechle36a88532007-03-01 11:56:43 +0000168 printk("Last L2 tag w/ bad ECC: %016llx\n", l2_tag);
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700169#endif
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700170 } else {
Ralf Baechle36a88532007-03-01 11:56:43 +0000171 printk("Bus watcher indicates no error\n");
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700172 }
173}
174#else
175extern void check_bus_watcher(void);
176#endif
177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178asmlinkage void sb1_cache_error(void)
179{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res;
Ralf Baechle41a81982007-03-24 14:09:59 +0000181 unsigned long long cerr_dpa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700183#ifdef CONFIG_SIBYTE_BW_TRACE
184 /* Freeze the trace buffer now */
Ralf Baechle33b75e5c2007-11-06 00:43:51 +0000185 csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
Ralf Baechle36a88532007-03-01 11:56:43 +0000186 printk("Trace buffer frozen\n");
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700187#endif
188
Ralf Baechle36a88532007-03-01 11:56:43 +0000189 printk("Cache error exception on CPU %x:\n",
190 (read_c0_prid() >> 25) & 0x7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192 __asm__ __volatile__ (
193 " .set push\n\t"
194 " .set mips64\n\t"
195 " .set noat\n\t"
196 " mfc0 %0, $26\n\t"
197 " mfc0 %1, $27\n\t"
198 " mfc0 %2, $27, 1\n\t"
199 " dmfc0 $1, $27, 3\n\t"
200 " dsrl32 %3, $1, 0 \n\t"
201 " sll %4, $1, 0 \n\t"
202 " mfc0 %5, $30\n\t"
203 " .set pop"
204 : "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d),
205 "=r" (dpahi), "=r" (dpalo), "=r" (eepc));
206
207 cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo;
Ralf Baechle70342282013-01-22 12:59:30 +0100208 printk(" c0_errorepc == %08x\n", eepc);
209 printk(" c0_errctl == %08x", errctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 breakout_errctl(errctl);
211 if (errctl & CP0_ERRCTL_ICACHE) {
Ralf Baechle70342282013-01-22 12:59:30 +0100212 printk(" c0_cerr_i == %08x", cerr_i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 breakout_cerri(cerr_i);
214 if (CP0_CERRI_IDX_VALID(cerr_i)) {
215 /* Check index of EPC, allowing for delay slot */
216 if (((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) &&
217 ((eepc & SB1_CACHE_INDEX_MASK) != ((cerr_i & SB1_CACHE_INDEX_MASK) - 4)))
Ralf Baechle36a88532007-03-01 11:56:43 +0000218 printk(" cerr_i idx doesn't match eepc\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 else {
220 res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK,
221 (cerr_i & CP0_CERRI_DATA) != 0);
222 if (!(res & cerr_i))
Ralf Baechle36a88532007-03-01 11:56:43 +0000223 printk("...didn't see indicated icache problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 }
225 }
226 }
227 if (errctl & CP0_ERRCTL_DCACHE) {
Ralf Baechle70342282013-01-22 12:59:30 +0100228 printk(" c0_cerr_d == %08x", cerr_d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 breakout_cerrd(cerr_d);
230 if (CP0_CERRD_DPA_VALID(cerr_d)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000231 printk(" c0_cerr_dpa == %010llx\n", cerr_dpa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 if (!CP0_CERRD_IDX_VALID(cerr_d)) {
233 res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK,
234 (cerr_d & CP0_CERRD_DATA) != 0);
235 if (!(res & cerr_d))
Ralf Baechle36a88532007-03-01 11:56:43 +0000236 printk("...didn't see indicated dcache problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 } else {
238 if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK))
Ralf Baechle36a88532007-03-01 11:56:43 +0000239 printk(" cerr_d idx doesn't match cerr_dpa\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 else {
241 res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK,
242 (cerr_d & CP0_CERRD_DATA) != 0);
243 if (!(res & cerr_d))
Ralf Baechle36a88532007-03-01 11:56:43 +0000244 printk("...didn't see indicated problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 }
246 }
247 }
248 }
249
250 check_bus_watcher();
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 /*
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700253 * Calling panic() when a fatal cache error occurs scrambles the
254 * state of the system (and the cache), making it difficult to
Ralf Baechle70342282013-01-22 12:59:30 +0100255 * investigate after the fact. However, if you just stall the CPU,
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700256 * the other CPU may keep on running, which is typically very
257 * undesirable.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 */
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700259#ifdef CONFIG_SB1_CERR_STALL
260 while (1)
261 ;
262#else
263 panic("unhandled cache error");
264#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266
267
268/* Parity lookup table. */
269static const uint8_t parity[256] = {
Ralf Baechle21a151d2007-10-11 23:46:15 +0100270 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
271 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
272 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
273 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
274 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
275 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
276 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
277 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
278 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
279 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
280 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
281 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
282 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
283 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
284 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
285 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286};
287
288/* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */
289static const uint64_t mask_72_64[8] = {
290 0x0738C808099264FFULL,
291 0x38C808099264FF07ULL,
292 0xC808099264FF0738ULL,
293 0x08099264FF0738C8ULL,
294 0x099264FF0738C808ULL,
295 0x9264FF0738C80809ULL,
296 0x64FF0738C8080992ULL,
297 0xFF0738C808099264ULL
298};
299
300/* Calculate the parity on a range of bits */
301static char range_parity(uint64_t dword, int max, int min)
302{
303 char parity = 0;
304 int i;
305 dword >>= min;
306 for (i=max-min; i>=0; i--) {
307 if (dword & 0x1)
308 parity = !parity;
309 dword >>= 1;
310 }
311 return parity;
312}
313
314/* Calculate the 4-bit even byte-parity for an instruction */
315static unsigned char inst_parity(uint32_t word)
316{
317 int i, j;
318 char parity = 0;
319 for (j=0; j<4; j++) {
320 char byte_parity = 0;
321 for (i=0; i<8; i++) {
322 if (word & 0x80000000)
323 byte_parity = !byte_parity;
324 word <<= 1;
325 }
326 parity <<= 1;
327 parity |= byte_parity;
328 }
329 return parity;
330}
331
332static uint32_t extract_ic(unsigned short addr, int data)
333{
334 unsigned short way;
335 int valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 uint32_t taghi, taglolo, taglohi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000337 unsigned long long taglo, va;
338 uint64_t tlo_tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 uint8_t lru;
340 int res = 0;
341
Ralf Baechle36a88532007-03-01 11:56:43 +0000342 printk("Icache index 0x%04x ", addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 for (way = 0; way < 4; way++) {
344 /* Index-load-tag-I */
345 __asm__ __volatile__ (
346 " .set push \n\t"
347 " .set noreorder \n\t"
348 " .set mips64 \n\t"
349 " .set noat \n\t"
350 " cache 4, 0(%3) \n\t"
351 " mfc0 %0, $29 \n\t"
352 " dmfc0 $1, $28 \n\t"
353 " dsrl32 %1, $1, 0 \n\t"
354 " sll %2, $1, 0 \n\t"
355 " .set pop"
356 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
357 : "r" ((way << 13) | addr));
358
359 taglo = ((unsigned long long)taglohi << 32) | taglolo;
360 if (way == 0) {
361 lru = (taghi >> 14) & 0xff;
Ralf Baechle36a88532007-03-01 11:56:43 +0000362 printk("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 ((addr >> 5) & 0x3), /* bank */
364 ((addr >> 7) & 0x3f), /* index */
365 (lru & 0x3),
366 ((lru >> 2) & 0x3),
367 ((lru >> 4) & 0x3),
368 ((lru >> 6) & 0x3));
369 }
370 va = (taglo & 0xC0000FFFFFFFE000ULL) | addr;
371 if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3))
372 va |= 0x3FFFF00000000000ULL;
373 valid = ((taghi >> 29) & 1);
374 if (valid) {
375 tlo_tmp = taglo & 0xfff3ff;
376 if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000377 printk(" ** bad parity in VTag0/G/ASID\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 res |= CP0_CERRI_TAG_PARITY;
379 }
380 if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000381 printk(" ** bad parity in R/VTag1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 res |= CP0_CERRI_TAG_PARITY;
383 }
384 }
385 if (valid ^ ((taghi >> 27) & 1)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000386 printk(" ** bad parity for valid bit\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 res |= CP0_CERRI_TAG_PARITY;
388 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000389 printk(" %d [VA %016llx] [Vld? %d] raw tags: %08X-%016llX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 way, va, valid, taghi, taglo);
391
392 if (data) {
393 uint32_t datahi, insta, instb;
394 uint8_t predecode;
395 int offset;
396
397 /* (hit all banks and ways) */
398 for (offset = 0; offset < 4; offset++) {
399 /* Index-load-data-I */
400 __asm__ __volatile__ (
401 " .set push\n\t"
402 " .set noreorder\n\t"
403 " .set mips64\n\t"
404 " .set noat\n\t"
405 " cache 6, 0(%3) \n\t"
406 " mfc0 %0, $29, 1\n\t"
407 " dmfc0 $1, $28, 1\n\t"
408 " dsrl32 %1, $1, 0 \n\t"
409 " sll %2, $1, 0 \n\t"
Ralf Baechle70342282013-01-22 12:59:30 +0100410 " .set pop \n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 : "=r" (datahi), "=r" (insta), "=r" (instb)
412 : "r" ((way << 13) | addr | (offset << 3)));
413 predecode = (datahi >> 8) & 0xff;
414 if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000415 printk(" ** bad parity in predecode\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 res |= CP0_CERRI_DATA_PARITY;
417 }
418 /* XXXKW should/could check predecode bits themselves */
419 if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000420 printk(" ** bad parity in instruction a\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 res |= CP0_CERRI_DATA_PARITY;
422 }
423 if ((datahi & 0xf) ^ inst_parity(instb)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000424 printk(" ** bad parity in instruction b\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 res |= CP0_CERRI_DATA_PARITY;
426 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000427 printk(" %05X-%08X%08X", datahi, insta, instb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000429 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
431 }
432 return res;
433}
434
435/* Compute the ECC for a data doubleword */
436static uint8_t dc_ecc(uint64_t dword)
437{
438 uint64_t t;
439 uint32_t w;
Ralf Baechle70342282013-01-22 12:59:30 +0100440 uint8_t p;
441 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 p = 0;
444 for (i = 7; i >= 0; i--)
445 {
446 p <<= 1;
447 t = dword & mask_72_64[i];
448 w = (uint32_t)(t >> 32);
449 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
450 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
451 w = (uint32_t)(t & 0xFFFFFFFF);
452 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
453 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
454 }
455 return p;
456}
457
458struct dc_state {
459 unsigned char val;
460 char *name;
461};
462
463static struct dc_state dc_states[] = {
464 { 0x00, "INVALID" },
465 { 0x0f, "COH-SHD" },
466 { 0x13, "NCO-E-C" },
467 { 0x19, "NCO-E-D" },
468 { 0x16, "COH-E-C" },
469 { 0x1c, "COH-E-D" },
470 { 0xff, "*ERROR*" }
471};
472
473#define DC_TAG_VALID(state) \
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700474 (((state) == 0x0) || ((state) == 0xf) || ((state) == 0x13) || \
475 ((state) == 0x19) || ((state) == 0x16) || ((state) == 0x1c))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477static char *dc_state_str(unsigned char state)
478{
479 struct dc_state *dsc = dc_states;
480 while (dsc->val != 0xff) {
481 if (dsc->val == state)
482 break;
483 dsc++;
484 }
485 return dsc->name;
486}
487
488static uint32_t extract_dc(unsigned short addr, int data)
489{
490 int valid, way;
491 unsigned char state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 uint32_t taghi, taglolo, taglohi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000493 unsigned long long taglo, pa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 uint8_t ecc, lru;
495 int res = 0;
496
Ralf Baechle36a88532007-03-01 11:56:43 +0000497 printk("Dcache index 0x%04x ", addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 for (way = 0; way < 4; way++) {
499 __asm__ __volatile__ (
500 " .set push\n\t"
501 " .set noreorder\n\t"
502 " .set mips64\n\t"
503 " .set noat\n\t"
504 " cache 5, 0(%3)\n\t" /* Index-load-tag-D */
505 " mfc0 %0, $29, 2\n\t"
506 " dmfc0 $1, $28, 2\n\t"
507 " dsrl32 %1, $1, 0\n\t"
508 " sll %2, $1, 0\n\t"
509 " .set pop"
510 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
511 : "r" ((way << 13) | addr));
512
513 taglo = ((unsigned long long)taglohi << 32) | taglolo;
514 pa = (taglo & 0xFFFFFFE000ULL) | addr;
515 if (way == 0) {
516 lru = (taghi >> 14) & 0xff;
Ralf Baechle36a88532007-03-01 11:56:43 +0000517 printk("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */
519 ((addr >> 6) & 0x3f), /* index */
520 (lru & 0x3),
521 ((lru >> 2) & 0x3),
522 ((lru >> 4) & 0x3),
523 ((lru >> 6) & 0x3));
524 }
525 state = (taghi >> 25) & 0x1f;
526 valid = DC_TAG_VALID(state);
Ralf Baechle36a88532007-03-01 11:56:43 +0000527 printk(" %d [PA %010llx] [state %s (%02x)] raw tags: %08X-%016llX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 way, pa, dc_state_str(state), state, taghi, taglo);
529 if (valid) {
530 if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000531 printk(" ** bad parity in PTag1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 res |= CP0_CERRD_TAG_ADDRESS;
533 }
534 if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000535 printk(" ** bad parity in PTag0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 res |= CP0_CERRD_TAG_ADDRESS;
537 }
538 } else {
539 res |= CP0_CERRD_TAG_STATE;
540 }
541
542 if (data) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 uint32_t datalohi, datalolo, datahi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000544 unsigned long long datalo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 int offset;
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700546 char bad_ecc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 for (offset = 0; offset < 4; offset++) {
549 /* Index-load-data-D */
550 __asm__ __volatile__ (
551 " .set push\n\t"
552 " .set noreorder\n\t"
553 " .set mips64\n\t"
554 " .set noat\n\t"
555 " cache 7, 0(%3)\n\t" /* Index-load-data-D */
556 " mfc0 %0, $29, 3\n\t"
557 " dmfc0 $1, $28, 3\n\t"
558 " dsrl32 %1, $1, 0 \n\t"
559 " sll %2, $1, 0 \n\t"
560 " .set pop"
561 : "=r" (datahi), "=r" (datalohi), "=r" (datalolo)
562 : "r" ((way << 13) | addr | (offset << 3)));
563 datalo = ((unsigned long long)datalohi << 32) | datalolo;
564 ecc = dc_ecc(datalo);
565 if (ecc != datahi) {
Akinobu Mita13e79b42009-11-13 16:04:53 +0900566 int bits;
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700567 bad_ecc |= 1 << (3-offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 ecc ^= datahi;
Akinobu Mita13e79b42009-11-13 16:04:53 +0900569 bits = hweight8(ecc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE;
571 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000572 printk(" %02X-%016llX", datahi, datalo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000574 printk("\n");
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700575 if (bad_ecc)
Ralf Baechle36a88532007-03-01 11:56:43 +0000576 printk(" dwords w/ bad ECC: %d %d %d %d\n",
577 !!(bad_ecc & 8), !!(bad_ecc & 4),
578 !!(bad_ecc & 2), !!(bad_ecc & 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580 }
581 return res;
582}