Merge branch 'master' of /home/cbou/linux-2.6
[pandora-kernel.git] / arch / blackfin / mach-common / entry.S
index cee54ce..eceb484 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/unistd.h>
 #include <asm/blackfin.h>
 #include <asm/errno.h>
+#include <asm/fixed_code.h>
 #include <asm/thread_info.h>  /* TIF_NEED_RESCHED */
 #include <asm/asm-offsets.h>
 #include <asm/trace.h>
 # define EX_SCRATCH_REG CYCLES
 #endif
 
-#if ANOMALY_05000281
-ENTRY(_safe_speculative_execution)
-       NOP;
-       NOP;
-       NOP;
-       jump _safe_speculative_execution;
-ENDPROC(_safe_speculative_execution)
-#endif
-
 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
 .section .l1.text
 #else
@@ -121,10 +113,14 @@ ENTRY(_ex_icplb_miss)
        (R7:6,P5:4) = [sp++];
        ASTAT = [sp++];
        SAVE_ALL_SYS
-       DEBUG_HWTRACE_SAVE(p5, r7)
 #ifdef CONFIG_MPU
+       /* We must load R1 here, _before_ DEBUG_HWTRACE_SAVE, since that
+        * will change the stack pointer.  */
        R0 = SEQSTAT;
        R1 = SP;
+#endif
+       DEBUG_HWTRACE_SAVE(p5, r7)
+#ifdef CONFIG_MPU
        sp += -12;
        call _cplb_hdr;
        sp += 12;
@@ -155,26 +151,88 @@ ENTRY(_ex_soft_bp)
 ENDPROC(_ex_soft_bp)
 
 ENTRY(_ex_single_step)
+       /* If we just returned from an interrupt, the single step event is
+          for the RTI instruction.  */
        r7 = retx;
        r6 = reti;
        cc = r7 == r6;
-       if cc jump _bfin_return_from_exception
+       if cc jump _bfin_return_from_exception;
+
+       /* Don't do single step in hardware exception handler */
+        p5.l = lo(IPEND);
+        p5.h = hi(IPEND);
+       r6 = [p5];
+       cc = bittst(r6, 5);
+       if cc jump _bfin_return_from_exception;
+
+#ifdef CONFIG_KGDB
+       /* skip single step if current interrupt priority is higher than
+        * that of the first instruction, from which gdb starts single step */
+       r6 >>= 6;
+       r7 = 10;
+.Lfind_priority_start:
+       cc = bittst(r6, 0);
+       if cc jump .Lfind_priority_done;
+       r6 >>= 1;
+       r7 += -1;
+       cc = r7 == 0;
+       if cc jump .Lfind_priority_done;
+       jump.s .Lfind_priority_start;
+.Lfind_priority_done:
+       p4.l = _debugger_step;
+       p4.h = _debugger_step;
+       r6 = [p4];
+       cc = r6 == 0;
+       if cc jump .Ldo_single_step;
+       r6 += -1;
+       cc = r6 < r7;
+       if cc jump _bfin_return_from_exception;
+.Ldo_single_step:
+#endif
+
+       /* If we were in user mode, do the single step normally.  */
+       r6 = [p5];
+       r7 = 0xffe0 (z);
+       r7 = r7 & r6;
+       cc = r7 == 0;
+       if cc jump 1f;
+
+       /*
+        * We were in an interrupt handler.  By convention, all of them save
+        * SYSCFG with their first instruction, so by checking whether our
+        * RETX points at the entry point, we can determine whether to allow
+        * a single step, or whether to clear SYSCFG.
+        *
+        * First, find out the interrupt level and the event vector for it.
+        */
+       p5.l = lo(EVT0);
+       p5.h = hi(EVT0);
+       p5 += -4;
+2:
+       r7 = rot r7 by -1;
+       p5 += 4;
+       if !cc jump 2b;
+
+       /* What we actually do is test for the _second_ instruction in the
+        * IRQ handler.  That way, if there are insns following the restore
+        * of SYSCFG after leaving the handler, we will not turn off SYSCFG
+        * for them.  */
+
+       r7 = [p5];
+       r7 += 2;
+       r6 = RETX;
+       cc = R7 == R6;
+       if !cc jump _bfin_return_from_exception;
+
+1:
+       /* Single stepping only a single instruction, so clear the trace
+        * bit here.  */
        r7 = syscfg;
        bitclr (r7, 0);
        syscfg = R7;
 
-       p5.l = lo(IPEND);
-       p5.h = hi(IPEND);
-       r6 = [p5];
-       cc = bittst(r6, 5);
-       if !cc jump _ex_trap_c;
-       p4.l = lo(EVT5);
-       p4.h = hi(EVT5);
-       r6.h = _exception_to_level5;
-       r6.l = _exception_to_level5;
-       r7 = [p4];
-       cc = r6 == r7;
-       if !cc jump _ex_trap_c;
+       jump _ex_trap_c;
+
 ENDPROC(_ex_single_step)
 
 ENTRY(_bfin_return_from_exception)
@@ -191,6 +249,7 @@ ENTRY(_bfin_return_from_exception)
 ENDPROC(_bfin_return_from_exception)
 
 ENTRY(_handle_bad_cplb)
+       DEBUG_HWTRACE_RESTORE(p5, r7)
        /* To get here, we just tried and failed to change a CPLB
         * so, handle things in trap_c (C code), by lowering to
         * IRQ5, just like we normally do. Since this is not a
@@ -225,32 +284,41 @@ ENTRY(_ex_trap_c)
        [p4] = p5;
        csync;
 
+       p4.l = lo(DCPLB_FAULT_ADDR);
+       p4.h = hi(DCPLB_FAULT_ADDR);
+       r7 = [p4];
+       p5.h = _saved_dcplb_fault_addr;
+       p5.l = _saved_dcplb_fault_addr;
+       [p5] = r7;
+
+       r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
+       p5.h = _saved_icplb_fault_addr;
+       p5.l = _saved_icplb_fault_addr;
+       [p5] = r7;
+
+       p4.l = _excpt_saved_stuff;
+       p4.h = _excpt_saved_stuff;
+
+       r6 = retx;
+       [p4] = r6;
+
+       r6 = SYSCFG;
+       [p4 + 4] = r6;
+       BITCLR(r6, 0);
+       SYSCFG = r6;
+
        /* Disable all interrupts, but make sure level 5 is enabled so
         * we can switch to that level.  Save the old mask.  */
        cli r6;
-       p4.l = _excpt_saved_imask;
-       p4.h = _excpt_saved_imask;
-       [p4] = r6;
+       [p4 + 8] = r6;
+
+       p4.l = lo(SAFE_USER_INSTRUCTION);
+       p4.h = hi(SAFE_USER_INSTRUCTION);
+       retx = p4;
+
        r6 = 0x3f;
        sti r6;
 
-       /* Save the excause into a circular buffer, in case the instruction
-        * which caused this excecptions causes others.
-        */
-       P5.l = _in_ptr_excause;
-       P5.h = _in_ptr_excause;
-       R7 = [P5];
-       R7 += 4;
-       R6 = 0xF;
-       R7 = R7 & R6;
-       [P5] = R7;
-       R6.l = _excause_circ_buf;
-       R6.h = _excause_circ_buf;
-       R7 = R7 + R6;
-       p5 = R7;
-       R6 = SEQSTAT;
-       [P5] = R6;
-
        (R7:6,P5:4) = [sp++];
        ASTAT = [sp++];
        SP = EX_SCRATCH_REG;
@@ -295,6 +363,11 @@ ENTRY(_double_fault)
          */
         SAVE_ALL_SYS
 
+       /* The dumping functions expect the return address in the RETI
+        * slot.  */
+       r6 = retx;
+       [sp + PT_PC] = r6;
+
         r0 = sp;        /* stack frame pt_regs pointer argument ==> r0 */
         SP += -12;
         call _double_fault_c;
@@ -307,11 +380,17 @@ ENDPROC(_double_fault)
 ENTRY(_exception_to_level5)
        SAVE_ALL_SYS
 
+       p4.l = _excpt_saved_stuff;
+       p4.h = _excpt_saved_stuff;
+       r6 = [p4];
+       [sp + PT_PC] = r6;
+
+       r6 = [p4 + 4];
+       [sp + PT_SYSCFG] = r6;
+
        /* Restore interrupt mask.  We haven't pushed RETI, so this
         * doesn't enable interrupts until we return from this handler.  */
-       p4.l = _excpt_saved_imask;
-       p4.h = _excpt_saved_imask;
-       r6 = [p4];
+       r6 = [p4 + 8];
        sti r6;
 
        /* Restore the hardware error vector.  */
@@ -328,42 +407,11 @@ ENTRY(_exception_to_level5)
        r0 = [p2];              /* Read current IPEND */
        [sp + PT_IPEND] = r0;   /* Store IPEND */
 
-       /* Pop the excause from the circular buffer and push it on the stack
-        * (in the right place - if you change the location of SEQSTAT, you
-        * must change this offset.
-        */
-.L_excep_to_5_again:
-       P5.l = _out_ptr_excause;
-       P5.h = _out_ptr_excause;
-       R7 = [P5];
-       R7 += 4;
-       R6 = 0xF;
-       R7 = R7 & R6;
-       [P5] = R7;
-       R6.l = _excause_circ_buf;
-       R6.h = _excause_circ_buf;
-       R7 = R7 + R6;
-       P5 = R7;
-       R1 = [P5];
-       [SP + PT_SEQSTAT] = r1;
-
        r0 = sp;        /* stack frame pt_regs pointer argument ==> r0 */
        SP += -12;
        call _trap_c;
        SP += 12;
 
-       /* See if anything else is in the exception buffer
-        * if there is, process it
-        */
-       P5.l = _out_ptr_excause;
-       P5.h = _out_ptr_excause;
-       P4.l = _in_ptr_excause;
-       P4.h = _in_ptr_excause;
-       R6 = [P5];
-       R7 = [P4];
-       CC = R6 == R7;
-       if ! CC JUMP .L_excep_to_5_again
-
        call _ret_from_exception;
        RESTORE_ALL_SYS
        rti;
@@ -727,8 +775,8 @@ ENTRY(_return_from_int)
        [p0] = p1;
        csync;
 #if ANOMALY_05000281
-       r0.l = _safe_speculative_execution;
-       r0.h = _safe_speculative_execution;
+       r0.l = lo(SAFE_USER_INSTRUCTION);
+       r0.h = hi(SAFE_USER_INSTRUCTION);
        reti = r0;
 #endif
        r0 = 0x801f (z);
@@ -741,8 +789,8 @@ ENDPROC(_return_from_int)
 
 ENTRY(_lower_to_irq14)
 #if ANOMALY_05000281
-       r0.l = _safe_speculative_execution;
-       r0.h = _safe_speculative_execution;
+       r0.l = lo(SAFE_USER_INSTRUCTION);
+       r0.h = hi(SAFE_USER_INSTRUCTION);
        reti = r0;
 #endif
        r0 = 0x401f;
@@ -809,20 +857,6 @@ _schedule_and_signal:
        rti;
 ENDPROC(_lower_to_irq14)
 
-/* Make sure when we start, that the circular buffer is initialized properly
- * R0 and P0 are call clobbered, so we can use them here.
- */
-ENTRY(_init_exception_buff)
-       r0 = 0;
-       p0.h = _in_ptr_excause;
-       p0.l = _in_ptr_excause;
-       [p0] = r0;
-       p0.h = _out_ptr_excause;
-       p0.l = _out_ptr_excause;
-       [p0] = r0;
-       rts;
-ENDPROC(_init_exception_buff)
-
 /* We handle this 100% in exception space - to reduce overhead
  * Only potiential problem is if the software buffer gets swapped out of the
  * CPLB table - then double fault. - so we don't let this happen in other places
@@ -1384,7 +1418,14 @@ ENTRY(_sys_call_table)
        .rept NR_syscalls-(.-_sys_call_table)/4
        .long _sys_ni_syscall
        .endr
-_excpt_saved_imask:
+
+       /*
+        * Used to save the real RETX, IMASK and SYSCFG when temporarily
+        * storing safe values across the transition from exception to IRQ5.
+        */
+_excpt_saved_stuff:
+       .long 0;
+       .long 0;
        .long 0;
 
 _exception_stack:
@@ -1398,17 +1439,3 @@ _exception_stack_top:
 _last_cplb_fault_retx:
        .long 0;
 #endif
-/*
- * Single instructions can have multiple faults, which need to be
- * handled by traps.c, in irq5. We store the exception cause to ensure
- * we don't miss a double fault condition
- */
-ENTRY(_in_ptr_excause)
-       .long 0;
-ENTRY(_out_ptr_excause)
-       .long 0;
-ALIGN
-ENTRY(_excause_circ_buf)
-       .rept 4
-       .long 0
-       .endr