【Linux基础系列之】中断系统(1)-框架

  本文分为两篇,第一篇主要描述中断控制器和中断处理流程;第二篇主要讲述中断的下半部分处理机制包括softirq,tasklet,workqueue;

Linux中断系统(1)-中断框架
Linux中断系统(2)-中断下半部


(一)中断综述

  中断硬件系统主要有各个外设、中断控制器(Interrupt Controller)和CPU组成。各个外设提供irq request line连接到中断控制器,在发生中断事件的时候,通过irq request line上的电气信号向CPU系统请求处理,Interrupt Controller是连接外设中断系统和CPU系统的桥梁。CPU的主要功能是运算,而Interrupt Controller来负责处理来自irq request line的电气信号;

根据中断控制器处理的类型对中断分类:

  1. SGI(Software Generated Interrupt),Interrupt IDs 0-15。系统一般用其来实现 IPI 中断。
  2. PPI(Private Peripheral Interrupt),Interrupt IDs16-31。私有中断,这种中断对每个 CPU 都是独立一份的,比如 per-core timer 中断。
  3. SPI(Shared Peripheral Interrupt),Interrupt numbers 32-1020。最常用的外设中断,中断可以发给一个或者多个 CPU。
  4. LPI(Locality-specific Peripheral Interrupt)。基于 message 的中断,GICv2 和 GICv1 中不支持;

  linux中断框架:底层部分为跟硬件相关联的cpu部分和中断控制器部分,这两部分跟平台关联,cpu部分负责中断产生时的上下文切换,中断控制器部分完成中断控制器初始化,同时管理HW irq number,代码位于kernel-3.18\drivers\irqchip;

  通用处理模块负责对不用平台硬件抽象,提供IRQ 相关API给到驱动使用,代码位于kernel-3.18\kernel\irq:

  最上层为驱动层,负责不同的外设设备的中断申请和下半部份的处理;


(二)中断准备

(1) 中断控制器申明和设备匹配

  在kernel启动函数start_kernel(kernel-3.18/init/main.c)通过调用arch平台init_IRQ()来实现中断的初始化,以arm64为例:
  kernel-3.18/arch/arm64/kernel/irq.c:

 53 void __init init_IRQ(void)
 54 {
 55     irqchip_init();
 56     if (!handle_arch_irq)
 57         panic("No interrupt controller found.");
 58 }

  of_irq_init在所有的device node中寻找中断控制器节点,形成树状结构。之后,从root interrupt controller节点开始,对于每一个interrupt controller的device node,扫描irq chip table,进行匹配,一旦匹配到,就调用该interrupt controller的初始化函数,并把该中断控制器的device node以及parent中断控制器的device node作为参数传递给irq chip driver;

  kernel-3.18/drivers/irqchip/irqchip.c :

 24 extern struct of_device_id __irqchip_of_table[];
 25 
 26 void __init irqchip_init(void)
 27 {
 28     of_irq_init(__irqchip_of_table);
 29 }

  kernel-3.18/drivers/of/irq.c:

484 void __init of_irq_init(const struct of_device_id *matches)
485 {

540             pr_debug("of_irq_init: init %s @ %p, parent %p\n",
541                  match->compatible,
542                  desc->dev, desc->interrupt_parent);
543             irq_init_cb = (of_irq_init_cb_t)match->data;
544             ret = irq_init_cb(desc->dev, desc->interrupt_parent);//调用mt_gic_of_init;

  以通用的irq-gic.c为例:通过IRQCHIP_DECLARE定义若干个静态的struct of_device_id常量,编译系统会把所有的IRQCHIP_DECLARE宏定义的数据放入到一个特殊的section中(__irqchip_of_table),我们称这个特殊的section叫做irq chip table,这个table也就保存了kernel支持的所有的中断控制器的ID信息;
msm-3.18/drivers/irqchip/irq-gic.c:

1531 IRQCHIP_DECLARE(mt_gic, "mediatek,mt6735-gic", mt_gic_of_init);
//使用IRQCHIP_DECLARE来定义了若干个静态的struct of_device_id常量;
//gic_of_init : gic初始化函数指针;
//"mediatek,mt6735-gic" : 要匹配的device node的名字;
 865 #define _OF_DECLARE(table, name, compat, fn, fn_type)           \
 866     static const struct of_device_id __of_table_##name      \
 867         __used __section(__##table##_of_table)          \
 868          = { .compatible = compat,              \
 869              .data = (fn == (fn_type)NULL) ? fn : fn  }

  这个of_device_id主要被用来进行Device node和driver模块进行匹配用的,这里在drvier里面先声明,然后我们看下dts里面device node:

  kernel-3.18/arch/arm64/boot/dts/mt6755.dtsi:

 305     gic: interrupt-controller@10230000 {
 306         compatible = "mediatek,mt6735-gic";
         //该中断控制器用多少个cell(一个cell就是一个32-bit的单元)
         //描述一个外设的interrupt request line;
 308         #address-cells = <0>;
 309         interrupt-controller;//表明该device node就是一个中断控制器;
 310         reg = <0 0x10231000 0 0x1000>, 
 311               <0 0x10232000 0 0x1000>,
 312               <0 0x10200620 0 0x1000>;
 313     };

  以i2c0中断声明为例:

  kernel-3.18/arch/arm64/boot/dts/mt6755.dtsi

1124         i2c0: i2c@11007000 {
1125             compatible = "mediatek,mt6755-i2c", "mediatek,i2c0";
1126             cell-index = <0>;
1127             reg = <0x11007000 0x1000>,
1128                   <0x11000100 0x80>;
1129             interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_LOW>;
 //这个属性描述了具体该外设产生的interrupt的细节信息(也就是
 //interrupt specifier)。例如:HW interrupt ID(由该外设的
 //device node中的interrupt-parent指向的interrupt controller
 //解析)、interrupt触发类型等。
1130             clock-div = <10>;
1131             clocks = <&infrasys INFRA_I2C0>, <&infrasys INFRA_AP_DMA>;
1132             clock-names = "main", "dma";
1133             #address-cells = <1>;
1134             #size-cells = <0>;
1135         };
 //三个cell的情况:第一个值:表示中断类型(SPI,PPI,SPI);第二个是中断号;第三个是中断触发条件;

(2) 中断控制器初始化

  通过注册的mt_gic_of_init函数来完成控制器的初始化,kernel-3.18/drivers/irqchip/irq-mt-gic.c:

1474 int __init mt_gic_of_init(struct device_node *node, struct device_node *parent)
1475 {
1476     void __iomem *cpu_base;
1477     void __iomem *dist_base;
1478     void __iomem *pol_base;
1479     u32 percpu_offset;
1480     int irq;
1481     struct resource res;
1485 
1486     if (WARN_ON(!node))
1487         return -ENODEV;
1488 
1489     spin_lock_init(&irq_lock);
1490 
1491     dist_base = of_iomap(node, 0);
        ///*映射GIC Distributor的寄存器地址空间,Distributor负
        //责连接系统中所有的中断源,通过寄存器可以独立的配置每个中断的
        //属性:priority、state、security、outing  
        //information、enable status;定义哪些中断可以转发到 CPU core。*/
1492     WARN(!dist_base, "unable to map gic dist registers\n");
1493     GIC_DIST_BASE = dist_base;
1494 
1495     cpu_base = of_iomap(node, 1);
//映射GIC CPU interface的寄存器地址空间;CPU core 用来接收中断,寄
//存器主要提供的功能:mask、 identify 、control states of //interrupts forwarded to that core。每个 CPU core 拥有自己的 
//CPU interface。
1496     WARN(!cpu_base, "unable to map gic cpu registers\n");
1497     GIC_CPU_BASE = cpu_base;
1498 
1499     pol_base = of_iomap(node, 2);
1500     WARN(!pol_base, "unable to map pol registers\n");
1501     INT_POL_CTL0 = pol_base;
1502     if (of_address_to_resource(node, 2, &res))
1503         WARN(!pol_base, "unable to map pol registers\n");
1504 
1505     INT_POL_CTL0_phys = res.start;
1506 
1507     if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
1508         percpu_offset = 0;
1509 
1510     mt_gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
1511 
1512     if (parent) {
  //处理interrupt级联;
1513         irq = irq_of_parse_and_map(node, 0);
1514         mt_gic_cascade_irq(gic_cnt, irq);
1515     }
1516     gic_cnt++;
1517 
1527 
1528     return 0;
1529 }

  函数mt_gic_init_bases():

855 void __init mt_gic_init_bases(unsigned int gic_nr, int irq_start,
 856           void __iomem *dist_base, void __iomem *cpu_base,
 857           u32 percpu_offset, struct device_node *node)
 858 {
 859     irq_hw_number_t hwirq_base;
 860     struct gic_chip_data *gic;
 861     int gic_irqs, irq_base, i;
 862 
 863     BUG_ON(gic_nr >= MAX_GIC_NR);
 864 
 865     gic = &gic_data[gic_nr];
 
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值