Chinaunix首页 | 论坛 | 博客
  • 博客访问: 507593
  • 博文数量: 95
  • 博客积分: 5168
  • 博客等级: 大校
  • 技术积分: 1271
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-28 23:31
文章分类

全部博文(95)

文章存档

2013年(2)

2012年(3)

2011年(1)

2010年(8)

2009年(81)

分类: LINUX

2009-03-06 22:59:53

------------------------------------
本文系本站原创,欢迎转载!

转载请注明出处:http://sjj0412.cublog.cn/

------------------------------------

今天讲一下s3c2410arm开发板下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_t   handle;

       struct irqchip  *chip;

       struct irqaction *action;

       struct list_head pend;

       void        *chipdata;

       void        *data;

       unsigned int    disable_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 int  s3c_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函数设置的。

阅读(1531) | 评论(0) | 转发(0) |
0

上一篇:根文件系统挂载

下一篇:boa分析及移植

给主人留下些什么吧!~~