xref: /openbmc/linux/arch/arm/kernel/fiq.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  *  linux/arch/arm/kernel/fiq.c
3*1da177e4SLinus Torvalds  *
4*1da177e4SLinus Torvalds  *  Copyright (C) 1998 Russell King
5*1da177e4SLinus Torvalds  *  Copyright (C) 1998, 1999 Phil Blundell
6*1da177e4SLinus Torvalds  *
7*1da177e4SLinus Torvalds  *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
8*1da177e4SLinus Torvalds  *
9*1da177e4SLinus Torvalds  *  FIQ support re-written by Russell King to be more generic
10*1da177e4SLinus Torvalds  *
11*1da177e4SLinus Torvalds  * We now properly support a method by which the FIQ handlers can
12*1da177e4SLinus Torvalds  * be stacked onto the vector.  We still do not support sharing
13*1da177e4SLinus Torvalds  * the FIQ vector itself.
14*1da177e4SLinus Torvalds  *
15*1da177e4SLinus Torvalds  * Operation is as follows:
16*1da177e4SLinus Torvalds  *  1. Owner A claims FIQ:
17*1da177e4SLinus Torvalds  *     - default_fiq relinquishes control.
18*1da177e4SLinus Torvalds  *  2. Owner A:
19*1da177e4SLinus Torvalds  *     - inserts code.
20*1da177e4SLinus Torvalds  *     - sets any registers,
21*1da177e4SLinus Torvalds  *     - enables FIQ.
22*1da177e4SLinus Torvalds  *  3. Owner B claims FIQ:
23*1da177e4SLinus Torvalds  *     - if owner A has a relinquish function.
24*1da177e4SLinus Torvalds  *       - disable FIQs.
25*1da177e4SLinus Torvalds  *       - saves any registers.
26*1da177e4SLinus Torvalds  *       - returns zero.
27*1da177e4SLinus Torvalds  *  4. Owner B:
28*1da177e4SLinus Torvalds  *     - inserts code.
29*1da177e4SLinus Torvalds  *     - sets any registers,
30*1da177e4SLinus Torvalds  *     - enables FIQ.
31*1da177e4SLinus Torvalds  *  5. Owner B releases FIQ:
32*1da177e4SLinus Torvalds  *     - Owner A is asked to reacquire FIQ:
33*1da177e4SLinus Torvalds  *	 - inserts code.
34*1da177e4SLinus Torvalds  *	 - restores saved registers.
35*1da177e4SLinus Torvalds  *	 - enables FIQ.
36*1da177e4SLinus Torvalds  *  6. Goto 3
37*1da177e4SLinus Torvalds  */
38*1da177e4SLinus Torvalds #include <linux/module.h>
39*1da177e4SLinus Torvalds #include <linux/kernel.h>
40*1da177e4SLinus Torvalds #include <linux/init.h>
41*1da177e4SLinus Torvalds #include <linux/seq_file.h>
42*1da177e4SLinus Torvalds 
43*1da177e4SLinus Torvalds #include <asm/cacheflush.h>
44*1da177e4SLinus Torvalds #include <asm/fiq.h>
45*1da177e4SLinus Torvalds #include <asm/irq.h>
46*1da177e4SLinus Torvalds #include <asm/system.h>
47*1da177e4SLinus Torvalds #include <asm/uaccess.h>
48*1da177e4SLinus Torvalds 
49*1da177e4SLinus Torvalds static unsigned long no_fiq_insn;
50*1da177e4SLinus Torvalds 
51*1da177e4SLinus Torvalds /* Default reacquire function
52*1da177e4SLinus Torvalds  * - we always relinquish FIQ control
53*1da177e4SLinus Torvalds  * - we always reacquire FIQ control
54*1da177e4SLinus Torvalds  */
55*1da177e4SLinus Torvalds static int fiq_def_op(void *ref, int relinquish)
56*1da177e4SLinus Torvalds {
57*1da177e4SLinus Torvalds 	if (!relinquish)
58*1da177e4SLinus Torvalds 		set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
59*1da177e4SLinus Torvalds 
60*1da177e4SLinus Torvalds 	return 0;
61*1da177e4SLinus Torvalds }
62*1da177e4SLinus Torvalds 
63*1da177e4SLinus Torvalds static struct fiq_handler default_owner = {
64*1da177e4SLinus Torvalds 	.name	= "default",
65*1da177e4SLinus Torvalds 	.fiq_op = fiq_def_op,
66*1da177e4SLinus Torvalds };
67*1da177e4SLinus Torvalds 
68*1da177e4SLinus Torvalds static struct fiq_handler *current_fiq = &default_owner;
69*1da177e4SLinus Torvalds 
70*1da177e4SLinus Torvalds int show_fiq_list(struct seq_file *p, void *v)
71*1da177e4SLinus Torvalds {
72*1da177e4SLinus Torvalds 	if (current_fiq != &default_owner)
73*1da177e4SLinus Torvalds 		seq_printf(p, "FIQ:              %s\n", current_fiq->name);
74*1da177e4SLinus Torvalds 
75*1da177e4SLinus Torvalds 	return 0;
76*1da177e4SLinus Torvalds }
77*1da177e4SLinus Torvalds 
78*1da177e4SLinus Torvalds void set_fiq_handler(void *start, unsigned int length)
79*1da177e4SLinus Torvalds {
80*1da177e4SLinus Torvalds 	memcpy((void *)0xffff001c, start, length);
81*1da177e4SLinus Torvalds 	flush_icache_range(0xffff001c, 0xffff001c + length);
82*1da177e4SLinus Torvalds 	if (!vectors_high())
83*1da177e4SLinus Torvalds 		flush_icache_range(0x1c, 0x1c + length);
84*1da177e4SLinus Torvalds }
85*1da177e4SLinus Torvalds 
86*1da177e4SLinus Torvalds /*
87*1da177e4SLinus Torvalds  * Taking an interrupt in FIQ mode is death, so both these functions
88*1da177e4SLinus Torvalds  * disable irqs for the duration.  Note - these functions are almost
89*1da177e4SLinus Torvalds  * entirely coded in assembly.
90*1da177e4SLinus Torvalds  */
91*1da177e4SLinus Torvalds void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs)
92*1da177e4SLinus Torvalds {
93*1da177e4SLinus Torvalds 	register unsigned long tmp;
94*1da177e4SLinus Torvalds 	asm volatile (
95*1da177e4SLinus Torvalds 	"mov	ip, sp\n\
96*1da177e4SLinus Torvalds 	stmfd	sp!, {fp, ip, lr, pc}\n\
97*1da177e4SLinus Torvalds 	sub	fp, ip, #4\n\
98*1da177e4SLinus Torvalds 	mrs	%0, cpsr\n\
99*1da177e4SLinus Torvalds 	msr	cpsr_c, %2	@ select FIQ mode\n\
100*1da177e4SLinus Torvalds 	mov	r0, r0\n\
101*1da177e4SLinus Torvalds 	ldmia	%1, {r8 - r14}\n\
102*1da177e4SLinus Torvalds 	msr	cpsr_c, %0	@ return to SVC mode\n\
103*1da177e4SLinus Torvalds 	mov	r0, r0\n\
104*1da177e4SLinus Torvalds 	ldmea	fp, {fp, sp, pc}"
105*1da177e4SLinus Torvalds 	: "=&r" (tmp)
106*1da177e4SLinus Torvalds 	: "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
107*1da177e4SLinus Torvalds }
108*1da177e4SLinus Torvalds 
109*1da177e4SLinus Torvalds void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs)
110*1da177e4SLinus Torvalds {
111*1da177e4SLinus Torvalds 	register unsigned long tmp;
112*1da177e4SLinus Torvalds 	asm volatile (
113*1da177e4SLinus Torvalds 	"mov	ip, sp\n\
114*1da177e4SLinus Torvalds 	stmfd	sp!, {fp, ip, lr, pc}\n\
115*1da177e4SLinus Torvalds 	sub	fp, ip, #4\n\
116*1da177e4SLinus Torvalds 	mrs	%0, cpsr\n\
117*1da177e4SLinus Torvalds 	msr	cpsr_c, %2	@ select FIQ mode\n\
118*1da177e4SLinus Torvalds 	mov	r0, r0\n\
119*1da177e4SLinus Torvalds 	stmia	%1, {r8 - r14}\n\
120*1da177e4SLinus Torvalds 	msr	cpsr_c, %0	@ return to SVC mode\n\
121*1da177e4SLinus Torvalds 	mov	r0, r0\n\
122*1da177e4SLinus Torvalds 	ldmea	fp, {fp, sp, pc}"
123*1da177e4SLinus Torvalds 	: "=&r" (tmp)
124*1da177e4SLinus Torvalds 	: "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
125*1da177e4SLinus Torvalds }
126*1da177e4SLinus Torvalds 
127*1da177e4SLinus Torvalds int claim_fiq(struct fiq_handler *f)
128*1da177e4SLinus Torvalds {
129*1da177e4SLinus Torvalds 	int ret = 0;
130*1da177e4SLinus Torvalds 
131*1da177e4SLinus Torvalds 	if (current_fiq) {
132*1da177e4SLinus Torvalds 		ret = -EBUSY;
133*1da177e4SLinus Torvalds 
134*1da177e4SLinus Torvalds 		if (current_fiq->fiq_op != NULL)
135*1da177e4SLinus Torvalds 			ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
136*1da177e4SLinus Torvalds 	}
137*1da177e4SLinus Torvalds 
138*1da177e4SLinus Torvalds 	if (!ret) {
139*1da177e4SLinus Torvalds 		f->next = current_fiq;
140*1da177e4SLinus Torvalds 		current_fiq = f;
141*1da177e4SLinus Torvalds 	}
142*1da177e4SLinus Torvalds 
143*1da177e4SLinus Torvalds 	return ret;
144*1da177e4SLinus Torvalds }
145*1da177e4SLinus Torvalds 
146*1da177e4SLinus Torvalds void release_fiq(struct fiq_handler *f)
147*1da177e4SLinus Torvalds {
148*1da177e4SLinus Torvalds 	if (current_fiq != f) {
149*1da177e4SLinus Torvalds 		printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
150*1da177e4SLinus Torvalds 		       f->name, current_fiq->name);
151*1da177e4SLinus Torvalds 		dump_stack();
152*1da177e4SLinus Torvalds 		return;
153*1da177e4SLinus Torvalds 	}
154*1da177e4SLinus Torvalds 
155*1da177e4SLinus Torvalds 	do
156*1da177e4SLinus Torvalds 		current_fiq = current_fiq->next;
157*1da177e4SLinus Torvalds 	while (current_fiq->fiq_op(current_fiq->dev_id, 0));
158*1da177e4SLinus Torvalds }
159*1da177e4SLinus Torvalds 
160*1da177e4SLinus Torvalds void enable_fiq(int fiq)
161*1da177e4SLinus Torvalds {
162*1da177e4SLinus Torvalds 	enable_irq(fiq + FIQ_START);
163*1da177e4SLinus Torvalds }
164*1da177e4SLinus Torvalds 
165*1da177e4SLinus Torvalds void disable_fiq(int fiq)
166*1da177e4SLinus Torvalds {
167*1da177e4SLinus Torvalds 	disable_irq(fiq + FIQ_START);
168*1da177e4SLinus Torvalds }
169*1da177e4SLinus Torvalds 
170*1da177e4SLinus Torvalds EXPORT_SYMBOL(set_fiq_handler);
171*1da177e4SLinus Torvalds EXPORT_SYMBOL(set_fiq_regs);
172*1da177e4SLinus Torvalds EXPORT_SYMBOL(get_fiq_regs);
173*1da177e4SLinus Torvalds EXPORT_SYMBOL(claim_fiq);
174*1da177e4SLinus Torvalds EXPORT_SYMBOL(release_fiq);
175*1da177e4SLinus Torvalds EXPORT_SYMBOL(enable_fiq);
176*1da177e4SLinus Torvalds EXPORT_SYMBOL(disable_fiq);
177*1da177e4SLinus Torvalds 
178*1da177e4SLinus Torvalds void __init init_FIQ(void)
179*1da177e4SLinus Torvalds {
180*1da177e4SLinus Torvalds 	no_fiq_insn = *(unsigned long *)0xffff001c;
181*1da177e4SLinus Torvalds }
182