blob: 576add33bf5bf1ae19af08a9605959b89e62f123 [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 */
185#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
Ralf Baechle33b75e5c2007-11-06 00:43:51 +0000186 csr_out32(M_BCM1480_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700187#else
Ralf Baechle33b75e5c2007-11-06 00:43:51 +0000188 csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700189#endif
Ralf Baechle36a88532007-03-01 11:56:43 +0000190 printk("Trace buffer frozen\n");
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700191#endif
192
Ralf Baechle36a88532007-03-01 11:56:43 +0000193 printk("Cache error exception on CPU %x:\n",
194 (read_c0_prid() >> 25) & 0x7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196 __asm__ __volatile__ (
197 " .set push\n\t"
198 " .set mips64\n\t"
199 " .set noat\n\t"
200 " mfc0 %0, $26\n\t"
201 " mfc0 %1, $27\n\t"
202 " mfc0 %2, $27, 1\n\t"
203 " dmfc0 $1, $27, 3\n\t"
204 " dsrl32 %3, $1, 0 \n\t"
205 " sll %4, $1, 0 \n\t"
206 " mfc0 %5, $30\n\t"
207 " .set pop"
208 : "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d),
209 "=r" (dpahi), "=r" (dpalo), "=r" (eepc));
210
211 cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo;
Ralf Baechle70342282013-01-22 12:59:30 +0100212 printk(" c0_errorepc == %08x\n", eepc);
213 printk(" c0_errctl == %08x", errctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 breakout_errctl(errctl);
215 if (errctl & CP0_ERRCTL_ICACHE) {
Ralf Baechle70342282013-01-22 12:59:30 +0100216 printk(" c0_cerr_i == %08x", cerr_i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 breakout_cerri(cerr_i);
218 if (CP0_CERRI_IDX_VALID(cerr_i)) {
219 /* Check index of EPC, allowing for delay slot */
220 if (((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) &&
221 ((eepc & SB1_CACHE_INDEX_MASK) != ((cerr_i & SB1_CACHE_INDEX_MASK) - 4)))
Ralf Baechle36a88532007-03-01 11:56:43 +0000222 printk(" cerr_i idx doesn't match eepc\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 else {
224 res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK,
225 (cerr_i & CP0_CERRI_DATA) != 0);
226 if (!(res & cerr_i))
Ralf Baechle36a88532007-03-01 11:56:43 +0000227 printk("...didn't see indicated icache problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 }
229 }
230 }
231 if (errctl & CP0_ERRCTL_DCACHE) {
Ralf Baechle70342282013-01-22 12:59:30 +0100232 printk(" c0_cerr_d == %08x", cerr_d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 breakout_cerrd(cerr_d);
234 if (CP0_CERRD_DPA_VALID(cerr_d)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000235 printk(" c0_cerr_dpa == %010llx\n", cerr_dpa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 if (!CP0_CERRD_IDX_VALID(cerr_d)) {
237 res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK,
238 (cerr_d & CP0_CERRD_DATA) != 0);
239 if (!(res & cerr_d))
Ralf Baechle36a88532007-03-01 11:56:43 +0000240 printk("...didn't see indicated dcache problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 } else {
242 if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK))
Ralf Baechle36a88532007-03-01 11:56:43 +0000243 printk(" cerr_d idx doesn't match cerr_dpa\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 else {
245 res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK,
246 (cerr_d & CP0_CERRD_DATA) != 0);
247 if (!(res & cerr_d))
Ralf Baechle36a88532007-03-01 11:56:43 +0000248 printk("...didn't see indicated problem\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 }
250 }
251 }
252 }
253
254 check_bus_watcher();
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 /*
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700257 * Calling panic() when a fatal cache error occurs scrambles the
258 * state of the system (and the cache), making it difficult to
Ralf Baechle70342282013-01-22 12:59:30 +0100259 * investigate after the fact. However, if you just stall the CPU,
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700260 * the other CPU may keep on running, which is typically very
261 * undesirable.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 */
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700263#ifdef CONFIG_SB1_CERR_STALL
264 while (1)
265 ;
266#else
267 panic("unhandled cache error");
268#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
271
272/* Parity lookup table. */
273static const uint8_t parity[256] = {
Ralf Baechle21a151d2007-10-11 23:46:15 +0100274 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
275 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
276 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
277 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
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 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
283 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
284 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
285 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
286 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
287 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
288 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
289 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290};
291
292/* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */
293static const uint64_t mask_72_64[8] = {
294 0x0738C808099264FFULL,
295 0x38C808099264FF07ULL,
296 0xC808099264FF0738ULL,
297 0x08099264FF0738C8ULL,
298 0x099264FF0738C808ULL,
299 0x9264FF0738C80809ULL,
300 0x64FF0738C8080992ULL,
301 0xFF0738C808099264ULL
302};
303
304/* Calculate the parity on a range of bits */
305static char range_parity(uint64_t dword, int max, int min)
306{
307 char parity = 0;
308 int i;
309 dword >>= min;
310 for (i=max-min; i>=0; i--) {
311 if (dword & 0x1)
312 parity = !parity;
313 dword >>= 1;
314 }
315 return parity;
316}
317
318/* Calculate the 4-bit even byte-parity for an instruction */
319static unsigned char inst_parity(uint32_t word)
320{
321 int i, j;
322 char parity = 0;
323 for (j=0; j<4; j++) {
324 char byte_parity = 0;
325 for (i=0; i<8; i++) {
326 if (word & 0x80000000)
327 byte_parity = !byte_parity;
328 word <<= 1;
329 }
330 parity <<= 1;
331 parity |= byte_parity;
332 }
333 return parity;
334}
335
336static uint32_t extract_ic(unsigned short addr, int data)
337{
338 unsigned short way;
339 int valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 uint32_t taghi, taglolo, taglohi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000341 unsigned long long taglo, va;
342 uint64_t tlo_tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 uint8_t lru;
344 int res = 0;
345
Ralf Baechle36a88532007-03-01 11:56:43 +0000346 printk("Icache index 0x%04x ", addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 for (way = 0; way < 4; way++) {
348 /* Index-load-tag-I */
349 __asm__ __volatile__ (
350 " .set push \n\t"
351 " .set noreorder \n\t"
352 " .set mips64 \n\t"
353 " .set noat \n\t"
354 " cache 4, 0(%3) \n\t"
355 " mfc0 %0, $29 \n\t"
356 " dmfc0 $1, $28 \n\t"
357 " dsrl32 %1, $1, 0 \n\t"
358 " sll %2, $1, 0 \n\t"
359 " .set pop"
360 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
361 : "r" ((way << 13) | addr));
362
363 taglo = ((unsigned long long)taglohi << 32) | taglolo;
364 if (way == 0) {
365 lru = (taghi >> 14) & 0xff;
Ralf Baechle36a88532007-03-01 11:56:43 +0000366 printk("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 ((addr >> 5) & 0x3), /* bank */
368 ((addr >> 7) & 0x3f), /* index */
369 (lru & 0x3),
370 ((lru >> 2) & 0x3),
371 ((lru >> 4) & 0x3),
372 ((lru >> 6) & 0x3));
373 }
374 va = (taglo & 0xC0000FFFFFFFE000ULL) | addr;
375 if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3))
376 va |= 0x3FFFF00000000000ULL;
377 valid = ((taghi >> 29) & 1);
378 if (valid) {
379 tlo_tmp = taglo & 0xfff3ff;
380 if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000381 printk(" ** bad parity in VTag0/G/ASID\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 res |= CP0_CERRI_TAG_PARITY;
383 }
384 if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000385 printk(" ** bad parity in R/VTag1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 res |= CP0_CERRI_TAG_PARITY;
387 }
388 }
389 if (valid ^ ((taghi >> 27) & 1)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000390 printk(" ** bad parity for valid bit\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 res |= CP0_CERRI_TAG_PARITY;
392 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000393 printk(" %d [VA %016llx] [Vld? %d] raw tags: %08X-%016llX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 way, va, valid, taghi, taglo);
395
396 if (data) {
397 uint32_t datahi, insta, instb;
398 uint8_t predecode;
399 int offset;
400
401 /* (hit all banks and ways) */
402 for (offset = 0; offset < 4; offset++) {
403 /* Index-load-data-I */
404 __asm__ __volatile__ (
405 " .set push\n\t"
406 " .set noreorder\n\t"
407 " .set mips64\n\t"
408 " .set noat\n\t"
409 " cache 6, 0(%3) \n\t"
410 " mfc0 %0, $29, 1\n\t"
411 " dmfc0 $1, $28, 1\n\t"
412 " dsrl32 %1, $1, 0 \n\t"
413 " sll %2, $1, 0 \n\t"
Ralf Baechle70342282013-01-22 12:59:30 +0100414 " .set pop \n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 : "=r" (datahi), "=r" (insta), "=r" (instb)
416 : "r" ((way << 13) | addr | (offset << 3)));
417 predecode = (datahi >> 8) & 0xff;
418 if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000419 printk(" ** bad parity in predecode\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 res |= CP0_CERRI_DATA_PARITY;
421 }
422 /* XXXKW should/could check predecode bits themselves */
423 if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000424 printk(" ** bad parity in instruction a\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 res |= CP0_CERRI_DATA_PARITY;
426 }
427 if ((datahi & 0xf) ^ inst_parity(instb)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000428 printk(" ** bad parity in instruction b\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 res |= CP0_CERRI_DATA_PARITY;
430 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000431 printk(" %05X-%08X%08X", datahi, insta, instb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000433 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
435 }
436 return res;
437}
438
439/* Compute the ECC for a data doubleword */
440static uint8_t dc_ecc(uint64_t dword)
441{
442 uint64_t t;
443 uint32_t w;
Ralf Baechle70342282013-01-22 12:59:30 +0100444 uint8_t p;
445 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
447 p = 0;
448 for (i = 7; i >= 0; i--)
449 {
450 p <<= 1;
451 t = dword & mask_72_64[i];
452 w = (uint32_t)(t >> 32);
453 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
454 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
455 w = (uint32_t)(t & 0xFFFFFFFF);
456 p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
457 ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
458 }
459 return p;
460}
461
462struct dc_state {
463 unsigned char val;
464 char *name;
465};
466
467static struct dc_state dc_states[] = {
468 { 0x00, "INVALID" },
469 { 0x0f, "COH-SHD" },
470 { 0x13, "NCO-E-C" },
471 { 0x19, "NCO-E-D" },
472 { 0x16, "COH-E-C" },
473 { 0x1c, "COH-E-D" },
474 { 0xff, "*ERROR*" }
475};
476
477#define DC_TAG_VALID(state) \
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700478 (((state) == 0x0) || ((state) == 0xf) || ((state) == 0x13) || \
479 ((state) == 0x19) || ((state) == 0x16) || ((state) == 0x1c))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481static char *dc_state_str(unsigned char state)
482{
483 struct dc_state *dsc = dc_states;
484 while (dsc->val != 0xff) {
485 if (dsc->val == state)
486 break;
487 dsc++;
488 }
489 return dsc->name;
490}
491
492static uint32_t extract_dc(unsigned short addr, int data)
493{
494 int valid, way;
495 unsigned char state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 uint32_t taghi, taglolo, taglohi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000497 unsigned long long taglo, pa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 uint8_t ecc, lru;
499 int res = 0;
500
Ralf Baechle36a88532007-03-01 11:56:43 +0000501 printk("Dcache index 0x%04x ", addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 for (way = 0; way < 4; way++) {
503 __asm__ __volatile__ (
504 " .set push\n\t"
505 " .set noreorder\n\t"
506 " .set mips64\n\t"
507 " .set noat\n\t"
508 " cache 5, 0(%3)\n\t" /* Index-load-tag-D */
509 " mfc0 %0, $29, 2\n\t"
510 " dmfc0 $1, $28, 2\n\t"
511 " dsrl32 %1, $1, 0\n\t"
512 " sll %2, $1, 0\n\t"
513 " .set pop"
514 : "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
515 : "r" ((way << 13) | addr));
516
517 taglo = ((unsigned long long)taglohi << 32) | taglolo;
518 pa = (taglo & 0xFFFFFFE000ULL) | addr;
519 if (way == 0) {
520 lru = (taghi >> 14) & 0xff;
Ralf Baechle36a88532007-03-01 11:56:43 +0000521 printk("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */
523 ((addr >> 6) & 0x3f), /* index */
524 (lru & 0x3),
525 ((lru >> 2) & 0x3),
526 ((lru >> 4) & 0x3),
527 ((lru >> 6) & 0x3));
528 }
529 state = (taghi >> 25) & 0x1f;
530 valid = DC_TAG_VALID(state);
Ralf Baechle36a88532007-03-01 11:56:43 +0000531 printk(" %d [PA %010llx] [state %s (%02x)] raw tags: %08X-%016llX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 way, pa, dc_state_str(state), state, taghi, taglo);
533 if (valid) {
534 if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000535 printk(" ** bad parity in PTag1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 res |= CP0_CERRD_TAG_ADDRESS;
537 }
538 if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) {
Ralf Baechle36a88532007-03-01 11:56:43 +0000539 printk(" ** bad parity in PTag0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 res |= CP0_CERRD_TAG_ADDRESS;
541 }
542 } else {
543 res |= CP0_CERRD_TAG_STATE;
544 }
545
546 if (data) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 uint32_t datalohi, datalolo, datahi;
Ralf Baechle41a81982007-03-24 14:09:59 +0000548 unsigned long long datalo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 int offset;
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700550 char bad_ecc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
552 for (offset = 0; offset < 4; offset++) {
553 /* Index-load-data-D */
554 __asm__ __volatile__ (
555 " .set push\n\t"
556 " .set noreorder\n\t"
557 " .set mips64\n\t"
558 " .set noat\n\t"
559 " cache 7, 0(%3)\n\t" /* Index-load-data-D */
560 " mfc0 %0, $29, 3\n\t"
561 " dmfc0 $1, $28, 3\n\t"
562 " dsrl32 %1, $1, 0 \n\t"
563 " sll %2, $1, 0 \n\t"
564 " .set pop"
565 : "=r" (datahi), "=r" (datalohi), "=r" (datalolo)
566 : "r" ((way << 13) | addr | (offset << 3)));
567 datalo = ((unsigned long long)datalohi << 32) | datalolo;
568 ecc = dc_ecc(datalo);
569 if (ecc != datahi) {
Akinobu Mita13e79b42009-11-13 16:04:53 +0900570 int bits;
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700571 bad_ecc |= 1 << (3-offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 ecc ^= datahi;
Akinobu Mita13e79b42009-11-13 16:04:53 +0900573 bits = hweight8(ecc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE;
575 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000576 printk(" %02X-%016llX", datahi, datalo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 }
Ralf Baechle36a88532007-03-01 11:56:43 +0000578 printk("\n");
Andrew Isaacsona4b5bd92005-10-19 23:57:40 -0700579 if (bad_ecc)
Ralf Baechle36a88532007-03-01 11:56:43 +0000580 printk(" dwords w/ bad ECC: %d %d %d %d\n",
581 !!(bad_ecc & 8), !!(bad_ecc & 4),
582 !!(bad_ecc & 2), !!(bad_ecc & 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 }
584 }
585 return res;
586}