| /* |
| * Copyright 2004-2009 Analog Devices Inc. |
| * |
| * Licensed under the GPL-2 or later. |
| */ |
| |
| #ifndef __BFIN_ENTRY_H |
| #define __BFIN_ENTRY_H |
| |
| #include <asm/setup.h> |
| #include <asm/page.h> |
| |
| #ifdef __ASSEMBLY__ |
| |
| #define LFLUSH_I_AND_D 0x00000808 |
| #define LSIGTRAP 5 |
| |
| /* |
| * NOTE! The single-stepping code assumes that all interrupt handlers |
| * start by saving SYSCFG on the stack with their first instruction. |
| */ |
| |
| /* This one is used for exceptions, emulation, and NMI. It doesn't push |
| RETI and doesn't do cli. */ |
| #define SAVE_ALL_SYS save_context_no_interrupts |
| /* This is used for all normal interrupts. It saves a minimum of registers |
| to the stack, loads the IRQ number, and jumps to common code. */ |
| #ifdef CONFIG_IPIPE |
| # define LOAD_IPIPE_IPEND \ |
| P0.l = lo(IPEND); \ |
| P0.h = hi(IPEND); \ |
| R1 = [P0]; |
| #else |
| # define LOAD_IPIPE_IPEND |
| #endif |
| |
| /* |
| * Workaround for anomalies 05000283 and 05000315 |
| */ |
| #if ANOMALY_05000283 || ANOMALY_05000315 |
| # define ANOMALY_283_315_WORKAROUND(preg, dreg) \ |
| cc = dreg == dreg; \ |
| preg.h = HI(CHIPID); \ |
| preg.l = LO(CHIPID); \ |
| if cc jump 1f; \ |
| dreg.l = W[preg]; \ |
| 1: |
| #else |
| # define ANOMALY_283_315_WORKAROUND(preg, dreg) |
| #endif /* ANOMALY_05000283 || ANOMALY_05000315 */ |
| |
| #ifndef CONFIG_EXACT_HWERR |
| /* As a debugging aid - we save IPEND when DEBUG_KERNEL is on, |
| * otherwise it is a waste of cycles. |
| */ |
| # ifndef CONFIG_DEBUG_KERNEL |
| #define INTERRUPT_ENTRY(N) \ |
| [--sp] = SYSCFG; \ |
| [--sp] = P0; /*orig_p0*/ \ |
| [--sp] = R0; /*orig_r0*/ \ |
| [--sp] = (R7:0,P5:0); \ |
| R0 = (N); \ |
| LOAD_IPIPE_IPEND \ |
| jump __common_int_entry; |
| # else /* CONFIG_DEBUG_KERNEL */ |
| #define INTERRUPT_ENTRY(N) \ |
| [--sp] = SYSCFG; \ |
| [--sp] = P0; /*orig_p0*/ \ |
| [--sp] = R0; /*orig_r0*/ \ |
| [--sp] = (R7:0,P5:0); \ |
| p0.l = lo(IPEND); \ |
| p0.h = hi(IPEND); \ |
| r1 = [p0]; \ |
| R0 = (N); \ |
| LOAD_IPIPE_IPEND \ |
| jump __common_int_entry; |
| # endif /* CONFIG_DEBUG_KERNEL */ |
| |
| /* For timer interrupts, we need to save IPEND, since the user_mode |
| *macro accesses it to determine where to account time. |
| */ |
| #define TIMER_INTERRUPT_ENTRY(N) \ |
| [--sp] = SYSCFG; \ |
| [--sp] = P0; /*orig_p0*/ \ |
| [--sp] = R0; /*orig_r0*/ \ |
| [--sp] = (R7:0,P5:0); \ |
| p0.l = lo(IPEND); \ |
| p0.h = hi(IPEND); \ |
| r1 = [p0]; \ |
| R0 = (N); \ |
| jump __common_int_entry; |
| #else /* CONFIG_EXACT_HWERR is defined */ |
| |
| /* if we want hardware error to be exact, we need to do a SSYNC (which forces |
| * read/writes to complete to the memory controllers), and check to see that |
| * caused a pending HW error condition. If so, we assume it was caused by user |
| * space, by setting the same interrupt that we are in (so it goes off again) |
| * and context restore, and a RTI (without servicing anything). This should |
| * cause the pending HWERR to fire, and when that is done, this interrupt will |
| * be re-serviced properly. |
| * As you can see by the code - we actually need to do two SSYNCS - one to |
| * make sure the read/writes complete, and another to make sure the hardware |
| * error is recognized by the core. |
| * |
| * The extra nop before the SSYNC is to make sure we work around 05000244, |
| * since the 283/315 workaround includes a branch to the end |
| */ |
| #define INTERRUPT_ENTRY(N) \ |
| [--sp] = SYSCFG; \ |
| [--sp] = P0; /*orig_p0*/ \ |
| [--sp] = R0; /*orig_r0*/ \ |
| [--sp] = (R7:0,P5:0); \ |
| R1 = ASTAT; \ |
| ANOMALY_283_315_WORKAROUND(p0, r0) \ |
| P0.L = LO(ILAT); \ |
| P0.H = HI(ILAT); \ |
| NOP; \ |
| SSYNC; \ |
| SSYNC; \ |
| R0 = [P0]; \ |
| CC = BITTST(R0, EVT_IVHW_P); \ |
| IF CC JUMP 1f; \ |
| ASTAT = R1; \ |
| p0.l = lo(IPEND); \ |
| p0.h = hi(IPEND); \ |
| r1 = [p0]; \ |
| R0 = (N); \ |
| LOAD_IPIPE_IPEND \ |
| jump __common_int_entry; \ |
| 1: ASTAT = R1; \ |
| RAISE N; \ |
| (R7:0, P5:0) = [SP++]; \ |
| SP += 0x8; \ |
| SYSCFG = [SP++]; \ |
| CSYNC; \ |
| RTI; |
| |
| #define TIMER_INTERRUPT_ENTRY(N) \ |
| [--sp] = SYSCFG; \ |
| [--sp] = P0; /*orig_p0*/ \ |
| [--sp] = R0; /*orig_r0*/ \ |
| [--sp] = (R7:0,P5:0); \ |
| R1 = ASTAT; \ |
| ANOMALY_283_315_WORKAROUND(p0, r0) \ |
| P0.L = LO(ILAT); \ |
| P0.H = HI(ILAT); \ |
| NOP; \ |
| SSYNC; \ |
| SSYNC; \ |
| R0 = [P0]; \ |
| CC = BITTST(R0, EVT_IVHW_P); \ |
| IF CC JUMP 1f; \ |
| ASTAT = R1; \ |
| p0.l = lo(IPEND); \ |
| p0.h = hi(IPEND); \ |
| r1 = [p0]; \ |
| R0 = (N); \ |
| jump __common_int_entry; \ |
| 1: ASTAT = R1; \ |
| RAISE N; \ |
| (R7:0, P5:0) = [SP++]; \ |
| SP += 0x8; \ |
| SYSCFG = [SP++]; \ |
| CSYNC; \ |
| RTI; |
| #endif /* CONFIG_EXACT_HWERR */ |
| |
| /* This one pushes RETI without using CLI. Interrupts are enabled. */ |
| #define SAVE_CONTEXT_SYSCALL save_context_syscall |
| #define SAVE_CONTEXT save_context_with_interrupts |
| #define SAVE_CONTEXT_CPLB save_context_cplb |
| |
| #define RESTORE_ALL_SYS restore_context_no_interrupts |
| #define RESTORE_CONTEXT restore_context_with_interrupts |
| #define RESTORE_CONTEXT_CPLB restore_context_cplb |
| |
| #endif /* __ASSEMBLY__ */ |
| #endif /* __BFIN_ENTRY_H */ |