1/*
2 * Copyright 2004-2009 Analog Devices Inc.
3 *
4 * Licensed under the GPL-2 or later.
5 */
6
7#ifndef __BFIN_ENTRY_H
8#define __BFIN_ENTRY_H
9
10#include <asm/setup.h>
11#include <asm/page.h>
12
13#ifdef __ASSEMBLY__
14
15#define	LFLUSH_I_AND_D	0x00000808
16#define	LSIGTRAP	5
17
18/*
19 * NOTE!  The single-stepping code assumes that all interrupt handlers
20 * start by saving SYSCFG on the stack with their first instruction.
21 */
22
23/* This one is used for exceptions, emulation, and NMI.  It doesn't push
24   RETI and doesn't do cli.  */
25#define SAVE_ALL_SYS		save_context_no_interrupts
26/* This is used for all normal interrupts.  It saves a minimum of registers
27   to the stack, loads the IRQ number, and jumps to common code.  */
28#ifdef CONFIG_IPIPE
29# define LOAD_IPIPE_IPEND \
30	P0.l = lo(IPEND); \
31	P0.h = hi(IPEND); \
32	R1 = [P0];
33#else
34# define LOAD_IPIPE_IPEND
35#endif
36
37/*
38 * Workaround for anomalies 05000283 and 05000315
39 */
40#if ANOMALY_05000283 || ANOMALY_05000315
41# define ANOMALY_283_315_WORKAROUND(preg, dreg)		\
42	cc = dreg == dreg;				\
43	preg.h = HI(CHIPID);				\
44	preg.l = LO(CHIPID);				\
45	if cc jump 1f;					\
46	dreg.l = W[preg];				\
471:
48#else
49# define ANOMALY_283_315_WORKAROUND(preg, dreg)
50#endif /* ANOMALY_05000283 || ANOMALY_05000315 */
51
52#ifndef CONFIG_EXACT_HWERR
53/* As a debugging aid - we save IPEND when DEBUG_KERNEL is on,
54 * otherwise it is a waste of cycles.
55 */
56# ifndef CONFIG_DEBUG_KERNEL
57#define INTERRUPT_ENTRY(N)						\
58    [--sp] = SYSCFG;							\
59    [--sp] = P0;	/*orig_p0*/					\
60    [--sp] = R0;	/*orig_r0*/					\
61    [--sp] = (R7:0,P5:0);						\
62    R0 = (N);								\
63    LOAD_IPIPE_IPEND							\
64    jump __common_int_entry;
65# else /* CONFIG_DEBUG_KERNEL */
66#define INTERRUPT_ENTRY(N)						\
67    [--sp] = SYSCFG;							\
68    [--sp] = P0;	/*orig_p0*/					\
69    [--sp] = R0;	/*orig_r0*/					\
70    [--sp] = (R7:0,P5:0);						\
71    p0.l = lo(IPEND);							\
72    p0.h = hi(IPEND);							\
73    r1 = [p0];								\
74    R0 = (N);								\
75    LOAD_IPIPE_IPEND							\
76    jump __common_int_entry;
77# endif /* CONFIG_DEBUG_KERNEL */
78
79/* For timer interrupts, we need to save IPEND, since the user_mode
80 *macro accesses it to determine where to account time.
81 */
82#define TIMER_INTERRUPT_ENTRY(N)					\
83    [--sp] = SYSCFG;							\
84    [--sp] = P0;	/*orig_p0*/					\
85    [--sp] = R0;	/*orig_r0*/					\
86    [--sp] = (R7:0,P5:0);						\
87    p0.l = lo(IPEND);							\
88    p0.h = hi(IPEND);							\
89    r1 = [p0];								\
90    R0 = (N);								\
91    jump __common_int_entry;
92#else /* CONFIG_EXACT_HWERR is defined */
93
94/* if we want hardware error to be exact, we need to do a SSYNC (which forces
95 * read/writes to complete to the memory controllers), and check to see that
96 * caused a pending HW error condition. If so, we assume it was caused by user
97 * space, by setting the same interrupt that we are in (so it goes off again)
98 * and context restore, and a RTI (without servicing anything). This should
99 * cause the pending HWERR to fire, and when that is done, this interrupt will
100 * be re-serviced properly.
101 * As you can see by the code - we actually need to do two SSYNCS - one to
102 * make sure the read/writes complete, and another to make sure the hardware
103 * error is recognized by the core.
104 *
105 * The extra nop before the SSYNC is to make sure we work around 05000244,
106 * since the 283/315 workaround includes a branch to the end
107 */
108#define INTERRUPT_ENTRY(N)						\
109    [--sp] = SYSCFG;							\
110    [--sp] = P0;	/*orig_p0*/					\
111    [--sp] = R0;	/*orig_r0*/					\
112    [--sp] = (R7:0,P5:0);						\
113    R1 = ASTAT;								\
114    ANOMALY_283_315_WORKAROUND(p0, r0)					\
115    P0.L = LO(ILAT);							\
116    P0.H = HI(ILAT);							\
117    NOP;								\
118    SSYNC;								\
119    SSYNC;								\
120    R0 = [P0];								\
121    CC = BITTST(R0, EVT_IVHW_P);					\
122    IF CC JUMP 1f;							\
123    ASTAT = R1;								\
124    p0.l = lo(IPEND);							\
125    p0.h = hi(IPEND);							\
126    r1 = [p0];								\
127    R0 = (N);								\
128    LOAD_IPIPE_IPEND							\
129    jump __common_int_entry;						\
1301:  ASTAT = R1;								\
131    RAISE N;								\
132    (R7:0, P5:0) = [SP++];						\
133    SP += 0x8;								\
134    SYSCFG = [SP++];							\
135    CSYNC;								\
136    RTI;
137
138#define TIMER_INTERRUPT_ENTRY(N)					\
139    [--sp] = SYSCFG;							\
140    [--sp] = P0;	/*orig_p0*/					\
141    [--sp] = R0;	/*orig_r0*/					\
142    [--sp] = (R7:0,P5:0);						\
143    R1 = ASTAT;								\
144    ANOMALY_283_315_WORKAROUND(p0, r0)					\
145    P0.L = LO(ILAT);							\
146    P0.H = HI(ILAT);							\
147    NOP;								\
148    SSYNC;								\
149    SSYNC;								\
150    R0 = [P0];								\
151    CC = BITTST(R0, EVT_IVHW_P);					\
152    IF CC JUMP 1f;							\
153    ASTAT = R1;								\
154    p0.l = lo(IPEND);							\
155    p0.h = hi(IPEND);							\
156    r1 = [p0];								\
157    R0 = (N);								\
158    jump __common_int_entry;						\
1591:  ASTAT = R1;								\
160    RAISE N;								\
161    (R7:0, P5:0) = [SP++];						\
162    SP += 0x8;								\
163    SYSCFG = [SP++];							\
164    CSYNC;								\
165    RTI;
166#endif	/* CONFIG_EXACT_HWERR */
167
168/* This one pushes RETI without using CLI.  Interrupts are enabled.  */
169#define SAVE_CONTEXT_SYSCALL	save_context_syscall
170#define SAVE_CONTEXT		save_context_with_interrupts
171#define SAVE_CONTEXT_CPLB	save_context_cplb
172
173#define RESTORE_ALL_SYS		restore_context_no_interrupts
174#define RESTORE_CONTEXT		restore_context_with_interrupts
175#define RESTORE_CONTEXT_CPLB	restore_context_cplb
176
177#endif				/* __ASSEMBLY__ */
178#endif				/* __BFIN_ENTRY_H */
179