diff options
author | Thomas Bogendoerfer <tsbogend@alpha.franken.de> | 2007-12-02 07:00:32 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2008-01-29 05:14:58 -0500 |
commit | e2defae5a9b4f8d1acb058be212ef89c8763dc5b (patch) | |
tree | 3a0e72b5df8ee181e6c30fb6d1cba636c460a9fd /arch/mips/sgi-ip22 | |
parent | 81149be11327cbad006f82318f46e0b68a7b14ad (diff) |
[MIPS] IP28 support
Add support for SGI IP28 machines (Indigo 2 with R10k CPUs)
This work is mainly based on Peter Fuersts work.
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/sgi-ip22')
-rw-r--r-- | arch/mips/sgi-ip22/Makefile | 8 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-mc.c | 4 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip28-berr.c | 502 |
3 files changed, 511 insertions, 3 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile index e3acb51b70b5..ef1564e40c8d 100644 --- a/arch/mips/sgi-ip22/Makefile +++ b/arch/mips/sgi-ip22/Makefile | |||
@@ -3,9 +3,11 @@ | |||
3 | # under Linux. | 3 | # under Linux. |
4 | # | 4 | # |
5 | 5 | ||
6 | obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-berr.o \ | 6 | obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \ |
7 | ip22-time.o ip22-nvram.o ip22-platform.o ip22-reset.o ip22-setup.o | 7 | ip22-platform.o ip22-reset.o ip22-setup.o |
8 | 8 | ||
9 | obj-$(CONFIG_SGI_IP22) += ip22-berr.o | ||
10 | obj-$(CONFIG_SGI_IP28) += ip28-berr.o | ||
9 | obj-$(CONFIG_EISA) += ip22-eisa.o | 11 | obj-$(CONFIG_EISA) += ip22-eisa.o |
10 | 12 | ||
11 | EXTRA_CFLAGS += -Werror | 13 | # EXTRA_CFLAGS += -Werror |
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c index 01a805dcc67c..3f35d6367bec 100644 --- a/arch/mips/sgi-ip22/ip22-mc.c +++ b/arch/mips/sgi-ip22/ip22-mc.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | 4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) |
5 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes | 5 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes |
6 | * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org) | 6 | * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org) |
7 | * Copyright (C) 2004 Peter Fuerst (pf@net.alphadv.de) - IP28 | ||
7 | */ | 8 | */ |
8 | 9 | ||
9 | #include <linux/init.h> | 10 | #include <linux/init.h> |
@@ -137,9 +138,12 @@ void __init sgimc_init(void) | |||
137 | /* Step 2: Enable all parity checking in cpu control register | 138 | /* Step 2: Enable all parity checking in cpu control register |
138 | * zero. | 139 | * zero. |
139 | */ | 140 | */ |
141 | /* don't touch parity settings for IP28 */ | ||
142 | #ifndef CONFIG_SGI_IP28 | ||
140 | tmp = sgimc->cpuctrl0; | 143 | tmp = sgimc->cpuctrl0; |
141 | tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM | | 144 | tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM | |
142 | SGIMC_CCTRL0_R4KNOCHKPARR); | 145 | SGIMC_CCTRL0_R4KNOCHKPARR); |
146 | #endif | ||
143 | sgimc->cpuctrl0 = tmp; | 147 | sgimc->cpuctrl0 = tmp; |
144 | 148 | ||
145 | /* Step 3: Setup the MC write buffer depth, this is controlled | 149 | /* Step 3: Setup the MC write buffer depth, this is controlled |
diff --git a/arch/mips/sgi-ip22/ip28-berr.c b/arch/mips/sgi-ip22/ip28-berr.c new file mode 100644 index 000000000000..30e12e2ec4b5 --- /dev/null +++ b/arch/mips/sgi-ip22/ip28-berr.c | |||
@@ -0,0 +1,502 @@ | |||
1 | /* | ||
2 | * ip28-berr.c: Bus error handling. | ||
3 | * | ||
4 | * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org) | ||
5 | * Copyright (C) 2005 Peter Fuerst (pf@net.alphadv.de) - IP28 | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/seq_file.h> | ||
12 | |||
13 | #include <asm/addrspace.h> | ||
14 | #include <asm/system.h> | ||
15 | #include <asm/traps.h> | ||
16 | #include <asm/branch.h> | ||
17 | #include <asm/irq_regs.h> | ||
18 | #include <asm/sgi/mc.h> | ||
19 | #include <asm/sgi/hpc3.h> | ||
20 | #include <asm/sgi/ioc.h> | ||
21 | #include <asm/sgi/ip22.h> | ||
22 | #include <asm/r4kcache.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/bootinfo.h> | ||
25 | |||
26 | static unsigned int count_be_is_fixup; | ||
27 | static unsigned int count_be_handler; | ||
28 | static unsigned int count_be_interrupt; | ||
29 | static int debug_be_interrupt; | ||
30 | |||
31 | static unsigned int cpu_err_stat; /* Status reg for CPU */ | ||
32 | static unsigned int gio_err_stat; /* Status reg for GIO */ | ||
33 | static unsigned int cpu_err_addr; /* Error address reg for CPU */ | ||
34 | static unsigned int gio_err_addr; /* Error address reg for GIO */ | ||
35 | static unsigned int extio_stat; | ||
36 | static unsigned int hpc3_berr_stat; /* Bus error interrupt status */ | ||
37 | |||
38 | struct hpc3_stat { | ||
39 | unsigned long addr; | ||
40 | unsigned int ctrl; | ||
41 | unsigned int cbp; | ||
42 | unsigned int ndptr; | ||
43 | }; | ||
44 | |||
45 | static struct { | ||
46 | struct hpc3_stat pbdma[8]; | ||
47 | struct hpc3_stat scsi[2]; | ||
48 | struct hpc3_stat ethrx, ethtx; | ||
49 | } hpc3; | ||
50 | |||
51 | static struct { | ||
52 | unsigned long err_addr; | ||
53 | struct { | ||
54 | u32 lo; | ||
55 | u32 hi; | ||
56 | } tags[1][2], tagd[4][2], tagi[4][2]; /* Way 0/1 */ | ||
57 | } cache_tags; | ||
58 | |||
59 | static inline void save_cache_tags(unsigned busaddr) | ||
60 | { | ||
61 | unsigned long addr = CAC_BASE | busaddr; | ||
62 | int i; | ||
63 | cache_tags.err_addr = addr; | ||
64 | |||
65 | /* | ||
66 | * Starting with a bus-address, save secondary cache (indexed by | ||
67 | * PA[23..18:7..6]) tags first. | ||
68 | */ | ||
69 | addr &= ~1L; | ||
70 | #define tag cache_tags.tags[0] | ||
71 | cache_op(Index_Load_Tag_S, addr); | ||
72 | tag[0].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ | ||
73 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
74 | cache_op(Index_Load_Tag_S, addr | 1L); | ||
75 | tag[1].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ | ||
76 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
77 | #undef tag | ||
78 | |||
79 | /* | ||
80 | * Save all primary data cache (indexed by VA[13:5]) tags which | ||
81 | * might fit to this bus-address, knowing that VA[11:0] == PA[11:0]. | ||
82 | * Saving all tags and evaluating them later is easier and safer | ||
83 | * than relying on VA[13:12] from the secondary cache tags to pick | ||
84 | * matching primary tags here already. | ||
85 | */ | ||
86 | addr &= (0xffL << 56) | ((1 << 12) - 1); | ||
87 | #define tag cache_tags.tagd[i] | ||
88 | for (i = 0; i < 4; ++i, addr += (1 << 12)) { | ||
89 | cache_op(Index_Load_Tag_D, addr); | ||
90 | tag[0].lo = read_c0_taglo(); /* PA[35:12] */ | ||
91 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
92 | cache_op(Index_Load_Tag_D, addr | 1L); | ||
93 | tag[1].lo = read_c0_taglo(); /* PA[35:12] */ | ||
94 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
95 | } | ||
96 | #undef tag | ||
97 | |||
98 | /* | ||
99 | * Save primary instruction cache (indexed by VA[13:6]) tags | ||
100 | * the same way. | ||
101 | */ | ||
102 | addr &= (0xffL << 56) | ((1 << 12) - 1); | ||
103 | #define tag cache_tags.tagi[i] | ||
104 | for (i = 0; i < 4; ++i, addr += (1 << 12)) { | ||
105 | cache_op(Index_Load_Tag_I, addr); | ||
106 | tag[0].lo = read_c0_taglo(); /* PA[35:12] */ | ||
107 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
108 | cache_op(Index_Load_Tag_I, addr | 1L); | ||
109 | tag[1].lo = read_c0_taglo(); /* PA[35:12] */ | ||
110 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
111 | } | ||
112 | #undef tag | ||
113 | } | ||
114 | |||
115 | #define GIO_ERRMASK 0xff00 | ||
116 | #define CPU_ERRMASK 0x3f00 | ||
117 | |||
118 | static void save_and_clear_buserr(void) | ||
119 | { | ||
120 | int i; | ||
121 | |||
122 | /* save status registers */ | ||
123 | cpu_err_addr = sgimc->cerr; | ||
124 | cpu_err_stat = sgimc->cstat; | ||
125 | gio_err_addr = sgimc->gerr; | ||
126 | gio_err_stat = sgimc->gstat; | ||
127 | extio_stat = sgioc->extio; | ||
128 | hpc3_berr_stat = hpc3c0->bestat; | ||
129 | |||
130 | hpc3.scsi[0].addr = (unsigned long)&hpc3c0->scsi_chan0; | ||
131 | hpc3.scsi[0].ctrl = hpc3c0->scsi_chan0.ctrl; /* HPC3_SCTRL_ACTIVE ? */ | ||
132 | hpc3.scsi[0].cbp = hpc3c0->scsi_chan0.cbptr; | ||
133 | hpc3.scsi[0].ndptr = hpc3c0->scsi_chan0.ndptr; | ||
134 | |||
135 | hpc3.scsi[1].addr = (unsigned long)&hpc3c0->scsi_chan1; | ||
136 | hpc3.scsi[1].ctrl = hpc3c0->scsi_chan1.ctrl; /* HPC3_SCTRL_ACTIVE ? */ | ||
137 | hpc3.scsi[1].cbp = hpc3c0->scsi_chan1.cbptr; | ||
138 | hpc3.scsi[1].ndptr = hpc3c0->scsi_chan1.ndptr; | ||
139 | |||
140 | hpc3.ethrx.addr = (unsigned long)&hpc3c0->ethregs.rx_cbptr; | ||
141 | hpc3.ethrx.ctrl = hpc3c0->ethregs.rx_ctrl; /* HPC3_ERXCTRL_ACTIVE ? */ | ||
142 | hpc3.ethrx.cbp = hpc3c0->ethregs.rx_cbptr; | ||
143 | hpc3.ethrx.ndptr = hpc3c0->ethregs.rx_ndptr; | ||
144 | |||
145 | hpc3.ethtx.addr = (unsigned long)&hpc3c0->ethregs.tx_cbptr; | ||
146 | hpc3.ethtx.ctrl = hpc3c0->ethregs.tx_ctrl; /* HPC3_ETXCTRL_ACTIVE ? */ | ||
147 | hpc3.ethtx.cbp = hpc3c0->ethregs.tx_cbptr; | ||
148 | hpc3.ethtx.ndptr = hpc3c0->ethregs.tx_ndptr; | ||
149 | |||
150 | for (i = 0; i < 8; ++i) { | ||
151 | /* HPC3_PDMACTRL_ISACT ? */ | ||
152 | hpc3.pbdma[i].addr = (unsigned long)&hpc3c0->pbdma[i]; | ||
153 | hpc3.pbdma[i].ctrl = hpc3c0->pbdma[i].pbdma_ctrl; | ||
154 | hpc3.pbdma[i].cbp = hpc3c0->pbdma[i].pbdma_bptr; | ||
155 | hpc3.pbdma[i].ndptr = hpc3c0->pbdma[i].pbdma_dptr; | ||
156 | } | ||
157 | i = 0; | ||
158 | if (gio_err_stat & CPU_ERRMASK) | ||
159 | i = gio_err_addr; | ||
160 | if (cpu_err_stat & CPU_ERRMASK) | ||
161 | i = cpu_err_addr; | ||
162 | save_cache_tags(i); | ||
163 | |||
164 | sgimc->cstat = sgimc->gstat = 0; | ||
165 | } | ||
166 | |||
167 | static void print_cache_tags(void) | ||
168 | { | ||
169 | u32 scb, scw; | ||
170 | int i; | ||
171 | |||
172 | printk(KERN_ERR "Cache tags @ %08x:\n", (unsigned)cache_tags.err_addr); | ||
173 | |||
174 | /* PA[31:12] shifted to PTag0 (PA[35:12]) format */ | ||
175 | scw = (cache_tags.err_addr >> 4) & 0x0fffff00; | ||
176 | |||
177 | scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 5) - 1); | ||
178 | for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ | ||
179 | if ((cache_tags.tagd[i][0].lo & 0x0fffff00) != scw && | ||
180 | (cache_tags.tagd[i][1].lo & 0x0fffff00) != scw) | ||
181 | continue; | ||
182 | printk(KERN_ERR | ||
183 | "D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x)\n", | ||
184 | cache_tags.tagd[i][0].hi, cache_tags.tagd[i][0].lo, | ||
185 | cache_tags.tagd[i][1].hi, cache_tags.tagd[i][1].lo, | ||
186 | scb | (1 << 12)*i); | ||
187 | } | ||
188 | scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 6) - 1); | ||
189 | for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ | ||
190 | if ((cache_tags.tagi[i][0].lo & 0x0fffff00) != scw && | ||
191 | (cache_tags.tagi[i][1].lo & 0x0fffff00) != scw) | ||
192 | continue; | ||
193 | printk(KERN_ERR | ||
194 | "I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x)\n", | ||
195 | cache_tags.tagi[i][0].hi, cache_tags.tagi[i][0].lo, | ||
196 | cache_tags.tagi[i][1].hi, cache_tags.tagi[i][1].lo, | ||
197 | scb | (1 << 12)*i); | ||
198 | } | ||
199 | i = read_c0_config(); | ||
200 | scb = i & (1 << 13) ? 7:6; /* scblksize = 2^[7..6] */ | ||
201 | scw = ((i >> 16) & 7) + 19 - 1; /* scwaysize = 2^[24..19] / 2 */ | ||
202 | |||
203 | i = ((1 << scw) - 1) & ~((1 << scb) - 1); | ||
204 | printk(KERN_ERR "S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x)\n", | ||
205 | cache_tags.tags[0][0].hi, cache_tags.tags[0][0].lo, | ||
206 | cache_tags.tags[0][1].hi, cache_tags.tags[0][1].lo, | ||
207 | scw-1, scb, i & (unsigned)cache_tags.err_addr); | ||
208 | } | ||
209 | |||
210 | static inline const char *cause_excode_text(int cause) | ||
211 | { | ||
212 | static const char *txt[32] = | ||
213 | { "Interrupt", | ||
214 | "TLB modification", | ||
215 | "TLB (load or instruction fetch)", | ||
216 | "TLB (store)", | ||
217 | "Address error (load or instruction fetch)", | ||
218 | "Address error (store)", | ||
219 | "Bus error (instruction fetch)", | ||
220 | "Bus error (data: load or store)", | ||
221 | "Syscall", | ||
222 | "Breakpoint", | ||
223 | "Reserved instruction", | ||
224 | "Coprocessor unusable", | ||
225 | "Arithmetic Overflow", | ||
226 | "Trap", | ||
227 | "14", | ||
228 | "Floating-Point", | ||
229 | "16", "17", "18", "19", "20", "21", "22", | ||
230 | "Watch Hi/Lo", | ||
231 | "24", "25", "26", "27", "28", "29", "30", "31", | ||
232 | }; | ||
233 | return txt[(cause & 0x7c) >> 2]; | ||
234 | } | ||
235 | |||
236 | static void print_buserr(const struct pt_regs *regs) | ||
237 | { | ||
238 | const int field = 2 * sizeof(unsigned long); | ||
239 | int error = 0; | ||
240 | |||
241 | if (extio_stat & EXTIO_MC_BUSERR) { | ||
242 | printk(KERN_ERR "MC Bus Error\n"); | ||
243 | error |= 1; | ||
244 | } | ||
245 | if (extio_stat & EXTIO_HPC3_BUSERR) { | ||
246 | printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n", | ||
247 | hpc3_berr_stat, | ||
248 | (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >> | ||
249 | HPC3_BESTAT_PIDSHIFT, | ||
250 | (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA", | ||
251 | hpc3_berr_stat & HPC3_BESTAT_BLMASK); | ||
252 | error |= 2; | ||
253 | } | ||
254 | if (extio_stat & EXTIO_EISA_BUSERR) { | ||
255 | printk(KERN_ERR "EISA Bus Error\n"); | ||
256 | error |= 4; | ||
257 | } | ||
258 | if (cpu_err_stat & CPU_ERRMASK) { | ||
259 | printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n", | ||
260 | cpu_err_stat, | ||
261 | cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "", | ||
262 | cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "", | ||
263 | cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "", | ||
264 | cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "", | ||
265 | cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "", | ||
266 | cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "", | ||
267 | cpu_err_addr); | ||
268 | error |= 8; | ||
269 | } | ||
270 | if (gio_err_stat & GIO_ERRMASK) { | ||
271 | printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n", | ||
272 | gio_err_stat, | ||
273 | gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "", | ||
274 | gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "", | ||
275 | gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "", | ||
276 | gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "", | ||
277 | gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "", | ||
278 | gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "", | ||
279 | gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "", | ||
280 | gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "", | ||
281 | gio_err_addr); | ||
282 | error |= 16; | ||
283 | } | ||
284 | if (!error) | ||
285 | printk(KERN_ERR "MC: Hmm, didn't find any error condition.\n"); | ||
286 | else { | ||
287 | printk(KERN_ERR "CP0: config %08x, " | ||
288 | "MC: cpuctrl0/1: %08x/%05x, giopar: %04x\n" | ||
289 | "MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x\n", | ||
290 | read_c0_config(), | ||
291 | sgimc->cpuctrl0, sgimc->cpuctrl0, sgimc->giopar, | ||
292 | sgimc->cmacc, sgimc->gmacc, | ||
293 | sgimc->mconfig0, sgimc->mconfig1); | ||
294 | print_cache_tags(); | ||
295 | } | ||
296 | printk(KERN_ALERT "%s, epc == %0*lx, ra == %0*lx\n", | ||
297 | cause_excode_text(regs->cp0_cause), | ||
298 | field, regs->cp0_epc, field, regs->regs[31]); | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * Check, whether MC's (virtual) DMA address caused the bus error. | ||
303 | * See "Virtual DMA Specification", Draft 1.5, Feb 13 1992, SGI | ||
304 | */ | ||
305 | |||
306 | static int addr_is_ram(unsigned long addr, unsigned sz) | ||
307 | { | ||
308 | int i; | ||
309 | |||
310 | for (i = 0; i < boot_mem_map.nr_map; i++) { | ||
311 | unsigned long a = boot_mem_map.map[i].addr; | ||
312 | if (a <= addr && addr+sz <= a+boot_mem_map.map[i].size) | ||
313 | return 1; | ||
314 | } | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int check_microtlb(u32 hi, u32 lo, unsigned long vaddr) | ||
319 | { | ||
320 | /* This is likely rather similar to correct code ;-) */ | ||
321 | |||
322 | vaddr &= 0x7fffffff; /* Doc. states that top bit is ignored */ | ||
323 | |||
324 | /* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */ | ||
325 | if ((lo & 2) && (vaddr >> 21) == ((hi<<1) >> 22)) { | ||
326 | u32 ctl = sgimc->dma_ctrl; | ||
327 | if (ctl & 1) { | ||
328 | unsigned int pgsz = (ctl & 2) ? 14:12; /* 16k:4k */ | ||
329 | /* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */ | ||
330 | unsigned long pte = (lo >> 6) << 12; /* PTEBase */ | ||
331 | pte += 8*((vaddr >> pgsz) & 0x1ff); | ||
332 | if (addr_is_ram(pte, 8)) { | ||
333 | /* | ||
334 | * Note: Since DMA hardware does look up | ||
335 | * translation on its own, this PTE *must* | ||
336 | * match the TLB/EntryLo-register format ! | ||
337 | */ | ||
338 | unsigned long a = *(unsigned long *) | ||
339 | PHYS_TO_XKSEG_UNCACHED(pte); | ||
340 | a = (a & 0x3f) << 6; /* PFN */ | ||
341 | a += vaddr & ((1 << pgsz) - 1); | ||
342 | return (cpu_err_addr == a); | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int check_vdma_memaddr(void) | ||
350 | { | ||
351 | if (cpu_err_stat & CPU_ERRMASK) { | ||
352 | u32 a = sgimc->maddronly; | ||
353 | |||
354 | if (!(sgimc->dma_ctrl & 0x100)) /* Xlate-bit clear ? */ | ||
355 | return (cpu_err_addr == a); | ||
356 | |||
357 | if (check_microtlb(sgimc->dtlb_hi0, sgimc->dtlb_lo0, a) || | ||
358 | check_microtlb(sgimc->dtlb_hi1, sgimc->dtlb_lo1, a) || | ||
359 | check_microtlb(sgimc->dtlb_hi2, sgimc->dtlb_lo2, a) || | ||
360 | check_microtlb(sgimc->dtlb_hi3, sgimc->dtlb_lo3, a)) | ||
361 | return 1; | ||
362 | } | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int check_vdma_gioaddr(void) | ||
367 | { | ||
368 | if (gio_err_stat & GIO_ERRMASK) { | ||
369 | u32 a = sgimc->gio_dma_trans; | ||
370 | a = (sgimc->gmaddronly & ~a) | (sgimc->gio_dma_sbits & a); | ||
371 | return (gio_err_addr == a); | ||
372 | } | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * MC sends an interrupt whenever bus or parity errors occur. In addition, | ||
378 | * if the error happened during a CPU read, it also asserts the bus error | ||
379 | * pin on the R4K. Code in bus error handler save the MC bus error registers | ||
380 | * and then clear the interrupt when this happens. | ||
381 | */ | ||
382 | |||
383 | static int ip28_be_interrupt(const struct pt_regs *regs) | ||
384 | { | ||
385 | int i; | ||
386 | |||
387 | save_and_clear_buserr(); | ||
388 | /* | ||
389 | * Try to find out, whether we got here by a mispredicted speculative | ||
390 | * load/store operation. If so, it's not fatal, we can go on. | ||
391 | */ | ||
392 | /* Any cause other than "Interrupt" (ExcCode 0) is fatal. */ | ||
393 | if (regs->cp0_cause & CAUSEF_EXCCODE) | ||
394 | goto mips_be_fatal; | ||
395 | |||
396 | /* Any cause other than "Bus error interrupt" (IP6) is weird. */ | ||
397 | if ((regs->cp0_cause & CAUSEF_IP6) != CAUSEF_IP6) | ||
398 | goto mips_be_fatal; | ||
399 | |||
400 | if (extio_stat & (EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR)) | ||
401 | goto mips_be_fatal; | ||
402 | |||
403 | /* Any state other than "Memory bus error" is fatal. */ | ||
404 | if (cpu_err_stat & CPU_ERRMASK & ~SGIMC_CSTAT_ADDR) | ||
405 | goto mips_be_fatal; | ||
406 | |||
407 | /* GIO errors other than timeouts are fatal */ | ||
408 | if (gio_err_stat & GIO_ERRMASK & ~SGIMC_GSTAT_TIME) | ||
409 | goto mips_be_fatal; | ||
410 | |||
411 | /* | ||
412 | * Now we have an asynchronous bus error, speculatively or DMA caused. | ||
413 | * Need to search all DMA descriptors for the error address. | ||
414 | */ | ||
415 | for (i = 0; i < sizeof(hpc3)/sizeof(struct hpc3_stat); ++i) { | ||
416 | struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; | ||
417 | if ((cpu_err_stat & CPU_ERRMASK) && | ||
418 | (cpu_err_addr == hp->ndptr || cpu_err_addr == hp->cbp)) | ||
419 | break; | ||
420 | if ((gio_err_stat & GIO_ERRMASK) && | ||
421 | (gio_err_addr == hp->ndptr || gio_err_addr == hp->cbp)) | ||
422 | break; | ||
423 | } | ||
424 | if (i < sizeof(hpc3)/sizeof(struct hpc3_stat)) { | ||
425 | struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; | ||
426 | printk(KERN_ERR "at DMA addresses: HPC3 @ %08lx:" | ||
427 | " ctl %08x, ndp %08x, cbp %08x\n", | ||
428 | CPHYSADDR(hp->addr), hp->ctrl, hp->ndptr, hp->cbp); | ||
429 | goto mips_be_fatal; | ||
430 | } | ||
431 | /* Check MC's virtual DMA stuff. */ | ||
432 | if (check_vdma_memaddr()) { | ||
433 | printk(KERN_ERR "at GIO DMA: mem address 0x%08x.\n", | ||
434 | sgimc->maddronly); | ||
435 | goto mips_be_fatal; | ||
436 | } | ||
437 | if (check_vdma_gioaddr()) { | ||
438 | printk(KERN_ERR "at GIO DMA: gio address 0x%08x.\n", | ||
439 | sgimc->gmaddronly); | ||
440 | goto mips_be_fatal; | ||
441 | } | ||
442 | /* A speculative bus error... */ | ||
443 | if (debug_be_interrupt) { | ||
444 | print_buserr(regs); | ||
445 | printk(KERN_ERR "discarded!\n"); | ||
446 | } | ||
447 | return MIPS_BE_DISCARD; | ||
448 | |||
449 | mips_be_fatal: | ||
450 | print_buserr(regs); | ||
451 | return MIPS_BE_FATAL; | ||
452 | } | ||
453 | |||
454 | void ip22_be_interrupt(int irq) | ||
455 | { | ||
456 | const struct pt_regs *regs = get_irq_regs(); | ||
457 | |||
458 | count_be_interrupt++; | ||
459 | |||
460 | if (ip28_be_interrupt(regs) != MIPS_BE_DISCARD) { | ||
461 | /* Assume it would be too dangerous to continue ... */ | ||
462 | die_if_kernel("Oops", regs); | ||
463 | force_sig(SIGBUS, current); | ||
464 | } else if (debug_be_interrupt) | ||
465 | show_regs((struct pt_regs *)regs); | ||
466 | } | ||
467 | |||
468 | static int ip28_be_handler(struct pt_regs *regs, int is_fixup) | ||
469 | { | ||
470 | /* | ||
471 | * We arrive here only in the unusual case of do_be() invocation, | ||
472 | * i.e. by a bus error exception without a bus error interrupt. | ||
473 | */ | ||
474 | if (is_fixup) { | ||
475 | count_be_is_fixup++; | ||
476 | save_and_clear_buserr(); | ||
477 | return MIPS_BE_FIXUP; | ||
478 | } | ||
479 | count_be_handler++; | ||
480 | return ip28_be_interrupt(regs); | ||
481 | } | ||
482 | |||
483 | void __init ip22_be_init(void) | ||
484 | { | ||
485 | board_be_handler = ip28_be_handler; | ||
486 | } | ||
487 | |||
488 | int ip28_show_be_info(struct seq_file *m) | ||
489 | { | ||
490 | seq_printf(m, "IP28 be fixups\t\t: %u\n", count_be_is_fixup); | ||
491 | seq_printf(m, "IP28 be interrupts\t: %u\n", count_be_interrupt); | ||
492 | seq_printf(m, "IP28 be handler\t\t: %u\n", count_be_handler); | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static int __init debug_be_setup(char *str) | ||
498 | { | ||
499 | debug_be_interrupt++; | ||
500 | return 1; | ||
501 | } | ||
502 | __setup("ip28_debug_be", debug_be_setup); | ||