⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_fiq.c

📁 linux内核不提供fiq的驱动的
💻 C
字号:
/* * Copyright 2007  Andy Green <andy@warmcat.com> */#include <linux/tty.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/module.h>#include <linux/kernel.h>#include <asm/arch/AT91RM9200.h>#include <asm/arch/hardware.h>#include <asm/arch/pio.h>#include <asm/cacheflush.h>#include <asm/serial.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/serial_core.h>#include <asm/arch/at91rm9200_fiq_ipc_type.h>#if 1#define PRINTK1(fmt...)	printk(fmt)#else#define PRINTK1(fmt...)	do { } while (0)#endif/* * 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 0x4000/* increase the size of the stack that is active during FIQ as needed */static u8 u8aFiqStack[16384];/* 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);static struct uart_8250_port serial8250_ports[4];EXPORT_SYMBOL(serial8250_ports);static _INLINE_ voidreceive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs){	struct tty_struct *tty = up->port.info->tty;	unsigned char ch, flag;	int max_count = 256;	PRINTK1("entering %s\n",__FUNCTION__);	do {		ch = serial_inp(up, UART_RX);		flag = TTY_NORMAL;		up->port.icount.rx++;		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |				       UART_LSR_FE | UART_LSR_OE))) {			/*			 * For statistics only			 */			if (*status & UART_LSR_BI) {				*status &= ~(UART_LSR_FE | UART_LSR_PE);				up->port.icount.brk++;				/*				 * We do the SysRQ and SAK checking				 * here because otherwise the break				 * may get masked by ignore_status_mask				 * or read_status_mask.				 */				if (uart_handle_break(&up->port))					goto ignore_char;			} else if (*status & UART_LSR_PE)				up->port.icount.parity++;			else if (*status & UART_LSR_FE)				up->port.icount.frame++;			if (*status & UART_LSR_OE)				up->port.icount.overrun++;			/*			 * Mask off conditions which should be ingored.			 */			*status &= up->port.read_status_mask;			if (*status & UART_LSR_BI) {				PRINTK1("handling break....");				flag = TTY_BREAK;			} else if (*status & UART_LSR_PE)				flag = TTY_PARITY;			else if (*status & UART_LSR_FE)				flag = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch, regs))			goto ignore_char;		if ((*status & up->port.ignore_status_mask) == 0)			tty_insert_flip_char(tty, ch, flag);		if (*status & UART_LSR_OE)			/*			 * Overrun is special, since it's reported			 * immediately, and doesn't affect the current			 * character.			 */			tty_insert_flip_char(tty, 0, TTY_OVERRUN);//		}ignore_char:		*status = serial_inp(up, UART_LSR);	} while ((*status & UART_LSR_DR) && (max_count-- > 0));	spin_unlock(&up->port.lock);	tty_flip_buffer_push(tty);	spin_lock(&up->port.lock);}static _INLINE_ void transmit_chars(struct uart_8250_port *up){	struct circ_buf *xmit = &up->port.info->xmit;	int count;	PRINTK1("entering %s\n",__FUNCTION__);	if (up->port.x_char) {		serial_outp(up, UART_TX, up->port.x_char);		up->port.icount.tx++;		up->port.x_char = 0;		return;	}	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {//		serial8250_stop_tx(&up->port,0);		return;	}	count = up->port.fifosize;	do {		serial_out(up, UART_TX, xmit->buf[xmit->tail]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		up->port.icount.tx++;		if (uart_circ_empty(xmit))			break;	} while (--count > 0);	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(&up->port);	PRINTK1("THRE...");//	if (uart_circ_empty(xmit))//		serial8250_stop_tx(&up->port,0);}static void serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs){	unsigned int status = serial_inp(up, UART_LSR);	PRINTK1("status = %x...", status);	if (status & UART_LSR_DR)		receive_chars(up, &status, regs);		if (status & UART_LSR_THRE)		transmit_chars(up);}/* the actual FIQ ISR */static void __attribute__ ((naked))at91rm9200_fiq_isr(void){	int i;	unsigned int iir;	struct pt_regs *regs;	struct uart_8250_port *up;	/*	*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 */	for(i=0;i<4;i++)	{		up=&serial8250_ports[i];				iir = serial_in(up, UART_IIR);		if (!(iir & UART_IIR_NO_INT)) {			serial8250_handle_port(up, regs);		} 	}	/* 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 */voidat91rm9200_fiq_init(void){	struct pt_regs regs;	register unsigned long tmp;		printk("Enabling FIQ\n");	/* set up PB28, the FIQ input pin */	AT91_SYS->PIOB_PDR |=AT91_PB28_FIQ;	AT91_SYS->PIOB_ODR |=AT91_PB28_FIQ;	AT91_SYS->PIOB_ASR |=AT91_PB28_FIQ;	AT91_SYS->AIC_IDCR |=1 << AT91C_ID_FIQ;	local_fiq_disable();		/* prep the special FIQ mode regs */	memset(&regs,0,sizeof(regs));	regs.ARM_r8 = (long)at91rm9200_fiq_isr;	regs.ARM_r9 = (long)AT91C_VA_BASE_SYS+66; //66,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" (&regs.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->AIC_SMR[0]=7 | AT91C_AIC_SRCTYPE;//	at91_sys_write(AT91_AIC_SMR(0), 7 | AT91C_AIC_SRCTYPE_FALLING);	/* oho... set it going! */	local_fiq_enable();//	at91_sys_write(AT91_AIC_IECR, 1 << AT91_ID_FIQ);	AT91_SYS->AIC_IECR=1 << AT91C_ID_FIQ;}EXPORT_SYMBOL(at91rm9200_fiq_init);/* call this from your kernel module disable generation of FIQ actions */voidat91rm9200_fiq_exit(void){//	at91_sys_write(AT91_AIC_IDCR, 1 << AT91_ID_FIQ);	AT91_SYS->AIC_IDCR=1 << AT91C_ID_FIQ;	local_fiq_disable();}EXPORT_SYMBOL(at91rm9200_fiq_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -