1 --- a/arch/arm/kernel/fiq.c
2 +++ b/arch/arm/kernel/fiq.c
5 * FIQ support re-written by Russell King to be more generic
7 + * FIQ handler in C supoprt written by Andy Green <andy@openmoko.com>
9 * We now properly support a method by which the FIQ handlers can
10 * be stacked onto the vector. We still do not support sharing
11 * the FIQ vector itself.
12 @@ -124,6 +126,83 @@ void __naked get_fiq_regs(struct pt_regs
13 : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
16 +/* -------- FIQ handler in C ---------
18 + * Major Caveats for using this
19 + * ---------------------------
21 + * * 1) it CANNOT touch any vmalloc()'d memory, only memory
22 + * that was kmalloc()'d. Static allocations in the monolithic kernel
23 + * are kmalloc()'d so they are okay. You can touch memory-mapped IO, but
24 + * the pointer for it has to have been stored in kmalloc'd memory. The
25 + * reason for this is simple: every now and then Linux turns off interrupts
26 + * and reorders the paging tables. If a FIQ happens during this time, the
27 + * virtual memory space can be partly or entirely disordered or missing.
29 + * 2) Because vmalloc() is used when a module is inserted, THIS FIQ
30 + * ISR HAS TO BE IN THE MONOLITHIC KERNEL, not a module. But the way
31 + * it is set up, you can all to enable and disable it from your module
32 + * and intercommunicate with it through struct fiq_ipc
33 + * fiq_ipc which you can define in
34 + * asm/archfiq_ipc_type.h. The reason is the same as above, a
35 + * FIQ could happen while even the ISR is not present in virtual memory
36 + * space due to pagetables being changed at the time.
38 + * 3) You can't call any Linux API code except simple macros
39 + * - understand that FIQ can come in at any time, no matter what
40 + * state of undress the kernel may privately be in, thinking it
41 + * locked the door by turning off interrupts... FIQ is an
42 + * unstoppable monster force (which is its value)
43 + * - they are not vmalloc()'d memory safe
44 + * - they might do crazy stuff like sleep: FIQ pisses fire and
45 + * is not interested in 'sleep' that the weak seem to need
46 + * - calling APIs from FIQ can re-enter un-renterable things
47 + * - summary: you cannot interoperate with linux APIs directly in the FIQ ISR
49 + * If you follow these rules, it is fantastic, an extremely powerful, solid,
50 + * genuine hard realtime feature.
53 +static void (*current_fiq_c_isr)(void);
54 +#define FIQ_C_ISR_STACK_SIZE 256
56 +static void __attribute__((naked)) __jump_to_isr(void)
58 + asm __volatile__ ("mov pc, r8");
62 +static void __attribute__((naked)) __actual_isr(void)
65 + "stmdb sp!, {r0-r12, lr};"
69 + current_fiq_c_isr();
72 + "ldmia sp!, {r0-r12, lr};"
77 +void set_fiq_c_handler(void (*isr)(void))
79 + struct pt_regs regs;
81 + memset(®s, 0, sizeof(regs));
82 + regs.ARM_r8 = (unsigned long) __actual_isr;
83 + regs.ARM_sp = 0xffff001c + FIQ_C_ISR_STACK_SIZE;
85 + set_fiq_handler(__jump_to_isr, 4);
87 + current_fiq_c_isr = isr;
89 + set_fiq_regs(®s);
91 +/* -------- FIQ handler in C ---------*/
93 int claim_fiq(struct fiq_handler *f)
96 --- a/arch/arm/include/asm/fiq.h
97 +++ b/arch/arm/include/asm/fiq.h
98 @@ -29,8 +29,9 @@ struct fiq_handler {
99 extern int claim_fiq(struct fiq_handler *f);
100 extern void release_fiq(struct fiq_handler *f);
101 extern void set_fiq_handler(void *start, unsigned int length);
102 -extern void set_fiq_regs(struct pt_regs *regs);
103 -extern void get_fiq_regs(struct pt_regs *regs);
104 +extern void set_fiq_c_handler(void (*handler)(void));
105 +extern void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs);
106 +extern void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs);
107 extern void enable_fiq(int fiq);
108 extern void disable_fiq(int fiq);
110 --- a/arch/arm/plat-s3c24xx/include/plat/irq.h
111 +++ b/arch/arm/plat-s3c24xx/include/plat/irq.h
114 #include <linux/io.h>
116 +#include <mach/irqs.h>
117 #include <mach/hardware.h>
118 #include <mach/regs-irq.h>
119 #include <mach/regs-gpio.h>
120 @@ -31,8 +32,15 @@ s3c_irqsub_mask(unsigned int irqno, unsi
123 unsigned long submask;
124 +#ifdef CONFIG_S3C2440_C_FIQ
125 + unsigned long flags;
128 submask = __raw_readl(S3C2410_INTSUBMSK);
129 +#ifdef CONFIG_S3C2440_C_FIQ
130 + local_save_flags(flags);
131 + local_fiq_disable();
133 mask = __raw_readl(S3C2410_INTMSK);
135 submask |= (1UL << (irqno - IRQ_S3CUART_RX0));
136 @@ -45,6 +53,9 @@ s3c_irqsub_mask(unsigned int irqno, unsi
138 /* write back masks */
139 __raw_writel(submask, S3C2410_INTSUBMSK);
140 +#ifdef CONFIG_S3C2440_C_FIQ
141 + local_irq_restore(flags);
146 @@ -53,8 +64,15 @@ s3c_irqsub_unmask(unsigned int irqno, un
149 unsigned long submask;
150 +#ifdef CONFIG_S3C2440_C_FIQ
151 + unsigned long flags;
154 submask = __raw_readl(S3C2410_INTSUBMSK);
155 +#ifdef CONFIG_S3C2440_C_FIQ
156 + local_save_flags(flags);
157 + local_fiq_disable();
159 mask = __raw_readl(S3C2410_INTMSK);
161 submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0));
162 @@ -63,6 +81,9 @@ s3c_irqsub_unmask(unsigned int irqno, un
163 /* write back masks */
164 __raw_writel(submask, S3C2410_INTSUBMSK);
165 __raw_writel(mask, S3C2410_INTMSK);
166 +#ifdef CONFIG_S3C2440_C_FIQ
167 + local_irq_restore(flags);
172 --- a/arch/arm/plat-s3c24xx/irq.c
173 +++ b/arch/arm/plat-s3c24xx/irq.c
175 #include <asm/mach/irq.h>
177 #include <plat/regs-irqtype.h>
178 +#include <mach/regs-irq.h>
179 +#include <mach/regs-gpio.h>
181 #include <plat/cpu.h>
183 @@ -37,12 +39,20 @@ static void
184 s3c_irq_mask(unsigned int irqno)
188 +#ifdef CONFIG_S3C2440_C_FIQ
189 + unsigned long flags;
193 +#ifdef CONFIG_S3C2440_C_FIQ
194 + local_save_flags(flags);
195 + local_fiq_disable();
197 mask = __raw_readl(S3C2410_INTMSK);
198 mask |= 1UL << irqno;
199 __raw_writel(mask, S3C2410_INTMSK);
200 +#ifdef CONFIG_S3C2440_C_FIQ
201 + local_irq_restore(flags);
206 @@ -59,9 +69,19 @@ s3c_irq_maskack(unsigned int irqno)
208 unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
211 +#ifdef CONFIG_S3C2440_C_FIQ
212 + unsigned long flags;
215 +#ifdef CONFIG_S3C2440_C_FIQ
216 + local_save_flags(flags);
217 + local_fiq_disable();
219 mask = __raw_readl(S3C2410_INTMSK);
220 __raw_writel(mask|bitval, S3C2410_INTMSK);
221 +#ifdef CONFIG_S3C2440_C_FIQ
222 + local_irq_restore(flags);
225 __raw_writel(bitval, S3C2410_SRCPND);
226 __raw_writel(bitval, S3C2410_INTPND);
227 @@ -72,15 +92,25 @@ static void
228 s3c_irq_unmask(unsigned int irqno)
231 +#ifdef CONFIG_S3C2440_C_FIQ
232 + unsigned long flags;
235 if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23)
236 irqdbf2("s3c_irq_unmask %d\n", irqno);
240 +#ifdef CONFIG_S3C2440_C_FIQ
241 + local_save_flags(flags);
242 + local_fiq_disable();
244 mask = __raw_readl(S3C2410_INTMSK);
245 mask &= ~(1UL << irqno);
246 __raw_writel(mask, S3C2410_INTMSK);
247 +#ifdef CONFIG_S3C2440_C_FIQ
248 + local_irq_restore(flags);
252 struct irq_chip s3c_irq_level_chip = {
253 @@ -523,26 +553,26 @@ void __init s3c24xx_init_irq(void)
256 for (i = 0; i < 4; i++) {
257 - pend = __raw_readl(S3C2410_INTPND);
258 + pend = __raw_readl(S3C2410_SUBSRCPND);
260 if (pend == 0 || pend == last)
263 - __raw_writel(pend, S3C2410_SRCPND);
264 - __raw_writel(pend, S3C2410_INTPND);
265 - printk("irq: clearing pending status %08x\n", (int)pend);
266 + printk("irq: clearing subpending status %08x\n", (int)pend);
267 + __raw_writel(pend, S3C2410_SUBSRCPND);
272 for (i = 0; i < 4; i++) {
273 - pend = __raw_readl(S3C2410_SUBSRCPND);
274 + pend = __raw_readl(S3C2410_INTPND);
276 if (pend == 0 || pend == last)
279 - printk("irq: clearing subpending status %08x\n", (int)pend);
280 - __raw_writel(pend, S3C2410_SUBSRCPND);
281 + __raw_writel(pend, S3C2410_SRCPND);
282 + __raw_writel(pend, S3C2410_INTPND);
283 + printk("irq: clearing pending status %08x\n", (int)pend);