]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - cpu/blackfin/traps.c
SAMSUNG: serial: modify name from s5pc1xx to s5p
[karo-tx-uboot.git] / cpu / blackfin / traps.c
index d17c0a195dcfed6aed122b0ddb7022d695fc7fc6..caaea94106a556d2095a85847967743a3eabfd19 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <common.h>
+#include <kgdb.h>
 #include <linux/types.h>
 #include <asm/traps.h>
 #include <asm/cplb.h>
@@ -25,6 +26,7 @@
 #include <asm/mach-common/bits/core.h>
 #include <asm/mach-common/bits/mpu.h>
 #include <asm/mach-common/bits/trace.h>
+#include <asm/deferred.h>
 #include "cpu.h"
 
 #define trace_buffer_save(x) \
@@ -69,8 +71,16 @@ const struct memory_map const bfin_memory_map[] = {
        }
 };
 
-void trap_c(struct pt_regs *regs)
+#ifdef CONFIG_EXCEPTION_DEFER
+unsigned int deferred_regs[deferred_regs_last];
+#endif
+
+/*
+ * Handle all exceptions while running in EVT3 or EVT5
+ */
+int trap_c(struct pt_regs *regs, uint32_t level)
 {
+       uint32_t ret = 0;
        uint32_t trapnr = (regs->seqstat & EXCAUSE);
        bool data = false;
 
@@ -87,7 +97,7 @@ void trap_c(struct pt_regs *regs)
                         */
                        if (last_cplb_fault_retx != regs->retx) {
                                last_cplb_fault_retx = regs->retx;
-                               return;
+                               return ret;
                        }
                }
 
@@ -100,6 +110,20 @@ void trap_c(struct pt_regs *regs)
                uint32_t new_cplb_addr = 0, new_cplb_data = 0;
                static size_t last_evicted;
                size_t i;
+               unsigned long tflags;
+
+#ifdef CONFIG_EXCEPTION_DEFER
+               /* This should never happen */
+               if (level == 5)
+                       bfin_panic(regs);
+#endif
+
+               /*
+                * Keep the trace buffer so that a miss here points people
+                * to the right place (their code).  Crashes here rarely
+                * happen.  If they do, only the Blackfin maintainer cares.
+                */
+               trace_buffer_save(tflags);
 
                new_cplb_addr = (data ? bfin_read_DCPLB_FAULT_ADDR() : bfin_read_ICPLB_FAULT_ADDR()) & ~(4 * 1024 * 1024 - 1);
 
@@ -111,23 +135,12 @@ void trap_c(struct pt_regs *regs)
                }
                if (i == ARRAY_SIZE(bfin_memory_map)) {
                        printf("%cCPLB exception outside of memory map at 0x%p\n",
-                               (data ? 'D' : 'I'), new_cplb_addr);
+                               (data ? 'D' : 'I'), (void *)new_cplb_addr);
                        bfin_panic(regs);
                } else
                        debug("CPLB addr %p matches map 0x%p - 0x%p\n", new_cplb_addr, bfin_memory_map[i].start, bfin_memory_map[i].end);
                new_cplb_data = (data ? bfin_memory_map[i].data_flags : bfin_memory_map[i].inst_flags);
 
-               /* Turn the cache off */
-               SSYNC();
-               if (data) {
-                       asm(" .align 8; ");
-                       *pDMEM_CONTROL &= ~ENDCPLB;
-               } else {
-                       asm(" .align 8; ");
-                       *pIMEM_CONTROL &= ~ENICPLB;
-               }
-               SSYNC();
-
                if (data) {
                        CPLB_ADDR_BASE = (uint32_t *)DCPLB_ADDR0;
                        CPLB_DATA_BASE = (uint32_t *)DCPLB_DATA0;
@@ -149,8 +162,17 @@ void trap_c(struct pt_regs *regs)
 
                debug("evicting entry %i: 0x%p 0x%08X\n", i, *CPLB_ADDR, *CPLB_DATA);
                last_evicted = i + 1;
+
+               /* need to turn off cplbs whenever we muck with the cplb table */
+#if ENDCPLB != ENICPLB
+# error cplb enable bit violates my sanity
+#endif
+               uint32_t mem_control = (data ? DMEM_CONTROL : IMEM_CONTROL);
+               bfin_write32(mem_control, bfin_read32(mem_control) & ~ENDCPLB);
                *CPLB_ADDR = new_cplb_addr;
                *CPLB_DATA = new_cplb_data;
+               bfin_write32(mem_control, bfin_read32(mem_control) | ENDCPLB);
+               SSYNC();
 
                /* dump current table for debugging purposes */
                CPLB_ADDR = CPLB_ADDR_BASE;
@@ -158,24 +180,43 @@ void trap_c(struct pt_regs *regs)
                for (i = 0; i < 16; ++i)
                        debug("%2i 0x%p 0x%08X\n", i, *CPLB_ADDR++, *CPLB_DATA++);
 
-               /* Turn the cache back on */
-               SSYNC();
-               if (data) {
-                       asm(" .align 8; ");
-                       *pDMEM_CONTROL |= ENDCPLB;
-               } else {
-                       asm(" .align 8; ");
-                       *pIMEM_CONTROL |= ENICPLB;
-               }
-               SSYNC();
-
+               trace_buffer_restore(tflags);
                break;
        }
-
+#ifdef CONFIG_CMD_KGDB
+       /* Single step
+        * if we are in IRQ5, just ignore, otherwise defer, and handle it in kgdb
+        */
+       case VEC_STEP:
+               if (level == 3) {
+                       /* If we just returned from an interrupt, the single step
+                        * event is for the RTI instruction.
+                        */
+                       if (regs->retx == regs->pc)
+                               break;
+                       /* we just return if we are single stepping through IRQ5 */
+                       if (regs->ipend & 0x20)
+                               break;
+                       /* Otherwise, turn single stepping off & fall through,
+                        * which defers to IRQ5
+                        */
+                       regs->syscfg &= ~1;
+               }
+               /* fall through */
+#endif
        default:
-               /* All traps come here */
+#ifdef CONFIG_CMD_KGDB
+               if (level == 3) {
+                       /* We need to handle this at EVT5, so try again */
+                       ret = 1;
+                       break;
+               }
+               if (debugger_exception_handler && (*debugger_exception_handler)(regs))
+                       return 0;
+#endif
                bfin_panic(regs);
        }
+       return ret;
 }
 
 #ifdef CONFIG_DEBUG_DUMP
@@ -184,56 +225,32 @@ void trap_c(struct pt_regs *regs)
 # define ENABLE_DUMP 0
 #endif
 
-#ifdef CONFIG_DEBUG_DUMP_SYMS
-# define ENABLE_DUMP_SYMS 1
-#else
-# define ENABLE_DUMP_SYMS 0
-#endif
-
-static const char *symbol_lookup(unsigned long addr, unsigned long *caddr)
+#ifndef CONFIG_KALLSYMS
+const char *symbol_lookup(unsigned long addr, unsigned long *caddr)
 {
-       if (!ENABLE_DUMP_SYMS)
-               return NULL;
-
-       extern const char system_map[] __attribute__((__weak__));
-       const char *sym, *csym;
-       char *esym;
-       unsigned long sym_addr;
-
-       sym = system_map;
-       csym = NULL;
-       *caddr = 0;
-
-       while (*sym) {
-               sym_addr = simple_strtoul(sym, &esym, 16);
-               sym = esym + 1;
-               if (sym_addr > addr)
-                       break;
-               *caddr = sym_addr;
-               csym = sym;
-               sym += strlen(sym) + 1;
-       }
-
-       return csym;
+       *caddr = addr;
+       return "N/A";
 }
