diff -urN a/arch/arm/mach-at91rm9200/at91_fiq.c b/arch/arm/mach-at91rm9200/at91_fiq.c --- a/arch/arm/mach-at91rm9200/at91_fiq.c 1970-01-01 01:00:00.000000000 +0100 +++ b/arch/arm/mach-at91rm9200/at91_fiq.c 2007-08-14 13:14:10.000000000 +0100 @@ -0,0 +1,217 @@ +/* + * Copyright 2007 Andy Green + */ + +#include +#include +#include +#include +#include +#include + +#include + + +/* + * HOW TO USE + * + * 1) Customize the struct in asm/archat91rm9200_fiq_ipc_type.h for your task + * + * 2) Customize the FIQ ISR routine at91rm9200_fiq_isr() below for your task + * at "Your C Code goes here" + * + * 3) Add the following in your kernel module so you + * can communicate with the FIQ ISR using at91rm9200_fiq_ipc + * + * #include + * extern struct at91rm9200_fiq_ipc at91rm9200_fiq_ipc; + * + * 4) Have your kernel module call at91rm9200_fiq_init() on init and + * at91rm9200_fiq_exit() on exit -- prototypes are already in + * asm/arch/at91rm9200_fiq_ipc_type.h we included in step 3 + * + * 5) Trigger a FIQ by giving a falling edge to PB28 + * + * + * Major Caveats for using FIQ + * --------------------------- + * + * 1) it CANNOT touch any vmalloc()'d memory, only memory + * that was kmalloc()'d. Static allocations in the monolithic kernel + * are kmalloc()'d so they are okay. You can touch memory-mapped IO, but + * the pointer for it has to have been stored in kmalloc'd memory. The + * reason for this is simple: every now and then Linux turns off interrupts + * and reorders the paging tables. If a FIQ happens during this time, the + * virtual memory space can be partly or entirely disordered or missing. + * + * 2) Because vmalloc() is used when a module is inserted, THIS FIQ + * ISR HAS TO BE IN THE MONOLITHIC KERNEL, not a module. But the way + * it is set up, you can all to enable and disable it from your module + * and intercommunicate with it through struct at91rm9200_fiq_ipc + * at91rm9200_fiq_ipc which you can define in + * asm/archat91rm9200_fiq_ipc_type.h. The reason is the same as above, a + * FIQ could happen while even the ISR is not present in virtual memory + * space due to pagetables being changed at the time. + * + * 3) You can't call any Linux API code except simple macros + * - understand that FIQ can come in at any time, no matter what + * state of undress the kernel may privately be in, thinking it + * locked the door by turning off interrupts... FIQ is an + * unstoppable monster force (which is its value) + * - they are not vmalloc()'d memory safe + * - they might do crazy stuff like sleep: FIQ pisses fire and + * is not interested in 'sleep' that the weak seem to need + * - calling APIs from FIQ can re-enter un-renterable things + * - summary: you cannot interoperate with linux APIs directly in the FIQ ISR + * + * If you follow these rules, it is fantastic, an extremely powerful, solid, + * genuine hard realtime feature. + * + */ + +#define AT91_PB28_FIQ (1 << 28) /* A: Fast Interrupt */ + +/* actual FIQ vector address where execution starts after FIQ */ +#define AT91RM9200_FIQ_VECTOR 0xffff001c +/* more than enough to cover our jump instruction to the isr */ +#define SIZEOF_FIQ_JUMP 8 +/* more than enough to cover at91rm9200_fiq_isr() in 4K blocks */ +#define SIZEOF_FIQ_ISR 0x2000 + +/* increase the size of the stack that is active during FIQ as needed */ +static u8 u8aFiqStack[1024]; + +/* contains stuff FIQ ISR modifies and normal kernel code can see and use + * this is defined in , you should customize + * the definition in there and include the same definition in your kernel + * module that wants to interoperate with your FIQ code. + */ +struct at91rm9200_fiq_ipc at91rm9200_fiq_ipc; +EXPORT_SYMBOL(at91rm9200_fiq_ipc); + +/* the actual FIQ ISR */ +static void __attribute__ ((naked)) +at91rm9200_fiq_isr(void) +{ + /* + *you can declare local vars here, take care to set the frame size + * below accordingly if there are more than a few dozen bytes of them + */ + + /* entry takes care to store registers we will be treading on here */ + asm __volatile__ ( + "mov ip, sp ;" + /* stash FIQ and r0-r8 normal regs */ + "stmdb sp!, {r0-r8, r10-r12, lr};" + /* stash R9 separtely so we can have it first on exit */ + "stmdb sp!, {r9};" + /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ + "sub fp, ip, #256 ;" + : + : + :"r9" + ); +/* + * your C code goes here + * + * as an example, we bump a counter, you can rip that out when you + * customize + */ + at91rm9200_fiq_ipc.nCountFiqEvents++; + + + /* exit back to normal mode restoring everything */ + asm __volatile__ ( + /* pop R9 to contain &AT91_SYS->AIC_FVR */ + "ldmia sp!, {r9};" + /* read from it to acknowledge FIQ source */ + "ldr r0, [r9];" + /* return FIQ regs back to pristine state + * and get normal regs back + */ + "ldmia sp!, {r0-r8, r10-r12, lr};" + + /* return */ + "subs pc, lr, #4;" + ); +} + + +/* this is copied into the hard FIQ vector during init */ + +static void __attribute__ ((naked)) +at91rm9200_FIQ_Branch(void) +{ + asm __volatile__ ( + "mov pc, r8 ; " + ); +} + + +/* call this from your kernel module to set up the FIQ ISR to service FIQs, + * to enable the FIQ pin (PB28 on AT91RM9200) and to start accepting FIQs + */ +void +at91rm9200_fiq_init(void) +{ + struct pt_regs regs; + register unsigned long tmp; + + printk("Enabling FIQ\n"); + + /* set up PB28, the FIQ input pin */ + at91_sys_write(AT91_PIOB + PIO_PDR, AT91_PB28_FIQ); + at91_sys_write(AT91_PIOB + PIO_ODR, AT91_PB28_FIQ); + at91_sys_write(AT91_PIOB + PIO_ASR, AT91_PB28_FIQ); + + /* disable FIQ interrupt */ + at91_sys_write(AT91_AIC_IDCR, 1 << AT91_ID_FIQ); + local_fiq_disable(); + + /* prep the special FIQ mode regs */ + memset(®s,0,sizeof(regs)); + regs.ARM_r8 = (long)at91rm9200_fiq_isr; + regs.ARM_r9 = (long)AT91_VA_BASE_SYS+AT91_AIC_FVR; + regs.ARM_sp = (long)u8aFiqStack + sizeof(u8aFiqStack) - 4; + + /* set up the special FIQ-mode-only registers from our regs */ + asm volatile ( + "mrs %0, cpsr\n\ + msr cpsr_c, %2 @ select FIQ mode\n\ + mov r0, r0\n\ + ldmia %1, {r8 - r14}\n\ + msr cpsr_c, %0 @ return to SVC mode\n\ + mov r0, r0\n" + : "=&r" (tmp) + : "r" (®s.ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE) + : "r0" + ); + + /* copy our jump to the real ISR into the hard vector address */ + memcpy((void *)AT91RM9200_FIQ_VECTOR, at91rm9200_FIQ_Branch, + SIZEOF_FIQ_JUMP); + + /* flush this area */ + flush_icache_range(AT91RM9200_FIQ_VECTOR, + AT91RM9200_FIQ_VECTOR + SIZEOF_FIQ_JUMP); + flush_icache_range((long)at91rm9200_fiq_isr, + (long)at91rm9200_fiq_isr + SIZEOF_FIQ_ISR); + + /* switch FIQ to edge triggered mode */ + at91_sys_write(AT91_AIC_SMR(0), 7 | AT91_AIC_SRCTYPE_FALLING); + + /* oho... set it going! */ + local_fiq_enable(); + at91_sys_write(AT91_AIC_IECR, 1 << AT91_ID_FIQ); +} +EXPORT_SYMBOL(at91rm9200_fiq_init); + + +/* call this from your kernel module disable generation of FIQ actions */ +void +at91rm9200_fiq_exit(void) +{ + at91_sys_write(AT91_AIC_IDCR, 1 << AT91_ID_FIQ); + local_fiq_disable(); +} +EXPORT_SYMBOL(at91rm9200_fiq_exit); diff -urN a/arch/arm/mach-at91rm9200/Makefile b/mach-at91rm9200/Makefile --- a/arch/arm/mach-at91rm9200/Makefile 2007-02-17 07:47:41.000000000 +0000 +++ b/arch/arm/mach-at91rm9200/Makefile 2007-08-14 12:45:49.000000000 +0100 @@ -11,7 +11,7 @@ obj-$(CONFIG_AT91_SLOW_CLOCK) += pm_slowclock.o # CPU-specific support -obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o +obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o at91_fiq.o obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o diff -urN a/include/asm-arm/arch-at91rm9200/at91rm9200_fiq_ipc_type.h b/include/asm-arm/arch-at91rm9200/at91rm9200_fiq_ipc_type.h --- a/include/asm-arm/arch-at91rm9200/at91rm9200_fiq_ipc_type.h 1970-01-01 01:00:00.000000000 +0100 +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_fiq_ipc_type.h 2007-08-14 10:07:50.000000000 +0100 @@ -0,0 +1,26 @@ +/* + * this defines the struct which is used to communicate between the FIQ + * world and the normal linux kernel world. One of these structs is + * statically defined for you in the monolithic kernel so the FIQ ISR code + * can safely touch it any any time. + * + * You also want to include this file in your kernel module that wants to + * communicate with your FIQ code. Add any kinds of vars that are used by + * the FIQ ISR and the module in here. + * + * To get you started there is just an int that is incremented every FIQ + * you can remove this when you are ready to customize, but it is useful + * for testing + */ + +struct at91rm9200_fiq_ipc { + int nCountFiqEvents; +}; + +/* inits and begins FIQ service */ +void +at91rm9200_fiq_init(void); + +/* stops FIQ events being seen any more */ +void +at91rm9200_fiq_exit(void);