]>
Commit | Line | Data |
---|---|---|
9171fc81 | 1 | /* |
a187559e | 2 | * U-Boot - traps.c Routines related to interrupts and exceptions |
9171fc81 MF |
3 | * |
4 | * Copyright (c) 2005-2008 Analog Devices Inc. | |
5 | * | |
6 | * This file is based on | |
7 | * No original Copyright holder listed, | |
8 | * Probabily original (C) Roman Zippel (assigned DJD, 1999) | |
9 | * | |
10 | * Copyright 2003 Metrowerks - for Blackfin | |
11 | * Copyright 2000-2001 Lineo, Inc. D. Jeff Dionne <jeff@lineo.ca> | |
12 | * Copyright 1999-2000 D. Jeff Dionne, <jeff@uclinux.org> | |
13 | * | |
14 | * (C) Copyright 2000-2004 | |
15 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
16 | * | |
17 | * Licensed under the GPL-2 or later. | |
18 | */ | |
19 | ||
20 | #include <common.h> | |
f19fd87e | 21 | #include <kgdb.h> |
9171fc81 MF |
22 | #include <linux/types.h> |
23 | #include <asm/traps.h> | |
24 | #include <asm/cplb.h> | |
25 | #include <asm/io.h> | |
26 | #include <asm/mach-common/bits/core.h> | |
27 | #include <asm/mach-common/bits/mpu.h> | |
28 | #include <asm/mach-common/bits/trace.h> | |
f19fd87e | 29 | #include <asm/deferred.h> |
9171fc81 MF |
30 | #include "cpu.h" |
31 | ||
66a4909a MF |
32 | #ifdef CONFIG_DEBUG_DUMP |
33 | # define ENABLE_DUMP 1 | |
34 | #else | |
35 | # define ENABLE_DUMP 0 | |
36 | #endif | |
37 | ||
9171fc81 MF |
38 | #define trace_buffer_save(x) \ |
39 | do { \ | |
66a4909a MF |
40 | if (!ENABLE_DUMP) \ |
41 | break; \ | |
9171fc81 MF |
42 | (x) = bfin_read_TBUFCTL(); \ |
43 | bfin_write_TBUFCTL((x) & ~TBUFEN); \ | |
44 | } while (0) | |
45 | ||
46 | #define trace_buffer_restore(x) \ | |
66a4909a MF |
47 | do { \ |
48 | if (!ENABLE_DUMP) \ | |
49 | break; \ | |
50 | bfin_write_TBUFCTL((x)); \ | |
51 | } while (0); | |
9171fc81 MF |
52 | |
53 | /* The purpose of this map is to provide a mapping of address<->cplb settings | |
54 | * rather than an exact map of what is actually addressable on the part. This | |
55 | * map covers all current Blackfin parts. If you try to access an address that | |
56 | * is in this map but not actually on the part, you won't get an exception and | |
57 | * reboot, you'll get an external hardware addressing error and reboot. Since | |
58 | * only the ends matter (you did something wrong and the board reset), the means | |
59 | * are largely irrelevant. | |
60 | */ | |
61 | struct memory_map { | |
62 | uint32_t start, end; | |
63 | uint32_t data_flags, inst_flags; | |
64 | }; | |
65 | const struct memory_map const bfin_memory_map[] = { | |
66 | { /* external memory */ | |
67 | .start = 0x00000000, | |
68 | .end = 0x20000000, | |
69 | .data_flags = SDRAM_DGENERIC, | |
70 | .inst_flags = SDRAM_IGENERIC, | |
71 | }, | |
72 | { /* async banks */ | |
73 | .start = 0x20000000, | |
74 | .end = 0x30000000, | |
75 | .data_flags = SDRAM_EBIU, | |
76 | .inst_flags = SDRAM_INON_CHBL, | |
77 | }, | |
78 | { /* everything on chip */ | |
79 | .start = 0xE0000000, | |
80 | .end = 0xFFFFFFFF, | |
81 | .data_flags = L1_DMEMORY, | |
82 | .inst_flags = L1_IMEMORY, | |
83 | } | |
84 | }; | |
85 | ||
f19fd87e RG |
86 | #ifdef CONFIG_EXCEPTION_DEFER |
87 | unsigned int deferred_regs[deferred_regs_last]; | |
88 | #endif | |
89 | ||
90 | /* | |
91 | * Handle all exceptions while running in EVT3 or EVT5 | |
92 | */ | |
93 | int trap_c(struct pt_regs *regs, uint32_t level) | |
9171fc81 | 94 | { |
f19fd87e | 95 | uint32_t ret = 0; |
9171fc81 | 96 | uint32_t trapnr = (regs->seqstat & EXCAUSE); |
66a4909a | 97 | unsigned long tflags; |
9171fc81 MF |
98 | bool data = false; |
99 | ||
66a4909a MF |
100 | /* |
101 | * Keep the trace buffer so that a miss here points people | |
102 | * to the right place (their code). Crashes here rarely | |
103 | * happen. If they do, only the Blackfin maintainer cares. | |
104 | */ | |
105 | trace_buffer_save(tflags); | |
106 | ||
9171fc81 MF |
107 | switch (trapnr) { |
108 | /* 0x26 - Data CPLB Miss */ | |
109 | case VEC_CPLB_M: | |
110 | ||
111 | if (ANOMALY_05000261) { | |
112 | static uint32_t last_cplb_fault_retx; | |
113 | /* | |
114 | * Work around an anomaly: if we see a new DCPLB fault, | |
115 | * return without doing anything. Then, | |
116 | * if we get the same fault again, handle it. | |
117 | */ | |
118 | if (last_cplb_fault_retx != regs->retx) { | |
119 | last_cplb_fault_retx = regs->retx; | |
66a4909a | 120 | break; |
9171fc81 MF |
121 | } |
122 | } | |
123 | ||
124 | data = true; | |
125 | /* fall through */ | |
126 | ||
127 | /* 0x27 - Instruction CPLB Miss */ | |
128 | case VEC_CPLB_I_M: { | |
129 | volatile uint32_t *CPLB_ADDR_BASE, *CPLB_DATA_BASE, *CPLB_ADDR, *CPLB_DATA; | |
130 | uint32_t new_cplb_addr = 0, new_cplb_data = 0; | |
131 | static size_t last_evicted; | |
132 | size_t i; | |
b6db2834 | 133 | |
f19fd87e RG |
134 | #ifdef CONFIG_EXCEPTION_DEFER |
135 | /* This should never happen */ | |
136 | if (level == 5) | |
137 | bfin_panic(regs); | |
138 | #endif | |
139 | ||
9171fc81 MF |
140 | new_cplb_addr = (data ? bfin_read_DCPLB_FAULT_ADDR() : bfin_read_ICPLB_FAULT_ADDR()) & ~(4 * 1024 * 1024 - 1); |
141 | ||
142 | for (i = 0; i < ARRAY_SIZE(bfin_memory_map); ++i) { | |
143 | /* if the exception is inside this range, lets use it */ | |
144 | if (new_cplb_addr >= bfin_memory_map[i].start && | |
145 | new_cplb_addr < bfin_memory_map[i].end) | |
146 | break; | |
147 | } | |
148 | if (i == ARRAY_SIZE(bfin_memory_map)) { | |
149 | printf("%cCPLB exception outside of memory map at 0x%p\n", | |
fe033ad6 | 150 | (data ? 'D' : 'I'), (void *)new_cplb_addr); |
9171fc81 MF |
151 | bfin_panic(regs); |
152 | } else | |
ff1a618a MF |
153 | debug("CPLB addr %p matches map 0x%p - 0x%p\n", |
154 | (void *)new_cplb_addr, | |
155 | (void *)bfin_memory_map[i].start, | |
156 | (void *)bfin_memory_map[i].end); | |
9171fc81 MF |
157 | new_cplb_data = (data ? bfin_memory_map[i].data_flags : bfin_memory_map[i].inst_flags); |
158 | ||
9171fc81 MF |
159 | if (data) { |
160 | CPLB_ADDR_BASE = (uint32_t *)DCPLB_ADDR0; | |
161 | CPLB_DATA_BASE = (uint32_t *)DCPLB_DATA0; | |
162 | } else { | |
163 | CPLB_ADDR_BASE = (uint32_t *)ICPLB_ADDR0; | |
164 | CPLB_DATA_BASE = (uint32_t *)ICPLB_DATA0; | |
165 | } | |
166 | ||
167 | /* find the next unlocked entry and evict it */ | |
168 | i = last_evicted & 0xF; | |
ff1a618a | 169 | debug("last evicted = %zu\n", i); |
9171fc81 MF |
170 | CPLB_DATA = CPLB_DATA_BASE + i; |
171 | while (*CPLB_DATA & CPLB_LOCK) { | |
ff1a618a | 172 | debug("skipping %zu %p - %08X\n", i, CPLB_DATA, *CPLB_DATA); |
9171fc81 MF |
173 | i = (i + 1) & 0xF; /* wrap around */ |
174 | CPLB_DATA = CPLB_DATA_BASE + i; | |
175 | } | |
176 | CPLB_ADDR = CPLB_ADDR_BASE + i; | |
177 | ||
ff1a618a MF |
178 | debug("evicting entry %zu: 0x%p 0x%08X\n", i, |
179 | (void *)*CPLB_ADDR, *CPLB_DATA); | |
9171fc81 | 180 | last_evicted = i + 1; |
0332e4df MF |
181 | |
182 | /* need to turn off cplbs whenever we muck with the cplb table */ | |
183 | #if ENDCPLB != ENICPLB | |
184 | # error cplb enable bit violates my sanity | |
185 | #endif | |
186 | uint32_t mem_control = (data ? DMEM_CONTROL : IMEM_CONTROL); | |
187 | bfin_write32(mem_control, bfin_read32(mem_control) & ~ENDCPLB); | |
9171fc81 MF |
188 | *CPLB_ADDR = new_cplb_addr; |
189 | *CPLB_DATA = new_cplb_data; | |
0332e4df MF |
190 | bfin_write32(mem_control, bfin_read32(mem_control) | ENDCPLB); |
191 | SSYNC(); | |
9171fc81 MF |
192 | |
193 | /* dump current table for debugging purposes */ | |
194 | CPLB_ADDR = CPLB_ADDR_BASE; | |
195 | CPLB_DATA = CPLB_DATA_BASE; | |
196 | for (i = 0; i < 16; ++i) | |
ff1a618a MF |
197 | debug("%2zu 0x%p 0x%08X\n", i, |
198 | (void *)*CPLB_ADDR++, *CPLB_DATA++); | |
9171fc81 | 199 | |
9171fc81 MF |
200 | break; |
201 | } | |
f19fd87e RG |
202 | #ifdef CONFIG_CMD_KGDB |
203 | /* Single step | |
204 | * if we are in IRQ5, just ignore, otherwise defer, and handle it in kgdb | |
205 | */ | |
206 | case VEC_STEP: | |
207 | if (level == 3) { | |
208 | /* If we just returned from an interrupt, the single step | |
209 | * event is for the RTI instruction. | |
210 | */ | |
211 | if (regs->retx == regs->pc) | |
212 | break; | |
213 | /* we just return if we are single stepping through IRQ5 */ | |
214 | if (regs->ipend & 0x20) | |
215 | break; | |
216 | /* Otherwise, turn single stepping off & fall through, | |
217 | * which defers to IRQ5 | |
218 | */ | |
219 | regs->syscfg &= ~1; | |
220 | } | |
221 | /* fall through */ | |
222 | #endif | |
9171fc81 | 223 | default: |
f19fd87e RG |
224 | #ifdef CONFIG_CMD_KGDB |
225 | if (level == 3) { | |
226 | /* We need to handle this at EVT5, so try again */ | |
66a4909a | 227 | bfin_dump(regs); |
f19fd87e RG |
228 | ret = 1; |
229 | break; | |
230 | } | |
231 | if (debugger_exception_handler && (*debugger_exception_handler)(regs)) | |
66a4909a | 232 | break; |
f19fd87e | 233 | #endif |
9171fc81 MF |
234 | bfin_panic(regs); |
235 | } | |
66a4909a MF |
236 | |
237 | trace_buffer_restore(tflags); | |
238 | ||
f19fd87e | 239 | return ret; |
9171fc81 MF |
240 | } |
241 | ||
ecb1dc89 MF |
242 | #ifndef CONFIG_KALLSYMS |
243 | const char *symbol_lookup(unsigned long addr, unsigned long *caddr) | |
9171fc81 | 244 | { |
ecb1dc89 MF |
245 | *caddr = addr; |
246 | return "N/A"; | |
9171fc81 | 247 | } |
ecb1dc89 | 248 | #endif |
9171fc81 MF |
249 | |
250 | static void decode_address(char *buf, unsigned long address) | |
251 | { | |
252 | unsigned long sym_addr; | |
fe033ad6 | 253 | void *paddr = (void *)address; |
9171fc81 MF |
254 | const char *sym = symbol_lookup(address, &sym_addr); |
255 | ||
256 | if (sym) { | |
fe033ad6 | 257 | sprintf(buf, "<0x%p> { %s + 0x%lx }", paddr, sym, address - sym_addr); |
9171fc81 MF |
258 | return; |
259 | } | |
260 | ||
261 | if (!address) | |
fe033ad6 | 262 | sprintf(buf, "<0x%p> /* Maybe null pointer? */", paddr); |
6d0f6bcf | 263 | else if (address >= CONFIG_SYS_MONITOR_BASE && |
93e14596 | 264 | address < CONFIG_SYS_MONITOR_BASE + CONFIG_SYS_MONITOR_LEN) |
fe033ad6 | 265 | sprintf(buf, "<0x%p> /* somewhere in u-boot */", paddr); |
9171fc81 | 266 | else |
fe033ad6 | 267 | sprintf(buf, "<0x%p> /* unknown address */", paddr); |
9171fc81 MF |
268 | } |
269 | ||
0ba1da11 MF |
270 | static char *strhwerrcause(uint16_t hwerrcause) |
271 | { | |
272 | switch (hwerrcause) { | |
273 | case 0x02: return "system mmr error"; | |
274 | case 0x03: return "external memory addressing error"; | |
275 | case 0x12: return "performance monitor overflow"; | |
276 | case 0x18: return "raise 5 instruction"; | |
277 | default: return "undef"; | |
278 | } | |
279 | } | |
280 | ||
281 | static char *strexcause(uint16_t excause) | |
282 | { | |
283 | switch (excause) { | |
284 | case 0x00 ... 0xf: return "custom exception"; | |
285 | case 0x10: return "single step"; | |
286 | case 0x11: return "trace buffer full"; | |
287 | case 0x21: return "undef inst"; | |
288 | case 0x22: return "illegal inst"; | |
289 | case 0x23: return "dcplb prot violation"; | |
290 | case 0x24: return "misaligned data"; | |
291 | case 0x25: return "unrecoverable event"; | |
292 | case 0x26: return "dcplb miss"; | |
293 | case 0x27: return "multiple dcplb hit"; | |
294 | case 0x28: return "emulation watchpoint"; | |
295 | case 0x2a: return "misaligned inst"; | |
296 | case 0x2b: return "icplb prot violation"; | |
297 | case 0x2c: return "icplb miss"; | |
298 | case 0x2d: return "multiple icplb hit"; | |
299 | case 0x2e: return "illegal use of supervisor resource"; | |
300 | default: return "undef"; | |
301 | } | |
302 | } | |
303 | ||
9171fc81 MF |
304 | void dump(struct pt_regs *fp) |
305 | { | |
306 | char buf[150]; | |
fe033ad6 | 307 | int i; |
0ba1da11 | 308 | uint16_t hwerrcause, excause; |
9171fc81 MF |
309 | |
310 | if (!ENABLE_DUMP) | |
311 | return; | |
312 | ||
f19fd87e RG |
313 | #ifndef CONFIG_CMD_KGDB |
314 | /* fp->ipend is normally garbage, so load it ourself */ | |
2de95bb2 | 315 | fp->ipend = bfin_read_IPEND(); |
f19fd87e | 316 | #endif |
2de95bb2 | 317 | |
0ba1da11 MF |
318 | hwerrcause = (fp->seqstat & HWERRCAUSE) >> HWERRCAUSE_P; |
319 | excause = (fp->seqstat & EXCAUSE) >> EXCAUSE_P; | |
320 | ||
9171fc81 MF |
321 | printf("SEQUENCER STATUS:\n"); |
322 | printf(" SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", | |
323 | fp->seqstat, fp->ipend, fp->syscfg); | |
fe033ad6 MF |
324 | printf(" HWERRCAUSE: 0x%x: %s\n", hwerrcause, strhwerrcause(hwerrcause)); |
325 | printf(" EXCAUSE : 0x%x: %s\n", excause, strexcause(excause)); | |
9171fc81 MF |
326 | for (i = 6; i <= 15; ++i) { |
327 | if (fp->ipend & (1 << i)) { | |
328 | decode_address(buf, bfin_read32(EVT0 + 4*i)); | |
329 | printf(" physical IVG%i asserted : %s\n", i, buf); | |
330 | } | |
331 | } | |
332 | decode_address(buf, fp->rete); | |
333 | printf(" RETE: %s\n", buf); | |
334 | decode_address(buf, fp->retn); | |
335 | printf(" RETN: %s\n", buf); | |
336 | decode_address(buf, fp->retx); | |
337 | printf(" RETX: %s\n", buf); | |
338 | decode_address(buf, fp->rets); | |
339 | printf(" RETS: %s\n", buf); | |
2de95bb2 | 340 | /* we lie and store RETI in "pc" */ |
9171fc81 | 341 | decode_address(buf, fp->pc); |
2de95bb2 | 342 | printf(" RETI: %s\n", buf); |
9171fc81 MF |
343 | |
344 | if (fp->seqstat & EXCAUSE) { | |
345 | decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); | |
346 | printf("DCPLB_FAULT_ADDR: %s\n", buf); | |
347 | decode_address(buf, bfin_read_ICPLB_FAULT_ADDR()); | |
348 | printf("ICPLB_FAULT_ADDR: %s\n", buf); | |
349 | } | |
350 | ||
351 | printf("\nPROCESSOR STATE:\n"); | |
352 | printf(" R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", | |
353 | fp->r0, fp->r1, fp->r2, fp->r3); | |
354 | printf(" R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", | |
355 | fp->r4, fp->r5, fp->r6, fp->r7); | |
356 | printf(" P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n", | |
357 | fp->p0, fp->p1, fp->p2, fp->p3); | |
358 | printf(" P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n", | |
fe033ad6 | 359 | fp->p4, fp->p5, fp->fp, (unsigned long)fp); |
9171fc81 MF |
360 | printf(" LB0: %08lx LT0: %08lx LC0: %08lx\n", |
361 | fp->lb0, fp->lt0, fp->lc0); | |
362 | printf(" LB1: %08lx LT1: %08lx LC1: %08lx\n", | |
363 | fp->lb1, fp->lt1, fp->lc1); | |
364 | printf(" B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n", | |
365 | fp->b0, fp->l0, fp->m0, fp->i0); | |
366 | printf(" B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n", | |
367 | fp->b1, fp->l1, fp->m1, fp->i1); | |
368 | printf(" B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n", | |
369 | fp->b2, fp->l2, fp->m2, fp->i2); | |
370 | printf(" B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n", | |
371 | fp->b3, fp->l3, fp->m3, fp->i3); | |
372 | printf("A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", | |
373 | fp->a0w, fp->a0x, fp->a1w, fp->a1x); | |
374 | ||
375 | printf("USP : %08lx ASTAT: %08lx\n", | |
376 | fp->usp, fp->astat); | |
377 | ||
378 | printf("\n"); | |
379 | } | |
380 | ||
66a4909a | 381 | static void _dump_bfin_trace_buffer(void) |
9171fc81 MF |
382 | { |
383 | char buf[150]; | |
fe033ad6 | 384 | int i = 0; |
9171fc81 MF |
385 | |
386 | if (!ENABLE_DUMP) | |
387 | return; | |
388 | ||
9171fc81 MF |
389 | printf("Hardware Trace:\n"); |
390 | ||
391 | if (bfin_read_TBUFSTAT() & TBUFCNT) { | |
392 | for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) { | |
393 | decode_address(buf, bfin_read_TBUF()); | |
394 | printf("%4i Target : %s\n", i, buf); | |
395 | decode_address(buf, bfin_read_TBUF()); | |
396 | printf(" Source : %s\n", buf); | |
397 | } | |
398 | } | |
66a4909a | 399 | } |
9171fc81 | 400 | |
66a4909a MF |
401 | void dump_bfin_trace_buffer(void) |
402 | { | |
403 | unsigned long tflags; | |
404 | trace_buffer_save(tflags); | |
405 | _dump_bfin_trace_buffer(); | |
9171fc81 MF |
406 | trace_buffer_restore(tflags); |
407 | } | |
408 | ||
66a4909a | 409 | void bfin_dump(struct pt_regs *regs) |
9171fc81 | 410 | { |
66a4909a MF |
411 | unsigned long tflags; |
412 | ||
413 | trace_buffer_save(tflags); | |
9171fc81 MF |
414 | |
415 | puts( | |
416 | "\n" | |
417 | "\n" | |
418 | "\n" | |
419 | "Ack! Something bad happened to the Blackfin!\n" | |
420 | "\n" | |
421 | ); | |
422 | dump(regs); | |
66a4909a | 423 | _dump_bfin_trace_buffer(); |
7133999e | 424 | puts("\n"); |
66a4909a MF |
425 | |
426 | trace_buffer_restore(tflags); | |
427 | } | |
428 | ||
429 | void bfin_panic(struct pt_regs *regs) | |
430 | { | |
431 | unsigned long tflags; | |
432 | trace_buffer_save(tflags); | |
433 | bfin_dump(regs); | |
eed1a7b1 | 434 | panic("PANIC: Blackfin internal error"); |
9171fc81 | 435 | } |