INTx中断机制源码分析
创始人
2024-05-24 09:53:35
0

INTx中断机制源码分析


文章目录

  • INTx中断机制源码分析
  • 参考资料:
    • 一、 配置空间
    • 二、 扫描设备时分配中断号
    • 三、 使用INTx中断
    • 四、 PCIe中断树
    • 五、 PCIe INTx中断映射过程
      • 5.1 PCIe控制器支持的中断
      • 5.2 PCIe控制器注册中断
      • 5.3 PCIe设备中断号的分配
        • 5.3.1 IRQ domain
        • 5.3.2 得到PCIe设备的中断号
  • 致谢


参考资料:

  • 《PCI_SPEV_V3_0.pdf》6.8节
  • 《devicetree-specification-v0.2.pdf》

开发板资料:

  • 开发板Firefly-rk3399资料

本课程分析的文件:

  • linux-4.4_rk3399\drivers\pci\host\pcie-rockchip.c


一、 配置空间

无论是PCI设备还是PCIe设备,它们都可以在配置空间里声明:通过INTA#、INTB#、INTC#还是INTD#发出中断。

在这里插入图片描述
配置空间有2个寄存器:Interrupt Pin、Interrupt Line,作用如下:

  • Interrupt Pin:用来表示本设备通过哪条引脚发出中断信号,取值如下

    Interrupt Pin取值含义
    0不需要中断引脚
    1通过INTA#发出中断
    2通过INTB#发出中断
    3通过INTC#发出中断
    4通过INTD#发出中断
    5~0xff保留
  • Interrupt Line:给软件使用的,PCI设备本身不使用该寄存器。软件可以写入中断相关的信息,比如在Linux系统中,可以把分配的virq(虚拟中断号)写入此寄存器。软件完全可以自己记录中断信息,没必要依赖这个寄存器。

二、 扫描设备时分配中断号

PCIe设备在硬件信息里表明自己可以发出哪个中断,比如INTA、INTB、INTC或INTD,这个中断要转换为中断号,我们编写的软件才可以为它注册中断处理函数。

怎么得到中断号?下面只列出调用过程,后面再分析:

rockchip_pcie_probebus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);pci_scan_root_bus_msipci_scan_child_buspci_scan_slotdev = pci_scan_single_device(bus, devfn);dev = pci_scan_device(bus, devfn);struct pci_dev *dev;dev = pci_alloc_dev(bus);pci_setup_devicepci_read_bases(dev, 6, PCI_ROM_ADDRESS);	pci_device_add(dev, bus);pcibios_add_device(struct pci_dev *dev)dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);

解析出中断信息后,分配的中断号放在pci_dev的irq里面:

pci_scan_single_devicepci_device_addpcibios_add_device(struct pci_dev *dev)dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);

三、 使用INTx中断

每个PCIe设备,在Linux内核里都对应一个pci_dev结构体:
在这里插入图片描述可以为这个设备注册中断:

request_irq(pci_dev->irq, ....);

四、 PCIe中断树

要分析PCIe设备中断号的分配过程,需要从RK3399的芯片资料开始学习。

层级结构为:PCIe设备 => PCIe控制器 => GIC =>CPU
在这里插入图片描述在设备树中:

       gic: interrupt-controller@fee00000 {compatible = "arm,gic-v3";#interrupt-cells = <4>;#address-cells = <2>;#size-cells = <2>;ranges;interrupt-controller;/* 省略 */};pcie0: pcie@f8000000 {compatible = "rockchip,rk3399-pcie";#address-cells = <3>;#size-cells = <2>;aspm-no-l0s;clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,<&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;clock-names = "aclk", "aclk-perf","hclk", "pm";bus-range = <0x0 0x1f>;max-link-speed = <1>;linux,pci-domain = <0>;msi-map = <0x0 &its 0x0 0x1000>;interrupts = ,,;interrupt-names = "sys", "legacy", "client";#interrupt-cells = <1>;interrupt-map-mask = <0 0 0 7>;interrupt-map = <0 0 0 1 &pcie0_intc 0>,<0 0 0 2 &pcie0_intc 1>,<0 0 0 3 &pcie0_intc 2>,<0 0 0 4 &pcie0_intc 3>;};

发出中断的过程:

  • 任何一个PCIe设备向PCIe控制器发出"Assert INTx"(x=A/B/C/D)这类TLP包
  • PCIe控制器就会向GIC发出第50号SPI中断
  • GIC再给CPU发出中断。

中断的处理过程是反过来的:

  • CPU接收到中断,跳转到异常向量表处理代码,会调用GIC驱动
  • GIC:读取寄存器,得知发生的是SPI 50号中断,这个中断函数由PCIe控制器驱动提供
  • PCIe控制器:读取PCIe控制器的寄存器,分辨是INTA还是INTB、INTC、INTD,调用对应函数,这个函数由PCIe设备驱动程序提供
  • PCI设备:提供设备相关的驱动程序

五、 PCIe INTx中断映射过程

文件:drivers\pci\host\pcie-rockchip.c

5.1 PCIe控制器支持的中断

对于RK3399,PCIe控制器可以向GIC发出3个中断:sys、legacy、client:

  • sys:下图中Event ID为81,就是SPI 49号中断(81=32+49),用来处理一些系统性的中断,比如电源状态、热拔插
  • legacy:用来处理PCIe设备发来的INTA/INTB/INTC/INTD中断
  • client:跟外接的PCIe设备通信时,可能会发送传输错误,用这个中断来处理

在这里插入图片描述在设备树中,这3类中断如下定义:

       pcie0: pcie@f8000000 {/* 省略 */interrupts = ,,;interrupt-names = "sys", "legacy", "client";

5.2 PCIe控制器注册中断

在这里插入图片描述为什么legacy中断的函数,不是使用devm_request_irq而是使用irq_set_chained_handler_and_data?

因为发生legacy中断时,rockchip_pcie_legacy_int_handler函数要进一步分辨发生的是INTA还是INTB、INTC、INTD中断,然后处理。

5.3 PCIe设备中断号的分配

5.3.1 IRQ domain

在设备树里,PCIe控制器的节点里有一个更下一级的中断控制器,这是一个虚拟的中断控制器:

          pcie0: pcie@f8000000 {#address-cells = <3>;#interrupt-cells = <1>;interrupt-map-mask = <0 0 0 7>;interrupt-map = <0 0 0 1 &pcie0_intc 0>,<0 0 0 2 &pcie0_intc 1>,<0 0 0 3 &pcie0_intc 2>,<0 0 0 4 &pcie0_intc 3>;pcie0_intc: interrupt-controller {interrupt-controller;#address-cells = <0>;#interrupt-cells = <1>;};                    };

在代码里,对于pcie0_intc会创建出一个IRQ domain:
在这里插入图片描述在设备树的interrupt-map里面就用到了这个子节点,也就是用到了对应的IRQ domain:

               	interrupt-map-mask = <0 0 0 7>;interrupt-map = <0 0 0 1 &pcie0_intc 0>,<0 0 0 2 &pcie0_intc 1>,<0 0 0 3 &pcie0_intc 2>,<0 0 0 4 &pcie0_intc 3>;

5.3.2 得到PCIe设备的中断号

从PCIe设备得到的硬件中断信息,将会映射得到pcie0_intc,从它里面得到中断号。

这会涉及interrupt-map-maskinterrupt-map,比较复杂,在视频里讲解。

pci_scan_single_devicepci_device_addpcibios_add_device(struct pci_dev *dev)dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);of_irq_parse_and_map_pciret = of_irq_parse_pci(dev, &oirq);rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);out_irq->np = ppnode;out_irq->args_count = 1;out_irq->args[0] = pin;laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));laddr[1] = laddr[2] = cpu_to_be32(0);rc = of_irq_parse_raw(laddr, out_irq);				return irq_create_of_mapping(&oirq);


致谢

以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬



相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...