------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http://sjj0412.cublog.cn/
------------------------------------
今天讲一下s3c2410等arm开发板下linux中断相关的操作。
首先当然是进入start_kernel了,这里有中断初始化的函数
asmlinkage void
__init start_kernel(void)
{
………….
………….
init_IRQ();
……………..
……………
}
void __init init_IRQ(void)
{
struct
irqdesc *desc;
extern
void init_dma(void);
int
irq;
#ifdef CONFIG_SMP
bad_irq_desc.affinity
= CPU_MASK_ALL;
bad_irq_desc.cpu
= smp_processor_id();
#endif
for
(irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) {
*desc
= bad_irq_desc;
INIT_LIST_HEAD(&desc->pend);
}
//前面注册了哑铃中断
init_arch_irq();//注册具体开发板的中断。
init_dma();
}
init_arch_irq()这个函数又在哪里呢,它就是在这里
MACHINE_START(QQ2440, "QQ2440")
.phys_ram= S3C2410_SDRAM_PA,
.phys_io= S3C2410_PA_UART,
.io_pg_offst= (((u32)S3C24XX_VA_UART) >> 18) &
0xfffc,
.boot_params= S3C2410_SDRAM_PA + 0x100,
.init_irq=
sbc2440_init_irq,
.map_io= sbc2440_map_io,
.init_machine= sbc2440_init,
.timer= &s3c24xx_timer,
MACHINE_END
因此就调用具体开发板的中断初始函数。
sbc2440_init_irq然后调用s3c24xx_init_irq,我们知道中断在Linux以全局变量struct irqdesc
irq_desc[NR_IRQS]管理;
struct irqdesc {
irq_handler_thandle;
struct irqchip*chip;
struct irqaction *action;
struct
list_head pend;
void*chipdata;
void*data;
unsigned
intdisable_depth;
………….
…………
};
下面分析上面三个重要的成员
chip它是具体中断的操作函数集,如屏蔽开启此中断,设置中断类型。
struct irqchip
{
/*
* Acknowledge the IRQ.
* If this is a level-based IRQ, then it is
expected to mask the IRQ
* as well.
*/
void
(*ack)(unsigned int);
/*
* Mask the IRQ in hardware.
*/
void (*mask)(unsigned int);
/*
* Unmask the IRQ in hardware.
*/
void (*unmask)(unsigned int);
/*
* Ask the hardware to re-trigger the IRQ.
* Note: This method _must_ _not_ call the
interrupt handler.
* If you are unable to retrigger the
interrupt, do not
* provide a function, or if you do, return
non-zero.
*/
int
(*retrigger)(unsigned int);
/*
* Set the type of the IRQ.
*/
int (*type)(unsigned int, unsigned int);//设置中断类型是上升沿还是下降沿
/*
* Set wakeup-enable on the selected IRQ
*/
int
(*wake)(unsigned int, unsigned int);
#ifdef CONFIG_SMP
/*
* Route an interrupt to a CPU
*/
void
(*set_cpu)(struct irqdesc *desc, unsigned int irq, unsigned int cpu);
#endif
};
因此我们可以猜想s3c24xx_init_irq主要是给板子上相应中断赋具体值,事实上就是这样。
void __init s3c24xx_init_irq(void)
{
unsigned
long pend;
unsigned
long last;
int
irqno;
int
i;
irqdbf("s3c2410_init_irq:
clearing interrupt status flags\n");
/*
first, clear all interrupts pending...*/
last = 0;
for (i = 0; i < 4; i++) {
pend =
__raw_readl(S3C2410_EINTPEND);
if
(pend == 0 || pend == last)
break;
__raw_writel(pend,
S3C2410_EINTPEND);
printk("irq:
clearing pending ext status %08x\n", (int)pend);
last
= pend;
}
last
= 0;
for
(i = 0; i < 4; i++) {
pend
= __raw_readl(S3C2410_INTPND);
if
(pend == 0 || pend == last)
break;
__raw_writel(pend,
S3C2410_SRCPND);
__raw_writel(pend,
S3C2410_INTPND);
printk("irq:
clearing pending status %08x\n", (int)pend);
last
= pend;
}
last
= 0;
for
(i = 0; i < 4; i++) {
pend
= __raw_readl(S3C2410_SUBSRCPND);
if
(pend == 0 || pend == last)
break;
printk("irq:
clearing subpending status %08x\n", (int)pend);
__raw_writel(pend,
S3C2410_SUBSRCPND);
last
= pend;
}
/*
register the main interrupts */
irqdbf("s3c2410_init_irq:
registering s3c2410 interrupt handlers\n");
for
(irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) {
/*
set all the s3c2410 internal irqs */
switch
(irqno) {
/*
deal with the special IRQs (cascaded) */
case
IRQ_UART0:
case
IRQ_UART1:
case
IRQ_UART2:
case
IRQ_ADCPARENT:
set_irq_chip(irqno, &s3c_irq_level_chip);
set_irq_handler(irqno, do_level_IRQ);
break;
case
IRQ_RESERVED6:
case
IRQ_RESERVED24:
/*
no IRQ here */
break;
default:
//irqdbf("registering
irq %d (s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irq_chip);
set_irq_handler(irqno,
do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
}
/*
setup the cascade irq handlers */
set_irq_chained_handler(IRQ_UART0,
s3c_irq_demux_uart0);
set_irq_chained_handler(IRQ_UART1,
s3c_irq_demux_uart1);
set_irq_chained_handler(IRQ_UART2,
s3c_irq_demux_uart2);
set_irq_chained_handler(IRQ_ADCPARENT,
s3c_irq_demux_adc);
/*
external interrupts */
for
(irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering
irq %d (ext int)\n", irqno);
set_irq_chip(irqno,
&s3c_irq_eint0t4);
set_irq_handler(irqno,
do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <=
IRQ_EINT23; irqno++) {
irqdbf("registering
irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno,
&s3c_irqext_chip);
set_irq_handler(irqno,
do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
/*
register the uart interrupts */
irqdbf("s3c2410:
registering external interrupts\n");
for
(irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
irqdbf("registering
irq %d (s3c uart0 irq)\n", irqno);
set_irq_chip(irqno,
&s3c_irq_uart0);
set_irq_handler(irqno,
do_level_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
for
(irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
irqdbf("registering
irq %d (s3c uart1 irq)\n", irqno);
set_irq_chip(irqno,
&s3c_irq_uart1);
set_irq_handler(irqno,
do_level_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
for
(irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
irqdbf("registering
irq %d (s3c uart2 irq)\n", irqno);
set_irq_chip(irqno,
&s3c_irq_uart2);
set_irq_handler(irqno,
do_level_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_TC; irqno <= IRQ_ADC;
irqno++) {
irqdbf("registering
irq %d (s3c adc irq)\n", irqno);
set_irq_chip(irqno,
&s3c_irq_adc);
set_irq_handler(irqno,
do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
irqdbf("s3c2410:
registered interrupt handlers\n");
static struct irqchip s3c_irq_eint0t4 = {
.ack=
s3c_irq_ack,
.mask=
s3c_irq_mask,
.unmask=
s3c_irq_unmask,
.wake=
s3c_irq_wake,
.type= s3c_irqext_type,
};
static ints3c_irqext_type(unsigned int irq, unsigned int type)
{
void
__iomem *extint_reg;
void
__iomem *gpcon_reg;
unsigned
long gpcon_offset, extint_offset;
unsigned
long newvalue = 0, value;
if
((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
{
gpcon_reg
= S3C2410_GPFCON;
extint_reg
= S3C2410_EXTINT0;
gpcon_offset
= (irq - IRQ_EINT0) * 2;
extint_offset
= (irq - IRQ_EINT0) * 4;
}
else
if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
{
gpcon_reg
= S3C2410_GPFCON;
extint_reg
= S3C2410_EXTINT0;
gpcon_offset
= (irq - (EXTINT_OFF)) * 2;
extint_offset
= (irq - (EXTINT_OFF)) * 4;
}
else
if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
{
gpcon_reg
= S3C2410_GPGCON;
extint_reg
= S3C2410_EXTINT1;
gpcon_offset
= (irq - IRQ_EINT8) * 2;
extint_offset
= (irq - IRQ_EINT8) * 4;
}
else
if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
{
gpcon_reg
= S3C2410_GPGCON;
extint_reg
= S3C2410_EXTINT2;
gpcon_offset
= (irq - IRQ_EINT8) * 2;
extint_offset
= (irq - IRQ_EINT16) * 4;
}
else
return
-1;
/*
Set the GPIO to external interrupt mode */
value
= __raw_readl(gpcon_reg);
value
= (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
__raw_writel(value,
gpcon_reg);
/*
Set the external interrupt to pointed trigger type */
switch
(type)
{
case
IRQT_NOEDGE:
printk(KERN_WARNING
"No edge setting!\n");
break;
case
IRQT_RISING:
newvalue
= S3C2410_EXTINT_RISEEDGE;
break;
case
IRQT_FALLING:
newvalue
= S3C2410_EXTINT_FALLEDGE;
break;
case
IRQT_BOTHEDGE:
newvalue
= S3C2410_EXTINT_BOTHEDGE;
break;
case
IRQT_LOW:
newvalue
= S3C2410_EXTINT_LOWLEV;
break;
case
IRQT_HIGH:
newvalue
= S3C2410_EXTINT_HILEV;
break;
default:
printk(KERN_ERR
"No such irq type %d", type);
return
-1;
}
value
= __raw_readl(extint_reg);
value
= (value & ~(7 << extint_offset)) | (newvalue <<
extint_offset);
__raw_writel(value,
extint_reg);
return
0;
}
当中断初始化后我们在驱动中要使用中断时,就可以通过request_irq()申请,并且通过set_irq_type设置中断的类型,这个其实是调用具体中断的irq_chip->type函数设置的。