基于设备树的中断实现 (24x0平台)

一、平台信息

1. 平台信息

  • 以2440开发板为例,参考s3c2440的数据手册,得到如下中断寄存器信息:
1、SRCPND 地址: 0x4A000000 功能:每一位代表一个主中断,置1表示有对应的主中断请求,对应位写入1可以清除中断
2、INTMOD 地址: 0x4A000004 功能:设置对应的主中断为IRQ还是FIQ, 置1表示FIQ
3、INTMSK 地址: 0x4A000008 功能:置1表示对应的主中断被屏蔽(不会影响SRCPND)
4、INTPND 地址: 0x4A000010 功能:表示对应的主中断被request,只可能有一位被置位,写入1可以清除中断
5、INTOFFSET 地址:0x4A000014 功能:存放的是发生中断请求的主中断号
6、SUBSRCPND 地址:0x4A000018 功能:每一位代表一个子中断,置一表示对应子中断请求,对应位写入1清除子中断请求
7、INTSUBMSK 地址:0x4A00001C 功能:置1表示对应的子中断被屏蔽

在这里插入图片描述

二、中断的驱动实现方式

2. 不使用设备树的中断实现

2.1 mach-mini2440.c文件中注册中断

  • 不用设备树时,如mini2440,在mach-mini2440.c中写入s3c2440_init_irq
#mach-mini2440.c里注册irq-s3c24xx.c    drivers\irqchip    中的s3c2440_init_irq中断初始化函数
mach-mini2440.c    arch\arm\mach-s3c24xx    17942    2018/9/10    113
MACHINE_START(MINI2440, "MINI2440")
    /* Maintainer: Michel Pollet <buserror@gmail.com> */
    .atag_offset    = 0x100,
    .map_io        = mini2440_map_io,
    .init_machine    = mini2440_init,
    .init_irq    = s3c2440_init_irq,
    .init_time    = mini2440_init_time,
MACHINE_END

2.2 注册中断

  • s3c2440_init_irq 给s3c_intc[]初始化
  • 中断控制器的基地址在程序中写死
//s3c_intc[0]为主控制器,地址0x4a000000
//s3c_intc[1]为子控制器,地址0x4a000018

void __init s3c2440_init_irq(void)
{
    pr_info("S3C2440: IRQ Support\n");

#ifdef CONFIG_FIQ
    init_FIQ(FIQ_START);
#endif

    s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL,
                    0x4a000000);
    if (IS_ERR(s3c_intc[0])) {
        pr_err("irq: could not create main interrupt controller\n");
        return;
    }

    s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
    s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0],
                    s3c_intc[0], 0x4a000018);
}

[===========]

3. 使用设备树的中断实现

设备树的编译与反编译

编译设备树

make dtbs
或
dtc -I dts -O dtb  *.dts > my.dtb
  • arch/arm/boot/dts/Makefile会读取内核下include/config/auto.conf文件
  • 若CONFIG_ARCH_S3C24XX=y,编译arch/arm/boot/dts/Makefile对应的dts文件

反编译设备树
可参考附录的s3c2416-smdk2416.dts代码

./scripts/dtc/dtc -I dtb -O dts arch/arm/boot/dts/s3c2416-smdk2416.dtb > s3c2416-smdk2416.dts

设备树中断参数的修改

可以通过设备树向内核传递参数,但是参数如何传递和解析由程序决定。

  • 通过查询内核文档可以知道怎么编写设备树
##通过以下命令,可以查询到相关文档的信息
grep "\"samsung,s3c2440-uart\"" * -nR
  • 以2440开发板为例,参考
Documentation\devicetree\bindings\interrupt-controller\samsung,s3c24xx-irq.txt
  • 得到的重要信息
Required properties:
- compatible: Compatible property value should be "samsung,s3c2410-irq"
  for machines before s3c2416 and "samsung,s3c2416-irq" for s3c2416 and later.

- reg: Physical base address of the controller and length of memory mapped
  region.

- interrupt-controller : Identifies the node as an interrupt controller

- #interrupt-cells : Specifies the number of cells needed to encode an
  interrupt source. The value shall be 4 and interrupt descriptor shall
  have the following format:
      <ctrl_num parent_irq ctrl_irq type>

  ctrl_num contains the controller to use:
      - 0 ... main controller
      - 1 ... sub controller
      - 2 ... second main controller on s3c2416 and s3c2450
  parent_irq contains the parent bit in the main controller and will be
             ignored in main controllers
  ctrl_irq contains the interrupt bit of the controller
  type contains the trigger type to use
  • 分析设备树
  1. 中断控制器
