2008年9月10日星期三

共用中断源

多个事件共用中断源应该是很常见的,比如GPIO或外围设备
1 只调用一次request_irq,所有中断都根据注册的函数来判断状态而分发
2 自己初始化irq,所有中断事件都可调用request_irq挂自己的处理函数,当然分发函数还是存在,只是在do_IRQ之前
拿pcf50606的中断代码来说,平台是pxa270

1>
#define GPIOX_TO_IRQ(x) \
((x) - 2 + 64)
#define IRQ_GPIO(x) (((x) < 2) ? (IRQ_GPIO0 + (x)) : GPIOX_TO_IRQ(x))
#define PCF50606_IRQ(x) (IRQ_GPIO(128) + 1 + (x)) //也就是虚拟中断源号
void __init pcf50606_init_irq(void)
{
... //读pcf寄存器来清中断并设置中断掩码,未贴
// initialize PCF interrupt related registers //手动初始化虚拟中断源对应的irq_desc
for ( irq = PCF50606_IRQ(0); irq <= PCF50606_IRQ( MAX_PCF_INT ) ; irq++ ) {
irq_desc[irq].valid = 1;
irq_desc[irq].probe_ok = 1;
irq_desc[irq].mask_ack = PCF50606_mask_and_ack_irq;
irq_desc[irq].mask = PCF50606_mask_irq;
irq_desc[irq].unmask = PCF50606_unmask_irq;
//mask,unmask函数与enable_irq/disable_irq对应,把使能禁止中断对应到设置pcf50606中断mask中
}

set_GPIO_IRQ_edge( GPIO_PCF50606_INT , GPIO_FALLING_EDGE ); //相当于request_irq里的触发中断类型
setup_arm_irq( IRQ_GPIO( GPIO_PCF50606_INT ) , &pcf50606_irq ); //挂一个包含中断分发函数的数据结构pcf50606_irq
enable_irq( IRQ_GPIO(GPIO_PCF50606_INT) ); //request_irq函数最后也有enable_irq
}

2>
//中断action结构及处理函数pcf50606_irq_demux
static struct irqaction pcf50606_irq = {
name: "pcf50606",
handler: pcf50606_irq_demux,
flags: SA_INTERRUPT
};

void pcf50606_irq_demux( int irq, void *dev_id, struct pt_regs *regs )
{
int i ;
unsigned long stat0;
int flags;
//关中断
save_and_cli(flags);
disable_irq( IRQ_GPIO( GPIO_PCF50606_INT ));

while(1) {
stat0 = readPCFregister( PCF50606_INT3 ) << 16 |
readPCFregister( PCF50606_INT2 ) << 8 |
readPCFregister( PCF50606_INT1 );
//根据pcf50606中断寄存器中的值判断是哪个中断,调用do_IRQ,最终会走到它们注册该虚拟中断源的函数,因为同时可能产生多个中断,所以while里把所有产生中断的处理函数都执行一遍
for ( i = 0 ; i < 24 ; i++ )
if ( stat0 & ( 1 << i ) )
do_IRQ( PCF50606_IRQ(i), regs );
}
//开中断
enable_irq( IRQ_GPIO( GPIO_PCF50606_INT ));
restore_flags(flags);
}

3>
在某处注册希望处理的中断
err = request_irq ( PCF50606_ONKEYFM_IRQ, onkey_interrupt, 0, "Second", NULL );
err = request_irq ( PCF50606_ONKEYRM_IRQ , onkey_interrupt, 0, "Second", NULL );
err = request_irq ( PCF50606_EXTONRM_IRQ , extonkey_interrupt, 0, "Second", NULL );
err = request_irq ( PCF50606_EXTONFM_IRQ , extonkey_interrupt, 0, "Second", NULL );
虚拟中断源的号当然也要分配好,这里是191以上了
#define PCF50606_ONKEYRM_IRQ PCF50606_IRQ(0)
#define PCF50606_ONKEYFM_IRQ PCF50606_IRQ(1)
#define PCF50606_ONKEY1SM_IRQ PCF50606_IRQ(2)
#define PCF50606_EXTONRM_IRQ PCF50606_IRQ(3)
#define PCF50606_EXTONFM_IRQ PCF50606_IRQ(4)
...

4>
顺便贴一下mask/unmask

static void PCF50606_mask_irq( unsigned int irq )
{
unsigned int bit_value = ( irq - PCF50606_IRQ(0)) ;
unsigned int mask = 1 << (bit_value % 8 );
if ( bit_value > 15 )
writePCFregister( PCF50606_INT3M, readPCFregister( PCF50606_INT3M ) | mask ) ;
else if ( bit_value > 7 )
writePCFregister( PCF50606_INT2M, readPCFregister( PCF50606_INT2M ) | mask ) ;
else
writePCFregister( PCF50606_INT1M, readPCFregister( PCF50606_INT1M ) | mask ) ;
}

static void PCF50606_unmask_irq( unsigned int irq )
{
unsigned int bit_value = ( irq - PCF50606_IRQ(0)) ;
unsigned int mask = 1 << (bit_value % 8 );

if ( bit_value > 15 )
writePCFregister( PCF50606_INT3M, readPCFregister( PCF50606_INT3M ) & ~mask ) ;
else if ( bit_value > 7 )
writePCFregister( PCF50606_INT2M, readPCFregister( PCF50606_INT2M ) & ~mask ) ;
else
writePCFregister( PCF50606_INT1M, readPCFregister( PCF50606_INT1M ) & ~mask ) ;
}

换个平台试试看我漏掉些什么没。

没有评论: