📄 at91-fiq.patch
字号:
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.odiff -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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -