返回列表 发帖

[站长原创] 移植内核必备知识-内核移植三要素之一,Linux的IRQ中断机制

[站长原创] 移植内核必备知识-内核移植三要素之一,Linux的IRQ中断机制

移植内核必备知识-内核移植三要素之一,Linux的IRQ中断机制


嵌入式开发联盟www.mcuos.com


Osboy站长原创


QQ:82475491


Mcuos.com@gmail.com



知识点:在阅读这篇文章之前建议您先看osboy关于《Linux的中断机制的研究》的文章:http://mcuos.com/thread-5290-1-1.html了解下什么是irq_chip, irq_desc,irq handler的调用机制等研究。

(一)ok6410的中断初始化

在板级支持文件mach-mcuos6410.c中有定义:
  1. MACHINE_START(MCUOS6410, "MCUOS6410")
  2.         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
  3.         .boot_params        = S3C64XX_PA_SDRAM + 0x100,

  4.         .init_irq        = s3c6410_init_irq,
  5.         .map_io                = mcuos6410_map_io,
  6.         .init_machine        = mcuos6410_machine_init,
  7.         .timer                = &s3c24xx_timer,
  8. MACHINE_END
复制代码

有定义.init_irq        = s3c6410_init_irq在start_kernel函数中有调用
init_IRQ()函数,该函数原型:
  1. void __init init_IRQ(void)
  2. {
  3.         machine_desc->init_irq();
  4. }
复制代码

从上面的代码可以machine_desc->init_irq就是函数s3c6410_init_irq的指针。下面我们来分析:
下面的代码:
  1. void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
  2. {
  3.         printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);

  4.         /* initialise the pair of VICs */
  5.         vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0);
  6.         vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0);
  7. IRQ_VIC0_BASE ,我们有定义:
  8. #define S3C_IRQ_OFFSET        (32)
  9. #define S3C_IRQ(x)        ((x) + S3C_IRQ_OFFSET)
  10. #define IRQ_VIC0_BASE        S3C_IRQ(0)#define IRQ_VIC1_BASE        S3C_IRQ(32)
  11. 所以:IRQ_VIC0_BASE =32, IRQ_VIC1_BASE=64.ok6410的中断irq号是从32开始数的。
  12.        /* add the timer sub-irqs */
  13.         s3c_init_vic_timer_irq(5, IRQ_TIMER0);//s3c_init_vic_timer_irq 是对timer中断的初始化,这个是os的基本要素之一。
  14.         s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); //s3c_init_uart_irqs  是对uart中断的初始化,这个是os的基本要素之一。 }
复制代码

下面主要从(1)vic_init函数,(2)s3c_init_vic_timer_irq函数 (3)s3c_init_uart_irqs函数来介绍ok6410的中断初始化部分代码
(1)vic_init函数


void __init vic_init(void __iomem *base, unsigned int irq_start,
     u32 vic_sources, u32 resume_sources){
unsigned int i;
u32 cellid = 0;
enum amba_vendor vendor;

/* Identify which VIC cell this one is, by reading the ID */
for (i = 0; i < 4; i++) {
u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
cellid |= (readl(addr) & 0xff) << (8 * i);
}
vendor = (cellid >> 12) & 0xff;
printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
       base, cellid, vendor);//读取vendor,似乎6410的spec上没有说明这个0xfe0偏移量的寄存器,这里应该是AMBA_VENDOR_ARM。
switch(vendor) {
case AMBA_VENDOR_ST:
vic_init_st(base, irq_start, vic_sources);
return;
default:
printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
/* fall through */
case AMBA_VENDOR_ARM:
break;
}

/* Disable all interrupts initially. */
vic_disable(base);
vic_disable的原型为:static void __init vic_disable(void __iomem *base){
writel(0, base + VIC_INT_SELECT);//中断选择寄存器
writel(0, base + VIC_INT_ENABLE);//中断使能寄存器
writel(~0, base + VIC_INT_ENABLE_CLEAR);//中断使能清楚寄存器
writel(0, base + VIC_IRQ_STATUS);//中断状态寄存器
writel(0, base + VIC_ITCR);
writel(~0, base + VIC_INT_SOFT_CLEAR);//软件中断清除寄存器}就是清除中断选择寄存器,中断使能寄存器,中断使能清楚寄存器,中断状态寄存器,软件中断清除寄存器的值,使这些值处于reset的值。


/* Make sure we clear all existing interrupts */
vic_clear_interrupts(base);

vic_init2(base);//设置中断优先级。
vic_set_irq_sources(base, irq_start, vic_sources);
static void __init vic_set_irq_sources(void __iomem *base,
unsigned int irq_start, u32 vic_sources){
unsigned int i;

for (i = 0; i < 32; i++) {
if (vic_sources & (1 << i)) {//vic0的irq7是保留的,所以我们必须排斥这个irq7,如果i=7的话下面的code就不执行。
unsigned int irq = irq_start + i;//注意这边的irq_start是从32开始数的哦。

irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq); //初始化设置每一个irq都是电平触发。我们知道每个irq都有一个struct irq_desc对应,这个函数要做的就是初始化每个irq对应的 irq_desc
->handle_irq为handle_level_irq ,初始化每个irq对应的 irq_desc->irq_data.chip为:vic_chip 。

irq_set_chip_data(irq, base);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
}}

vic_pm_register(base, irq_start, resume_sources);}
(2)s3c_init_vic_timer_irq函数下面我们主要来分析下s3c_init_vic_timer_irq这个函数:


void __init s3c_init_vic_timer_irq(unsigned int num, unsigned int timer_irq)
{

unsigned int pirq[5] = { IRQ_TIMER0_VIC, IRQ_TIMER1_VIC, IRQ_TIMER2_VIC,


IRQ_TIMER3_VIC, IRQ_TIMER4_VIC };
//timer0-timer4的中断向量号,由于他们属于vic0,所以在linux的驱动中实际的irq号是从32开始数的,由s3c6410的spec我们可以知道,他们的中断号分别为:23,24,25,27,28.

struct irq_chip_generic *s3c_tgc;


struct irq_chip_type *ct;


unsigned int i;




s3c_tgc = irq_alloc_generic_chip("s3c-timer", 1, timer_irq,

S3C64XX_TINT_CSTAT, handle_level_irq);
//irq_alloc_generic_chip,是为timer分配一个irq_chip_generic的结构体,并且初始化它。
S3C64XX_TINT_CSTAT为timer寄存器组中的中断控制状态寄存器。它的定义为:#define S3C64XX_TINT_CSTAT    S3C_TIMERREG(0x44)。timer_irq,是调用s3c_init_vic_timer_irq的时候传入的参数:定义如下:
#define S3C64XX_TIMER_IRQ(x)
S3C_IRQ(64 + (x)),#define IRQ_TIMER0
S3C64XX_TIMER_IRQ(0),所以timer中断的irq号实际上是从64+32=96开始数的。



if (!s3c_tgc) {

pr_err("%s: irq_alloc_generic_chip for IRQ %d failed\n",


       __func__, timer_irq);


return;


}




ct = s3c_tgc->chip_types;


ct->chip.irq_mask = irq_gc_mask_clr_bit;


ct->chip.irq_unmask = irq_gc_mask_set_bit;


ct->chip.irq_ack = s3c_irq_timer_ack;


irq_setup_generic_chip(s3c_tgc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,

       IRQ_NOREQUEST | IRQ_NOPROBE, 0);
//

/* Clear the upper bits of the mask_cache*/


s3c_tgc->mask_cache &= 0x1f;


for (i = 0; i < num; i++, timer_irq++) {

irq_set_chained_handler(pirq, s3c_irq_demux_vic_timer);

irq_set_handler_data(pirq, (void *)timer_irq);

}
}整体来说s3c6410的irq,timer,uart的中断设置还是比较复杂的。移植的时候并不需要考虑太多事情,因为接口都比较成熟,我们会在后续的章节具体讨论,linux3.0内核新的中断调用实现机制。
分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

谢谢楼主对irq的机制分析

TOP

回复 1# osboy


   在认真看完站长的这IRQ机制分析以后,我个人有一个小小的疑问,就是关于中断的发生时刻,例如用外部中断,它开始时是通过外部引脚上的信号来触发外部中断,然后将中断的相关标志位置位来提示处理器有中断发生,于是我们才会知道中断发生了。那么在LINUX系统的运行过程中,它是通过那个函数,去判断中断的相关标志位是否置位了?即中断发生的第一时刻,它是通过那个函数去查它的标志位的?系统又是怎么调度这个函数的?嘿嘿,谢谢了啊!

TOP

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服