www.pudn.com > at91rm9200_fiq_linux_driver.rar > at91-fiq.patch, change:2008-04-22,size:9430b


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 <andy@warmcat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/arch/at91rm9200.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_pio.h>
+#include <asm/cacheflush.h>
+
+#include <asm/arch/at91rm9200_fiq_ipc_type.h>
+
+
+/*
+ * 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 <asm/arch/at91rm9200_fiq_ipc_type.h>
+ *    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 <asm/archat91rm9200_fiq_ipc_type.h>, 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);