intc:interrupt-controller@4a000000 {
compatible = "samsung,s3c2410-irq";
reg = <0x4a000000 0x100>;    //第一个参数是寄存器地址,第二个参数是映射多大,一般映射有余量
interrupt-controller;
#interrupt-cells = <4>;
};
  1. 引用
serial@50000000 {
compatible = "samsung,s3c2440-uart";
reg = <0x50000000 0x4000>;
interrupts = <1 28 0 4>, <1 28 1 4>;
status = "okay";
clock-names = "uart";
clocks = <&clock PCLK_UART0>;
pinctrl-names = "default";
pinctrl-0 = <0x3>;
};

i2c:i2c@54000000 {
compatible = "samsung,s3c2410-i2c";
reg = <0x54000000 0x100>;
interrupts = <0 0 27 3>;
#address-cells = <1>;
#size-cells = <0>;
};
  • 以uart为例
interrupts =  <1  28  0  4>,  <1  28  1  4>;`
  • <1 28 0 4>

  • 1:是子控制器

  • 28:主中断是28

  • 0:子中断的硬件中断号为0

  • 4:为高电平触发

  • 定位到INT_RXD0中断源

  • <1 28 1 4>

  • 1:是子控制器

  • 28:主中断是28

  • 1:子中断的硬件中断号为1

  • 4:为高电平触发

  • 定位到INT_TXD0中断源

i2c:i2c@54000000 {
compatible = "samsung,s3c2410-i2c";
reg = <0x54000000 0x100>;
//查数据手册P358的SRCPND寄存器信息,27刚好是i2c的中断号
//INT_IIC [27] 0 = Not requested, 1 = Requested 0
interrupts = <0 0 27 3>;
#address-cells = <1>;
#size-cells = <0>;
};

在这里插入图片描述
在这里插入图片描述

内核启动的过程调用设备树

一、内核部分

  1. dts ===> .dtb ============> device_node ========>platform_device
  2. 得到platform_device(获得资源),匹配platform_drv
  • 上述处理过程如何触发?
a. 内核启动时初始化中断的入口:
start_kernel // init/main.c
    init_IRQ();    //如果内核使用设备树则调用init_IRQ
        of_irq_init(__irqchip_of_table);    //扫描和初始化匹配设备树中的中断控制器
        
        if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
            irqchip_init();   // 一般使用它
        else
            machine_desc->init_irq();

二、调用对应驱动程序

b. 设备树中的中断控制器的处理入口:
irqchip_init // drivers/irqchip/irqchip.c
    of_irq_init(__irqchip_of_table);  // 对设备树文件中每一个中断控制器节点, 调用对应的处理函数
        为每一个符合的"interrupt-controller"节点,
        
        并调用处理函数
        
        (先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数...)
of_irq_init
//建立个中断控制器desc数据结构体,调用中断控制器注册的初始化程序,
//来建立父子中断控制器之间的联系
of_irq_init    
{
    //遍历符合compatible的设备节点
    //例子:遍历所有符合compatible = "samsung,s3c2416-irq"节点
    for循环(匹配设备树的输入节点)
    {
        1.符合compatible的节点中,找"interrupt-controller"属性的节点
        找中断控制器:匹配设备树输入节点的"interrupt-controller"属性
        
        //match =__of_table_s3c2410_irq, 即match->data 调用__of_table_s3c2410_irq
        2.分配一个of_intc_desc结构体, desc->irq_init_cb = match->data; // 驱动中IRQCHIP_DECLARE宏:match->data=init*irq函数
        
        3.创建of_intc_desc的中断控制器desc结构体,并持续添加进循环双链表
    }
    
    //遍历刚刚的循环双链表
    //1.先找出主中断控制器,调用驱动init*irq
    //2.再找出子中断控制器,调用驱动init*irq,建立和中断控制器的联系
    while (!list_empty(&intc_desc_list))//
}

总结:(先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数…)

其中:

#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type)           \
    static const struct of_device_id __of_table_##name      \
        __used __section(__##table##_of_table)          \
         = { .compatible = compat,              \
             .data = (fn == (fn_type)NULL) ? fn : fn  }

展开为:
    static const struct of_device_id __of_table_s3c2410_irq     \
        __used __section("__irqchip_of_table")          \
         = { .compatible = "samsung,s3c2410-irq",               \
             .data = s3c2410_init_intc_of  }

建立irq_domain

  • 每一个中断控制器都有一个irq_domain
  • 通过查询irq_domain,可以获取硬件中断号和软件中断号之前的联系
irq_domain是核心:
a. 每一个中断控制器都有一个irq_domain
b. 对设备中断信息的解析, 
b.1 需要调用 irq_domain->ops->xlate (即从设备树中获得hwirq, type)
b.2 获取未使用的virq, 保存: irq_domain->linear_revmap[hwirq] = virq;
b.3 在hwirq和virq之间建立联系:
   要调用 irq_domain->ops->map, 比如根据hwirq的属性设置virq的中断处理函数(是一个分发函数还是可以直接处理中断)
        irq_desc[virq].handle_irq = 常规函数;
   如果这个hwirq有上一级中断, 假设它的中断号为virq', 还要设置: 
        irq_desc[virq'].handle_irq = 中断分发函数;

一个引用的是主中断,另一个引用的是子中断

4. 代码如何解析设备树文件

4.1 注册中断控制器驱动

  • 1-10行:将中断控制器写入s3c2410_ctrl
  • 12-17行:中断初始化函数
  • 注册中断初始化函数
    1. IRQCHIP_DECLARE宏将"samsung,s3c2410-irq"和s3c2410_init_intc_of注册
    2. 当内核启动匹配设备树时,成功匹配会调用s3c2410_init_intc_of初始化函数
    3. s3c2410_init_intc_of实际调用->s3c_init_intc_of
static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
    {
        .name = "intc",
        .offset = 0,
    }, {
        .name = "subintc",
        .offset = 0x18,
        .parent = &s3c_intc[0],
    }
};

int __init s3c2410_init_intc_of(struct device_node *np,
            struct device_node *interrupt_parent)
{
    return s3c_init_intc_of(np, interrupt_parent,
                s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
}

//当匹配到"samsung,s3c2410-irq"设备树时,自动调用s3c2410_init_intc_of初始化函数
//s3c2410_irq这个名字不重要,只是创建一个名字相关的of_device_id结构体,
//__of_table_##name
/*
    __of_table_##name
    ={
        compatible = compat,
        .data = fn }
    }
*/
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);

4.2 s3c_init_intc_of初始化函数解析

  • 在内核启动时被调用,以24xx为例
  • 因为.compatible = "samsung,s3c2410-irq"为,且interrupt-controller,只有主中断控制器这一个设备节点,所以
  • 在设备树文件,中断部分只传递了主中断控制器的基地址
  • 所以在irq-s3c24xx.c 文件中在s3c2416_ctrl[]写死了主、子控制器的偏移位置
  • 所以s3c2416_init_intc_of仅被调用一次,即s3c_init_intc_of仅被调用一次
  1. 映射出中断控制器的基地址
  2. 这里选择父和子控制器共享domain方式,支持2 * 32 =64个中断源
  3. 清中断控制器
  4. 放控制器进全局数组s3c_intc[i] = intc;
  5. 设置中断汇编entry-armv.S跳转到s3c24xx_handle_irq
static int __init s3c_init_intc_of(struct device_node *np,
            struct device_node *interrupt_parent,
            struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
{
    struct s3c_irq_intc *intc;
    struct s3c24xx_irq_of_ctrl *ctrl;
    struct irq_domain *domain;
    void __iomem *reg_base;
    int i;

    //根据中断控制器设备节点,映射出寄存器的虚拟地址
    reg_base = of_iomap(np, 0);
    
    if (!reg_base) {
        pr_err("irq-s3c24xx: could not map irq registers\n");
        return -EINVAL;
    }

    //s3c24xx 只有主和子共2个控制器,这里采用共用domain,所以是2*32
    //每个中断控制器都分配和注册一个反向映射domain域
    //通过domains找3c24xx_irq_ops_of.map函数,映射硬件和虚拟中断号之间的联系
    //通过domain通过s3c24xx_irq_ops_of.xlate调用分发函数demux,设置中断服务函数
    domain = irq_domain_add_linear(np, num_ctrl * 32,
                             &s3c24xx_irq_ops_of, NULL);
    if (!domain) {
        pr_err("irq: could not create irq-domain\n");
        return -EINVAL;
    }

    //num_ctrl = 2
    for (i = 0; i < num_ctrl; i++) {
        ctrl = &s3c_ctrl[i];

        pr_debug("irq: found controller %s\n", ctrl->name);

        intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
        if (!intc)
            return -ENOMEM;

        //主和子控制器共享一个domain
        intc->domain = domain;
        intc->irqs = kcalloc(32, sizeof(struct s3c_irq_data),
                     GFP_KERNEL);
        if (!intc->irqs) {
            kfree(intc);
            return -ENOMEM;
        }

        if (ctrl->parent) {        //如果是子控制器
            intc->reg_pending = reg_base + ctrl->offset;    //SUBSRCPND寄存器
            intc->reg_mask = reg_base + ctrl->offset + 0x4;    //INTSUBMSK(偏移一个字)

            if (*(ctrl->parent)) {
                intc->parent = *(ctrl->parent);
            } else {
                pr_warn("irq: parent of %s missing\n",
                    ctrl->name);
                kfree(intc->irqs);
                kfree(intc);
                continue;
            }
        } else {        //主控制器
            intc->reg_pending = reg_base + ctrl->offset;        //参考:0x4A000000
            intc->reg_mask = reg_base + ctrl->offset + 0x08;    //参考:0x4A000008
            intc->reg_intpnd = reg_base + ctrl->offset + 0x10;    //参考:0x4A000010
        }

        s3c24xx_clear_intc(intc);    //清中断

        //i=0,主控制器
        //i=1,子控制器
        
        s3c_intc[i] = intc;        //解析完设备树后,将解析得到中断控制器intc写入s3c_intc
    }

    // 将handle_arch_irq设置为s3c24xx_handle_irq
    //ldr    r1, =handle_arch_irq
    //这样在发生中断后,会从汇编entry-armv.S中跳转到s3c24xx_handle_irq
    set_handle_irq(s3c24xx_handle_irq);

    return 0;
}

4.3 s3c24xx_irq_ops_of的注册?

在s3c_init_intc_of中的第23行:

domain = irq_domain_add_linear(np, num_ctrl * 32,
                             &s3c24xx_irq_ops_of, NULL);
s3c24xx_irq_ops_of的成员
static const struct irq_domain_ops s3c24xx_irq_ops_of = {
    .map = s3c24xx_irq_map_of,//建立虚拟中断号和硬件中断号之间的映射
    .xlate = s3c24xx_irq_xlate_of,//获取中断触发方式,调用中断分发函数s3c_irq_demux
};
s3c24xx_irq_map_of
static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
                            irq_hw_number_t hw)
{
    unsigned int ctrl_num = hw / 32;    //从硬件中断号判断是主还是子中断控制器
    unsigned int intc_hw = hw % 32;        //寄存器上的硬件中断号
    struct s3c_irq_intc *intc = s3c_intc[ctrl_num];        //获取中断控制器的结构体
    struct s3c_irq_intc *parent_intc = intc->parent;    //获取该父中断控制器
    struct s3c_irq_data *irq_data = &intc->irqs[intc_hw];    //每个硬件中断号都有一个s3c_irq_data结构体

    /* attach controller pointer to irq_data */
    irq_data->intc = intc;        //指向本中断控制寄存器
    irq_data->offset = intc_hw;    //中断控制器上的中断号

    if (!parent_intc)    //如果是主irq控制器
        irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
    else
        irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
                     handle_edge_irq);

    irq_set_chip_data(virq, irq_data);

    return 0;
}
s3c24xx_irq_map_of调用s3c_irq_demux
  • 中断分发函数
  • 在含有子中断的主中断被触发后,会执行主中断对应的virq的irq_desc的handle_irq,也就是这里的s3c_irq_demux
static void s3c_irq_demux(struct irq_desc *desc)
{
    struct irq_chip *chip = irq_desc_get_chip(desc);    //获得主中断irq_chip
    struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
    struct s3c_irq_intc *intc = irq_data->intc;            //获取主中断控制器
    struct s3c_irq_intc *sub_intc = irq_data->sub_intc;    //获取子中断控制器
    unsigned int n, offset, irq;
    unsigned long src, msk;

    /* we're using individual domains for the non-dt case
     * and one big domain for the dt case where the subintc
     * starts at hwirq number 32.
     */
     //这里使用设备树,offset=32,子中断启使号从32号开始
    offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;

    // 屏蔽该主中断,并清中断
    chained_irq_enter(chip, desc);

    src = readl_relaxed(sub_intc->reg_pending);
    msk = readl_relaxed(sub_intc->reg_mask);

    src &= ~msk;    //去掉被屏蔽的子中断
    src &= irq_data->sub_bits;    //去掉没有被初始化的?????

    while (src) {
        n = __ffs(src);    //返回寄存器从0~31中,第一个置位的位置
        src &= ~(1 << n);
        irq = irq_find_mapping(sub_intc->domain, offset + n);    //根据hwirq找到virq
        generic_handle_irq(irq);        //调用该子中断向量的处理函数
    }

    chained_irq_exit(chip, desc);    //打开主中断
}
s3c24xx_irq_xlate_of
/* Translate our of irq notation
 * format: <ctrl_num ctrl_irq parent_irq type>
 */
static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
            const u32 *intspec, unsigned int intsize,
            irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
    struct s3c_irq_intc *intc;
    struct s3c_irq_intc *parent_intc;
    struct s3c_irq_data *irq_data;
    struct s3c_irq_data *parent_irq_data;
    int irqno;

    //参数个数不能小于4
    if (WARN_ON(intsize < 4))
        return -EINVAL;

    //第一个参数不能大于2,中断控制器(结构体)不能为空
    /*
     * Array holding pointers to the global controller structs
     * [0] ... main_intc
     * [1] ... sub_intc
     * [2] ... main_intc2 on s3c2416
     */
    if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
        pr_err("controller number %d invalid\n", intspec[0]);
        return -EINVAL;
    }

    //取出中断控制器
    intc = s3c_intc[intspec[0]];

    //解析(decode)出硬件中断号
    //可知主中断号:0~31  子中断号:32~
    *out_hwirq = intspec[0] * 32 + intspec[2];

    //获取中断触发方式
    *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;

    //尝试获取父中断控制器
    parent_intc = intc->parent;
    //如果父中断控制器存在
    if (parent_intc) {
        irq_data = &intc->irqs[intspec[2]];
        //记录父intc
        irq_data->parent_irq = intspec[1];
        parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
        parent_irq_data->sub_intc = intc;
        parent_irq_data->sub_bits |= (1UL << intspec[2]);

        /* parent_intc is always s3c_intc[0], so no offset */
        irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
        if (irqno < 0) {
            pr_err("irq: could not map parent interrupt\n");
            return irqno;
        }

        // 从函数名称中就可以看出,这个函数会再次进行检测该主中断的哪个子中断被请求
        //这里是主中断才调用s3c_irq_demux
        irq_set_chained_handler(irqno, s3c_irq_demux);
    }

    return 0;
}

5. 中断设备树的调用过程

这里通过由上到下的描述方式:

  • 声明设备树
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
  • 当设备树匹配时,自动调用s3c2410_init_intc_of函数进行初始化

  • s3c2410_init_intc_of

  1. 解析设备树文件传进来的参数
  2. 注册s3c24xx_irq_ops_of的.map .xlate函数,获取domain
  3. set_handle_irq(s3c24xx_handle_irq)设置汇编中断向量地址跳转到s3c24xx_handle_irq
  4. s3c24xx_handle_irq中断控制器处理函数,查询
s3c24xx_handle_irq
    (s3c24xx_handle_intc(s3c_intc[0], regs, 0))
        //根据1.中断控制器的域;2.硬件中断号;3.寄存器,调用中断处理函数
        handle_domain_irq(intc->domain, intc_offset + offset, regs);
            __handle_domain_irq
__handle_domain_irq最终的中断处理函数
/**
 * __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
 * @domain:    The domain where to perform the lookup
 * @hwirq:    The HW irq number to convert to a logical one
 * @lookup:    Whether to perform the domain lookup or not
 * @regs:    Register file coming from the low-level handling code
 *
 * Returns:    0 on success, or -EINVAL if conversion has failed
 */
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
            bool lookup, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    unsigned int irq = hwirq;    //保留硬件中断号作中断
    int ret = 0;

    irq_enter();    //进入中断,可能做关总中断、抢占和互斥等

#ifdef CONFIG_IRQ_DOMAIN
    if (lookup)
        irq = irq_find_mapping(domain, hwirq);
#endif

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(!irq || irq >= nr_irqs)) {
        ack_bad_irq(irq);
        ret = -EINVAL;
    } else {
        generic_handle_irq(irq);        //调用用户注册的中断处理函数
    }

    irq_exit();        //完成中断,解除中断保护
    set_irq_regs(old_regs);
    return ret;
}
generic_handle_irq
  • generic_handle_irq最终会执行desc->handle_irq(desc)
  • desc->handle_irq(desc)是用户注册的最终的中断处理程序
generic_handle_irq
    generic_handle_irq_desc
        desc->handle_irq(desc);//最终的中断处理程序

三、中断的申请调用

6. 设备驱动申请中断

1、串口驱动

drivers/tty/serial/samsung.c

函数调用:

s3c24xx_serial_probe

    ---> s3c24xx_serial_init_port

在函数s3c24xx_serial_init_port会申请中断

ret = platform_get_irq(platdev, 0); // UART0 receive interrupt

ret = platform_get_irq(platdev, 1); // UART0 transmit interrupt

2、I2C控制器

drivers/i2c/busses/i2c-s3c2410.c

在函数s3c24xx_i2c_probe中会申请中断:

i2c->irq = ret = platform_get_irq(pdev, 0)

7. 查看系统信息

开机后,可以从/proc/interrupts中看到当前的中断资源申请信息:

[root@tq2440 ]# cat /proc/interrupts 
           CPU0       
  7:        926  s3c-eint   7 Edge      eth0
  8:          0       s3c   8 Edge      s3c2410-rtc tick
 13:     567308       s3c  13 Edge      samsung_time_irq
 26:          0       s3c  26 Edge      ohci_hcd:usb1
 27:          4       s3c  27 Edge      54000000.i2c       //I2C
 30:          0       s3c  30 Edge      s3c2410-rtc alarm
 32:         53  s3c-level  32 Level     50000000.serial   //UART0 RXD
 33:        230  s3c-level  33 Level     50000000.serial   //UART0 TXD
 59:          0  s3c-level  59 Edge      53000000.watchdog

下面解释一下上面这些参数的含义:
在这里插入图片描述

下面我们在中断处理程序中加入log,看一下中断处理程序的调用栈:
1、 以I2C代表的主中断

[    1.851407] [<c035004c>] (s3c24xx_i2c_irq) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    1.851457] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    1.851490] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    1.851526] [<c0044960>] (handle_irq_event) from [<c00476c0>] (handle_edge_irq+0xbc/0x190)
[    1.851558] [<c00476c0>] (handle_edge_irq) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    1.851589] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    1.851622] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)

2、 以串口代表的子中断
TX:

[    2.393176] [<c0293410>] (s3c24xx_serial_tx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    2.393218] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    2.393249] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    2.393283] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)
[    2.393312] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)
[    2.393379] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)
[    2.393424] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    2.393460] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    2.393492] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)

RX:

[    9.223769] [<c0294570>] (s3c24xx_serial_rx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    9.223817] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    9.223851] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    9.223883] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)
[    9.223915] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)
[    9.223978] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)
[    9.224017] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    9.224050] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    9.224081] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)

8. 总结

  1. 设备树的使用使得配置中断控制寄存器比较灵活,不需要在代码写死
  2. linux下使用中断,用户不需要关心寄存器清中断的问题

9. 附录

9.1 s3c2416-smdk2416.dts

/dts-v1/;

/ {
    compatible = "samsung,s3c2416";
    interrupt-parent = <0x1>;
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    model = "SMDK2416";

    aliases {
        pinctrl0 = "/pinctrl@56000000";
        serial0 = "/serial@50000000";
        serial1 = "/serial@50004000";
        serial2 = "/serial@50008000";
        serial3 = "/serial@5000c000";
    };

    interrupt-controller@4a000000 {
        compatible = "samsung,s3c2416-irq";
        reg = <0x4a000000 0x100>;
        interrupt-controller;
        #interrupt-cells = <0x4>;
        phandle = <0x1>;
    };

    pinctrl@56000000 {
        reg = <0x56000000 0x1000>;
        compatible = "samsung,s3c2416-pinctrl";

        wakeup-interrupt-controller {
            compatible = "samsung,s3c2410-wakeup-eint";
            interrupts = <0x0 0x0 0x0 0x3 0x0 0x0 0x1 0x3 0x0 0x0 0x2 0x3 0x0 0x0 0x3 0x3 0x0 0x0 0x4 0x4 0x0 0x0 0x5 0x4>;
        };

        gpa {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpb {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpc {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpd {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpe {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpf {
            gpio-controller;
            #gpio-cells = <0x2>;
            interrupt-controller;
            #interrupt-cells = <0x2>;
            phandle = <0xd>;
        };

        gpg {
            gpio-controller;
            #gpio-cells = <0x2>;
            interrupt-controller;
            #interrupt-cells = <0x2>;
        };

        gph {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpj {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpk {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpl {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        gpm {
            gpio-controller;
            #gpio-cells = <0x2>;
        };

        uart0-data {
            samsung,pins = "gph-0", "gph-1";
            samsung,pin-function = <0x2>;
            phandle = <0x3>;
        };

        uart0-fctl {
            samsung,pins = "gph-8", "gph-9";
            samsung,pin-function = <0x2>;
            phandle = <0x4>;
        };

        uart1-data {
            samsung,pins = "gph-2", "gph-3";
            samsung,pin-function = <0x2>;
            phandle = <0x5>;
        };

        uart1-fctl {
            samsung,pins = "gph-10", "gph-11";
            samsung,pin-function = <0x2>;
            phandle = <0x6>;
        };

        uart2-data {
            samsung,pins = "gph-4", "gph-5";
            samsung,pin-function = <0x2>;
            phandle = <0x7>;
        };

        uart2-fctl {
            samsung,pins = "gph-6", "gph-7";
            samsung,pin-function = <0x2>;
        };

        uart3-data {
            samsung,pins = "gph-6", "gph-7";
            samsung,pin-function = <0x2>;
            phandle = <0x8>;
        };

        extuart-clk {
            samsung,pins = "gph-12";
            samsung,pin-function = <0x2>;
        };

        i2c0-bus {
            samsung,pins = "gpe-14", "gpe-15";
            samsung,pin-function = <0x2>;
        };

        spi0-bus {
            samsung,pins = "gpe-11", "gpe-12", "gpe-13";
            samsung,pin-function = <0x2>;
        };

        sd0-clk {
            samsung,pins = "gpe-5";
            samsung,pin-function = <0x2>;
            phandle = <0x9>;
        };

        sd0-cmd {
            samsung,pins = "gpe-6";
            samsung,pin-function = <0x2>;
            phandle = <0xa>;
        };

        sd0-bus1 {
            samsung,pins = "gpe-7";
            samsung,pin-function = <0x2>;
            phandle = <0xb>;
        };

        sd0-bus4 {
            samsung,pins = "gpe-8", "gpe-9", "gpe-10";
            samsung,pin-function = <0x2>;
            phandle = <0xc>;
        };

        sd1-cmd {
            samsung,pins = "gpl-8";
            samsung,pin-function = <0x2>;
            phandle = <0xf>;
        };

        sd1-clk {
            samsung,pins = "gpl-9";
            samsung,pin-function = <0x2>;
            phandle = <0xe>;
        };

        sd1-bus1 {
            samsung,pins = "gpl-0";
            samsung,pin-function = <0x2>;
            phandle = <0x10>;
        };

        sd1-bus4 {
            samsung,pins = "gpl-1", "gpl-2", "gpl-3";
            samsung,pin-function = <0x2>;
            phandle = <0x11>;
        };
    };

    timer@51000000 {
        compatible = "samsung,s3c2410-pwm";
        reg = <0x51000000 0x1000>;
        interrupts = <0x0 0x0 0xa 0x3 0x0 0x0 0xb 0x3 0x0 0x0 0xc 0x3 0x0 0x0 0xd 0x3 0x0 0x0 0xe 0x3>;
        #pwm-cells = <0x4>;
        clocks = <0x2 0x52>;
        clock-names = "timers";
    };

    serial@50000000 {
        compatible = "samsung,s3c2440-uart";
        reg = <0x50000000 0x4000>;
        interrupts = <0x1 0x1c 0x0 0x4 0x1 0x1c 0x1 0x4>;
        status = "okay";
        clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
        clocks = <0x2 0x48 0x2 0x48 0x2 0x17>;
        pinctrl-names = "default";
        pinctrl-0 = <0x3 0x4>;
    };

    serial@50004000 {
        compatible = "samsung,s3c2440-uart";
        reg = <0x50004000 0x4000>;
        interrupts = <0x1 0x17 0x3 0x4 0x1 0x17 0x4 0x4>;
        status = "okay";
        clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
        clocks = <0x2 0x49 0x2 0x49 0x2 0x17>;
        pinctrl-names = "default";
        pinctrl-0 = <0x5 0x6>;
    };

    serial@50008000 {
        compatible = "samsung,s3c2440-uart";
        reg = <0x50008000 0x4000>;
        interrupts = <0x1 0xf 0x6 0x4 0x1 0xf 0x7 0x4>;
        status = "okay";
        clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
        clocks = <0x2 0x4a 0x2 0x4a 0x2 0x17>;
        pinctrl-names = "default";
        pinctrl-0 = <0x7>;
    };

    watchdog@53000000 {
        compatible = "samsung,s3c2410-wdt";
        reg = <0x53000000 0x100>;
        interrupts = <0x1 0x9 0x1b 0x3>;
        status = "okay";
        clocks = <0x2 0x53>;
        clock-names = "watchdog";
    };

    rtc@57000000 {
        compatible = "samsung,s3c2416-rtc";
        reg = <0x57000000 0x100>;
        interrupts = <0x0 0x0 0x1e 0x3 0x0 0x0 0x8 0x3>;
        status = "okay";
        clocks = <0x2 0x54>;
        clock-names = "rtc";
    };

    i2c@54000000 {
        compatible = "samsung,s3c2440-i2c";
        reg = <0x54000000 0x100>;
        interrupts = <0x0 0x0 0x1b 0x3>;
        #address-cells = <0x1>;
        #size-cells = <0x0>;
        status = "disabled";
        clocks = <0x2 0x4c>;
        clock-names = "i2c";
    };

    cpus {

        cpu {
            compatible = "arm,arm926ej-s";
        };
    };

    clock-controller@4c000000 {
        compatible = "samsung,s3c2416-clock";
        reg = <0x4c000000 0x40>;
        #clock-cells = <0x1>;
        phandle = <0x2>;
    };

    serial@5000c000 {
        compatible = "samsung,s3c2440-uart";
        reg = <0x5000c000 0x4000>;
        interrupts = <0x1 0x12 0x18 0x4 0x1 0x12 0x19 0x4>;
        clock-names = "uart", "clk_uart_baud2", "clk_uart_baud3";
        clocks = <0x2 0x4b 0x2 0x4b 0x2 0x17>;
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <0x8>;
    };

    sdhci@4ac00000 {
        compatible = "samsung,s3c6410-sdhci";
        reg = <0x4ac00000 0x100>;
        interrupts = <0x0 0x0 0x15 0x3>;
        clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";
        clocks = <0x2 0x3d 0x2 0x3d 0x2 0x22>;
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <0x9 0xa 0xb 0xc>;
        bus-width = <0x4>;
        cd-gpios = <0xd 0x1 0x0>;
        cd-inverted;
    };

    sdhci@4a800000 {
        compatible = "samsung,s3c6410-sdhci";
        reg = <0x4a800000 0x100>;
        interrupts = <0x0 0x0 0x14 0x3>;
        clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";
        clocks = <0x2 0x3e 0x2 0x3e 0x2 0x23>;
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <0xe 0xf 0x10 0x11>;
        bus-width = <0x4>;
        broken-cd;
    };

    memory@30000000 {
        device_type = "memory";
        reg = <0x30000000 0x4000000>;
    };

    clocks {
        compatible = "simple-bus";

        xti {
            compatible = "fixed-clock";
            clock-frequency = <0xb71b00>;
            clock-output-names = "xti";
            #clock-cells = <0x0>;
        };
    };
};

10. 参考资料

基于设备树的TQ2440的中断(1)作者:彭东林
https://www.cnblogs.com/pengdonglin137/p/6847685.html

11. 建议阅读

基于设备树的TQ2440的中断(2)作者:彭东林
https://www.cnblogs.com/pengdonglin137/p/6848851.html

基於tiny4412的Linux內核移植 — 实例学习中断背后的知识(1)
http://www.cnblogs.com/pengdonglin137/p/6349209.html

Linux kernel的中断子系统之(一):综述
Linux kernel的中断子系统之(二):IRQ Domain介绍
linux kernel的中断子系统之(三):IRQ number和中断描述符
linux kernel的中断子系统之(四):High level irq event handler
Linux kernel中断子系统之(五):驱动申请中断API
Linux kernel的中断子系统之(六):ARM中断处理过程
linux kernel的中断子系统之(七):GIC代码分析
http://www.wowotech.net/irq_subsystem/interrupt_subsystem_architecture.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值