+#endif
 
 static void decode_address(char *buf, unsigned long address)
 {
        unsigned long sym_addr;
+       void *paddr = (void *)address;
        const char *sym = symbol_lookup(address, &sym_addr);
 
        if (sym) {
-               sprintf(buf, "<0x%p> { %s + 0x%x }", address, sym, address - sym_addr);
+               sprintf(buf, "<0x%p> { %s + 0x%lx }", paddr, sym, address - sym_addr);
                return;
        }
 
        if (!address)
-               sprintf(buf, "<0x%p> /* Maybe null pointer? */", address);
+               sprintf(buf, "<0x%p> /* Maybe null pointer? */", paddr);
        else if (address >= CONFIG_SYS_MONITOR_BASE &&
                 address < CONFIG_SYS_MONITOR_BASE + CONFIG_SYS_MONITOR_LEN)
-               sprintf(buf, "<0x%p> /* somewhere in u-boot */", address);
+               sprintf(buf, "<0x%p> /* somewhere in u-boot */", paddr);
        else
-               sprintf(buf, "<0x%p> /* unknown address */", address);
+               sprintf(buf, "<0x%p> /* unknown address */", paddr);
 }
 
 static char *strhwerrcause(uint16_t hwerrcause)
@@ -273,14 +290,16 @@ static char *strexcause(uint16_t excause)
 void dump(struct pt_regs *fp)
 {
        char buf[150];
-       size_t i;
+       int i;
        uint16_t hwerrcause, excause;
 
        if (!ENABLE_DUMP)
                return;
 
-       /* fp->ipend is garbage, so load it ourself */
+#ifndef CONFIG_CMD_KGDB
+       /* fp->ipend is normally garbage, so load it ourself */
        fp->ipend = bfin_read_IPEND();
+#endif
 
        hwerrcause = (fp->seqstat & HWERRCAUSE) >> HWERRCAUSE_P;
        excause = (fp->seqstat & EXCAUSE) >> EXCAUSE_P;
@@ -288,8 +307,8 @@ void dump(struct pt_regs *fp)
        printf("SEQUENCER STATUS:\n");
        printf(" SEQSTAT: %08lx  IPEND: %04lx  SYSCFG: %04lx\n",
                fp->seqstat, fp->ipend, fp->syscfg);
-       printf("  HWERRCAUSE: 0x%lx: %s\n", hwerrcause, strhwerrcause(hwerrcause));
-       printf("  EXCAUSE   : 0x%lx: %s\n", excause, strexcause(excause));
+       printf("  HWERRCAUSE: 0x%x: %s\n", hwerrcause, strhwerrcause(hwerrcause));
+       printf("  EXCAUSE   : 0x%x: %s\n", excause, strexcause(excause));
        for (i = 6; i <= 15; ++i) {
                if (fp->ipend & (1 << i)) {
                        decode_address(buf, bfin_read32(EVT0 + 4*i));
@@ -323,7 +342,7 @@ void dump(struct pt_regs *fp)
        printf(" P0 : %08lx    P1 : %08lx    P2 : %08lx    P3 : %08lx\n",
                fp->p0, fp->p1, fp->p2, fp->p3);
        printf(" P4 : %08lx    P5 : %08lx    FP : %08lx    SP : %08lx\n",
-               fp->p4, fp->p5, fp->fp, fp);
+               fp->p4, fp->p5, fp->fp, (unsigned long)fp);
        printf(" LB0: %08lx    LT0: %08lx    LC0: %08lx\n",
                fp->lb0, fp->lt0, fp->lc0);
        printf(" LB1: %08lx    LT1: %08lx    LC1: %08lx\n",
@@ -349,7 +368,7 @@ void dump_bfin_trace_buffer(void)
 {
        char buf[150];
        unsigned long tflags;
-       size_t i = 0;
+       int i = 0;
 
        if (!ENABLE_DUMP)
                return;