| 1 | --- a/arch/arm/kernel/fiq.c |
| 2 | +++ b/arch/arm/kernel/fiq.c |
| 3 | @@ -8,6 +8,8 @@ |
| 4 | * |
| 5 | * FIQ support re-written by Russell King to be more generic |
| 6 | * |
| 7 | + * FIQ handler in C supoprt written by Andy Green <andy@openmoko.com> |
| 8 | + * |
| 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)); |
| 14 | } |
| 15 | |
| 16 | +/* -------- FIQ handler in C --------- |
| 17 | + * |
| 18 | + * Major Caveats for using this |
| 19 | + * --------------------------- |
| 20 | + * * |
| 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. |
| 28 | + * |
| 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. |
| 37 | + * |
| 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 |
| 48 | + * |
| 49 | + * If you follow these rules, it is fantastic, an extremely powerful, solid, |
| 50 | + * genuine hard realtime feature. |
| 51 | + */ |
| 52 | + |
| 53 | +static void (*current_fiq_c_isr)(void); |
| 54 | +#define FIQ_C_ISR_STACK_SIZE 256 |
| 55 | + |
| 56 | +static void __attribute__((naked)) __jump_to_isr(void) |
| 57 | +{ |
| 58 | + asm __volatile__ ("mov pc, r8"); |
| 59 | +} |
| 60 | + |
| 61 | + |
| 62 | +static void __attribute__((naked)) __actual_isr(void) |
| 63 | +{ |
| 64 | + asm __volatile__ ( |
| 65 | + "stmdb sp!, {r0-r12, lr};" |
| 66 | + "mov fp, sp;" |
| 67 | + ); |
| 68 | + |
| 69 | + current_fiq_c_isr(); |
| 70 | + |
| 71 | + asm __volatile__ ( |
| 72 | + "ldmia sp!, {r0-r12, lr};" |
| 73 | + "subs pc, lr, #4;" |
| 74 | + ); |
| 75 | +} |
| 76 | + |
| 77 | +void set_fiq_c_handler(void (*isr)(void)) |
| 78 | +{ |
| 79 | + struct pt_regs regs; |
| 80 | + |
| 81 | + memset(®s, 0, sizeof(regs)); |
| 82 | + regs.ARM_r8 = (unsigned long) __actual_isr; |
| 83 | + regs.ARM_sp = 0xffff001c + FIQ_C_ISR_STACK_SIZE; |
| 84 | + |
| 85 | + set_fiq_handler(__jump_to_isr, 4); |
| 86 | + |
| 87 | + current_fiq_c_isr = isr; |
| 88 | + |
| 89 | + set_fiq_regs(®s); |
| 90 | +} |
| 91 | +/* -------- FIQ handler in C ---------*/ |
| 92 | + |
| 93 | int claim_fiq(struct fiq_handler *f) |
| 94 | { |
| 95 | int ret = 0; |
| 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); |
| 109 | |
| 110 | --- a/arch/arm/plat-s3c24xx/include/plat/irq.h |
| 111 | +++ b/arch/arm/plat-s3c24xx/include/plat/irq.h |
| 112 | @@ -12,6 +12,7 @@ |
| 113 | |
| 114 | #include <linux/io.h> |
| 115 | |
| 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 |
| 121 | { |
| 122 | unsigned long mask; |
| 123 | unsigned long submask; |
| 124 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 125 | + unsigned long flags; |
| 126 | +#endif |
| 127 | |
| 128 | submask = __raw_readl(S3C2410_INTSUBMSK); |
| 129 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 130 | + local_save_flags(flags); |
| 131 | + local_fiq_disable(); |
| 132 | +#endif |
| 133 | mask = __raw_readl(S3C2410_INTMSK); |
| 134 | |
| 135 | submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); |
| 136 | @@ -45,6 +53,9 @@ s3c_irqsub_mask(unsigned int irqno, unsi |
| 137 | |
| 138 | /* write back masks */ |
| 139 | __raw_writel(submask, S3C2410_INTSUBMSK); |
| 140 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 141 | + local_irq_restore(flags); |
| 142 | +#endif |
| 143 | |
| 144 | } |
| 145 | |
| 146 | @@ -53,8 +64,15 @@ s3c_irqsub_unmask(unsigned int irqno, un |
| 147 | { |
| 148 | unsigned long mask; |
| 149 | unsigned long submask; |
| 150 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 151 | + unsigned long flags; |
| 152 | +#endif |
| 153 | |
| 154 | submask = __raw_readl(S3C2410_INTSUBMSK); |
| 155 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 156 | + local_save_flags(flags); |
| 157 | + local_fiq_disable(); |
| 158 | +#endif |
| 159 | mask = __raw_readl(S3C2410_INTMSK); |
| 160 | |
| 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); |
| 168 | +#endif |
| 169 | } |
| 170 | |
| 171 | |
| 172 | --- a/arch/arm/plat-s3c24xx/irq.c |
| 173 | +++ b/arch/arm/plat-s3c24xx/irq.c |
| 174 | @@ -28,6 +28,8 @@ |
| 175 | #include <asm/mach/irq.h> |
| 176 | |
| 177 | #include <plat/regs-irqtype.h> |
| 178 | +#include <mach/regs-irq.h> |
| 179 | +#include <mach/regs-gpio.h> |
| 180 | |
| 181 | #include <plat/cpu.h> |
| 182 | #include <plat/pm.h> |
| 183 | @@ -37,12 +39,20 @@ static void |
| 184 | s3c_irq_mask(unsigned int irqno) |
| 185 | { |
| 186 | unsigned long mask; |
| 187 | - |
| 188 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 189 | + unsigned long flags; |
| 190 | +#endif |
| 191 | irqno -= IRQ_EINT0; |
| 192 | - |
| 193 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 194 | + local_save_flags(flags); |
| 195 | + local_fiq_disable(); |
| 196 | +#endif |
| 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); |
| 202 | +#endif |
| 203 | } |
| 204 | |
| 205 | static inline void |
| 206 | @@ -59,9 +69,19 @@ s3c_irq_maskack(unsigned int irqno) |
| 207 | { |
| 208 | unsigned long bitval = 1UL << (irqno - IRQ_EINT0); |
| 209 | unsigned long mask; |
| 210 | - |
| 211 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 212 | + unsigned long flags; |
| 213 | +#endif |
| 214 | + |
| 215 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 216 | + local_save_flags(flags); |
| 217 | + local_fiq_disable(); |
| 218 | +#endif |
| 219 | mask = __raw_readl(S3C2410_INTMSK); |
| 220 | __raw_writel(mask|bitval, S3C2410_INTMSK); |
| 221 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 222 | + local_irq_restore(flags); |
| 223 | +#endif |
| 224 | |
| 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) |
| 229 | { |
| 230 | unsigned long mask; |
| 231 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 232 | + unsigned long flags; |
| 233 | +#endif |
| 234 | |
| 235 | if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) |
| 236 | irqdbf2("s3c_irq_unmask %d\n", irqno); |
| 237 | |
| 238 | irqno -= IRQ_EINT0; |
| 239 | |
| 240 | +#ifdef CONFIG_S3C2440_C_FIQ |
| 241 | + local_save_flags(flags); |
| 242 | + local_fiq_disable(); |
| 243 | +#endif |
| 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); |
| 249 | +#endif |
| 250 | } |
| 251 | |
| 252 | struct irq_chip s3c_irq_level_chip = { |
| 253 | @@ -523,26 +553,26 @@ void __init s3c24xx_init_irq(void) |
| 254 | |
| 255 | last = 0; |
| 256 | for (i = 0; i < 4; i++) { |
| 257 | - pend = __raw_readl(S3C2410_INTPND); |
| 258 | + pend = __raw_readl(S3C2410_SUBSRCPND); |
| 259 | |
| 260 | if (pend == 0 || pend == last) |
| 261 | break; |
| 262 | |
| 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); |
| 268 | last = pend; |
| 269 | } |
| 270 | |
| 271 | last = 0; |
| 272 | for (i = 0; i < 4; i++) { |
| 273 | - pend = __raw_readl(S3C2410_SUBSRCPND); |
| 274 | + pend = __raw_readl(S3C2410_INTPND); |
| 275 | |
| 276 | if (pend == 0 || pend == last) |
| 277 | break; |
| 278 | |
| 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); |
| 284 | last = pend; |
| 285 | } |
| 286 | |
| 287 | |