File: /usr/src/linux/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/module.h>
40     #include <linux/mm.h>
41     #include <linux/mman.h>
42     #include <linux/init.h>
43     
44     #include <asm/fiq.h>
45     #include <asm/io.h>
46     #include <asm/irq.h>
47     #include <asm/pgalloc.h>
48     #include <asm/system.h>
49     #include <asm/uaccess.h>
50     
51     #define FIQ_VECTOR (vectors_base() + 0x1c)
52     
53     static unsigned long no_fiq_insn;
54     
55     #ifdef CONFIG_CPU_32
56     static inline void unprotect_page_0(void)
57     {
58     	modify_domain(DOMAIN_USER, DOMAIN_MANAGER);
59     }
60     
61     static inline void protect_page_0(void)
62     {
63     	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
64     }
65     #else
66     
67     #define unprotect_page_0()
68     #define protect_page_0()
69     
70     #endif
71     
72     /* Default reacquire function
73      * - we always relinquish FIQ control
74      * - we always reacquire FIQ control
75      */
76     static int fiq_def_op(void *ref, int relinquish)
77     {
78     	if (!relinquish) {
79     		unprotect_page_0();
80     		*(unsigned long *)FIQ_VECTOR = no_fiq_insn;
81     		protect_page_0();
82     		flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4);
83     	}
84     
85     	return 0;
86     }
87     
88     static struct fiq_handler default_owner = {
89     	name:	"default",
90     	fiq_op:	fiq_def_op,
91     };
92     
93     static struct fiq_handler *current_fiq = &default_owner;
94     
95     int get_fiq_list(char *buf)
96     {
97     	char *p = buf;
98     
99     	if (current_fiq != &default_owner)
100     		p += sprintf(p, "FIQ:              %s\n",
101     			     current_fiq->name);
102     
103     	return p - buf;
104     }
105     
106     void set_fiq_handler(void *start, unsigned int length)
107     {
108     	unprotect_page_0();
109     
110     	memcpy((void *)FIQ_VECTOR, start, length);
111     
112     	protect_page_0();
113     	flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length);
114     }
115     
116     /*
117      * Taking an interrupt in FIQ mode is death, so both these functions
118      * disable irqs for the duration. 
119      */
120     void set_fiq_regs(struct pt_regs *regs)
121     {
122     	register unsigned long tmp, tmp2;
123     	__asm__ volatile (
124     #ifdef CONFIG_CPU_26
125     	"mov	%0, pc
126     	bic	%1, %0, #0x3
127     	orr	%1, %1, %3
128     	teqp	%1, #0		@ select FIQ mode
129     	mov	r0, r0
130     	ldmia	%2, {r8 - r14}
131     	teqp	%0, #0		@ return to SVC mode
132     	mov	r0, r0"
133     #endif
134     #ifdef CONFIG_CPU_32
135     	"mrs	%0, cpsr
136     	mov	%1, %3
137     	msr	cpsr_c, %1	@ select FIQ mode
138     	mov	r0, r0
139     	ldmia	%2, {r8 - r14}
140     	msr	cpsr_c, %0	@ return to SVC mode
141     	mov	r0, r0"
142     #endif
143     	: "=&r" (tmp), "=&r" (tmp2)
144     	: "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
145     	/* These registers aren't modified by the above code in a way
146     	   visible to the compiler, but we mark them as clobbers anyway
147     	   so that GCC won't put any of the input or output operands in
148     	   them.  */
149     	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
150     }
151     
152     void get_fiq_regs(struct pt_regs *regs)
153     {
154     	register unsigned long tmp, tmp2;
155     	__asm__ volatile (
156     #ifdef CONFIG_CPU_26
157     	"mov	%0, pc
158     	bic	%1, %0, #0x3
159     	orr	%1, %1, %3
160     	teqp	%1, #0		@ select FIQ mode
161     	mov	r0, r0
162     	stmia	%2, {r8 - r14}
163     	teqp	%0, #0		@ return to SVC mode
164     	mov	r0, r0"
165     #endif
166     #ifdef CONFIG_CPU_32
167     	"mrs	%0, cpsr
168     	mov	%1, %3
169     	msr	cpsr_c, %1	@ select FIQ mode
170     	mov	r0, r0
171     	stmia	%2, {r8 - r14}
172     	msr	cpsr_c, %0	@ return to SVC mode
173     	mov	r0, r0"
174     #endif
175     	: "=&r" (tmp), "=&r" (tmp2)
176     	: "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
177     	/* These registers aren't modified by the above code in a way
178     	   visible to the compiler, but we mark them as clobbers anyway
179     	   so that GCC won't put any of the input or output operands in
180     	   them.  */
181     	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
182     }
183     
184     int claim_fiq(struct fiq_handler *f)
185     {
186     	int ret = 0;
187     
188     	if (current_fiq) {
189     		ret = -EBUSY;
190     
191     		if (current_fiq->fiq_op != NULL)
192     			ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
193     	}
194     
195     	if (!ret) {
196     		f->next = current_fiq;
197     		current_fiq = f;
198     	}
199     
200     	return ret;
201     }
202     
203     void release_fiq(struct fiq_handler *f)
204     {
205     	if (current_fiq != f) {
206     		printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
207     		       f->name, current_fiq->name);
208     #ifdef CONFIG_DEBUG_ERRORS
209     		__backtrace();
210     #endif
211     		return;
212     	}
213     
214     	do
215     		current_fiq = current_fiq->next;
216     	while (current_fiq->fiq_op(current_fiq->dev_id, 0));
217     }
218     
219     void enable_fiq(int fiq)
220     {
221     	enable_irq(fiq + FIQ_START);
222     }
223     
224     void disable_fiq(int fiq)
225     {
226     	disable_irq(fiq + FIQ_START);
227     }
228     
229     EXPORT_SYMBOL(set_fiq_handler);
230     EXPORT_SYMBOL(set_fiq_regs);
231     EXPORT_SYMBOL(get_fiq_regs);
232     EXPORT_SYMBOL(claim_fiq);
233     EXPORT_SYMBOL(release_fiq);
234     EXPORT_SYMBOL(enable_fiq);
235     EXPORT_SYMBOL(disable_fiq);
236     
237     void __init init_FIQ(void)
238     {
239     	no_fiq_insn = *(unsigned long *)FIQ_VECTOR;
240     	set_fs(get_fs());
241     }
242