Import changeset
[linux-flexiantxendom0-3.2.10.git] / arch / arm / kernel / fiq.c
1 /*
2  *  linux/arch/arm/kernel/fiq.c
3  *
4  *  Copyright (C) 1998 Russell King
5  *  Copyright (C) 1998, 1999 Phil Blundell
6  *
7  *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
8  *
9  *  FIQ support re-written by Russell King to be more generic
10  *
11  * We now properly support a method by which the FIQ handlers can
12  * be stacked onto the vector.  We still do not support sharing
13  * the FIQ vector itself.
14  *
15  * Operation is as follows:
16  *  1. Owner A claims FIQ:
17  *     - default_fiq relinquishes control.
18  *  2. Owner A:
19  *     - inserts code.
20  *     - sets any registers,
21  *     - enables FIQ.
22  *  3. Owner B claims FIQ:
23  *     - if owner A has a relinquish function.
24  *       - disable FIQs.
25  *       - saves any registers.
26  *       - returns zero.
27  *  4. Owner B:
28  *     - inserts code.
29  *     - sets any registers,
30  *     - enables FIQ.
31  *  5. Owner B releases FIQ:
32  *     - Owner A is asked to reacquire FIQ:
33  *       - inserts code.
34  *       - restores saved registers.
35  *       - enables FIQ.
36  *  6. Goto 3
37  */
38 #include <linux/config.h>
39 #include <linux/mm.h>
40 #include <linux/mman.h>
41 #include <linux/init.h>
42
43 #include <asm/fiq.h>
44 #include <asm/io.h>
45 #include <asm/pgalloc.h>
46 #include <asm/system.h>
47 #include <asm/uaccess.h>
48
49 #define FIQ_VECTOR 0x1c
50
51 static unsigned long no_fiq_insn;
52
53 #ifdef CONFIG_CPU_32
54 static inline void unprotect_page_0(void)
55 {
56         modify_domain(DOMAIN_USER, DOMAIN_MANAGER);
57 }
58
59 static inline void protect_page_0(void)
60 {
61         modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
62 }
63 #else
64
65 #define unprotect_page_0()
66 #define protect_page_0()
67
68 #endif
69
70 /* Default reacquire function
71  * - we always relinquish FIQ control
72  * - we always reacquire FIQ control
73  */
74 int fiq_def_op(void *ref, int relinquish)
75 {
76         if (!relinquish) {
77                 unprotect_page_0();
78                 *(unsigned long *)FIQ_VECTOR = no_fiq_insn;
79                 protect_page_0();
80                 flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4);
81         }
82
83         return 0;
84 }
85
86 static struct fiq_handler default_owner = {
87         name:   "default",
88         fiq_op: fiq_def_op,
89 };
90
91 static struct fiq_handler *current_fiq = &default_owner;
92
93 int get_fiq_list(char *buf)
94 {
95         char *p = buf;
96
97         if (current_fiq != &default_owner)
98                 p += sprintf(p, "FIQ:              %s\n",
99                              current_fiq->name);
100
101         return p - buf;
102 }
103
104 void set_fiq_handler(void *start, unsigned int length)
105 {
106         unprotect_page_0();
107
108         memcpy((void *)FIQ_VECTOR, start, length);
109
110         protect_page_0();
111         flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length);
112 }
113
114 /*
115  * Taking an interrupt in FIQ mode is death, so both these functions
116  * disable irqs for the duration. 
117  */
118 void set_fiq_regs(struct pt_regs *regs)
119 {
120         register unsigned long tmp, tmp2;
121         __asm__ volatile (
122 #ifdef CONFIG_CPU_26
123         "mov    %0, pc
124         bic     %1, %0, #0x3
125         orr     %1, %1, #0x0c000001
126         teqp    %1, #0          @ select FIQ mode
127         mov     r0, r0
128         ldmia   %2, {r8 - r14}
129         teqp    %0, #0          @ return to SVC mode
130         mov     r0, r0"
131 #endif
132 #ifdef CONFIG_CPU_32
133         "mrs    %0, cpsr
134         mov     %1, #0xc1
135         msr     cpsr_c, %1      @ select FIQ mode
136         mov     r0, r0
137         ldmia   %2, {r8 - r14}
138         msr     cpsr_c, %0      @ return to SVC mode
139         mov     r0, r0"
140 #endif
141         : "=&r" (tmp), "=&r" (tmp2)
142         : "r" (&regs->ARM_r8)
143         /* These registers aren't modified by the above code in a way
144            visible to the compiler, but we mark them as clobbers anyway
145            so that GCC won't put any of the input or output operands in
146            them.  */
147         : "r8", "r9", "r10", "r11", "r12", "r13", "r14");
148 }
149
150 void get_fiq_regs(struct pt_regs *regs)
151 {
152         register unsigned long tmp, tmp2;
153         __asm__ volatile (
154 #ifdef CONFIG_CPU_26
155         "mov    %0, pc
156         bic     %1, %0, #0x3
157         orr     %1, %1, #0x0c000001
158         teqp    %1, #0          @ select FIQ mode
159         mov     r0, r0
160         stmia   %2, {r8 - r14}
161         teqp    %0, #0          @ return to SVC mode
162         mov     r0, r0"
163 #endif
164 #ifdef CONFIG_CPU_32
165         "mrs    %0, cpsr
166         mov     %1, #0xc1
167         msr     cpsr_c, %1      @ select FIQ mode
168         mov     r0, r0
169         stmia   %2, {r8 - r14}
170         msr     cpsr_c, %0      @ return to SVC mode
171         mov     r0, r0"
172 #endif
173         : "=&r" (tmp), "=&r" (tmp2)
174         : "r" (&regs->ARM_r8)
175         /* These registers aren't modified by the above code in a way
176            visible to the compiler, but we mark them as clobbers anyway
177            so that GCC won't put any of the input or output operands in
178            them.  */
179         : "r8", "r9", "r10", "r11", "r12", "r13", "r14");
180 }
181
182 int claim_fiq(struct fiq_handler *f)
183 {
184         int ret = 0;
185
186         if (current_fiq) {
187                 ret = -EBUSY;
188
189                 if (current_fiq->fiq_op != NULL)
190                         ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
191         }
192
193         if (!ret) {
194                 f->next = current_fiq;
195                 current_fiq = f;
196         }
197
198         return ret;
199 }
200
201 void release_fiq(struct fiq_handler *f)
202 {
203         if (current_fiq != f) {
204                 printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
205                        f->name, current_fiq->name);
206 #ifdef CONFIG_DEBUG_ERRORS
207                 __backtrace();
208 #endif
209                 return;
210         }
211
212         do
213                 current_fiq = current_fiq->next;
214         while (current_fiq->fiq_op(current_fiq->dev_id, 0));
215 }
216
217 void __init init_FIQ(void)
218 {
219         no_fiq_insn = *(unsigned long *)FIQ_VECTOR;
220         set_fs(get_fs());
221 }