增加: 11_INTx_MSI_MSIX三种中断机制分析
@@ -48,7 +48,7 @@ PCI设备就像普通的设备一样,通过物理引脚发出中断信号:
|
||||
|
||||
| Interrupt Pin取值 | 含义 |
|
||||
| ----------------- | ----------------- |
|
||||
| 0 | 不属于中断 |
|
||||
| 0 | 不需要中断引脚 |
|
||||
| 1 | 通过INTA#发出中断 |
|
||||
| 2 | 通过INTB#发出中断 |
|
||||
| 3 | 通过INTC#发出中断 |
|
||||
@@ -124,7 +124,7 @@ PCIe控制器内部接收到INTx的TLP包后,就会向GIC发出中断,最终
|
||||
|
||||
在初始PCI设备时,可以告诉它一个地址(主控芯片的地址)、一个数据:
|
||||
|
||||
* PCI设备想发出中断时,把这个数据写入整个地址就可以触发中断
|
||||
* PCI设备想发出中断时,往这个地址写入这个数据就可以触发中断
|
||||
* 软件读到这个数据,就知道是哪个设备发出中断了
|
||||
|
||||
|
||||
@@ -140,274 +140,269 @@ PCIe控制器内部接收到INTx的TLP包后,就会向GIC发出中断,最终
|
||||
|
||||
上图中的"pci_addr/value"保存在哪里?保存在设备的配置空间的capability里。
|
||||
|
||||
|
||||
|
||||
#### 3.1 capability
|
||||
|
||||
capability的意思是"能力",PCI/PCIe设备可以提供各种能力,所以在配置空间里有寄存器来描述这些capability:
|
||||
|
||||
* 一个capability,配置空间里有对应的多个寄存器
|
||||
* 可以有多个capability
|
||||
* 配置空间里有第1个capability的位置:Capabilities Pointer
|
||||
* 它指向第1个capability的多个寄存器,这些寄存器也是在配置空间里
|
||||
* 第1个capability的寄存器里,也会指示第2个capability在哪里
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Capability示例图如下:
|
||||
|
||||
* 配置空间0x34位置,存放的是第1个capability的位置:假设是 A4H
|
||||
* 在配置空间0xA4位置,找到第1个capability,capability的寄存器有如下约定
|
||||
* 第1个字节表示ID,每类capability都有自己的ID
|
||||
* 第2个字节表示下一个capability的位置,如果等于0表示这是最后一个capability
|
||||
* 其他寄存器由capability决定,所占据的寄存器数量由capability决定
|
||||
* 第1个capability里面,它表示下一个capability在5CH
|
||||
* 在配置空间0x5C位置,找到第2个capability
|
||||
* 第1个字节表示ID,第2个字节表示下一个capability的位置(图里是E0H)
|
||||
* 其他字节由capability本身决定
|
||||
* 在配置空间0xE0位置,找到第3个capability
|
||||
* 第1个字节表示ID
|
||||
* 第2个字节表示下一个capability的位置,这里是00H表示没有更多的capability了
|
||||
* 其他字节由capability本身决定
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.2 MSI capability
|
||||
|
||||
### 5. 代码分析
|
||||
一个PCI设备是否支持MSI,需要读取配置空间的capability来判断: 有MSI capability的话就支持MSI机制。
|
||||
|
||||
#### 5.1 使用中断线
|
||||
在配置空间中,MSI capability用来保存:pci_addr、data。表示:PCI设备往这个pci_addr写入data,就可以触发中断。
|
||||
|
||||
设备树:
|
||||
有如下问题要确认:
|
||||
|
||||
```shell
|
||||
pcie0: pcie@f8000000 {
|
||||
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>;
|
||||
* pci_addr是32位、还是64位?
|
||||
* 能触发几个中断?通过地址来分辨,还是通过数据来分辨?
|
||||
* 这些中断能否屏蔽?
|
||||
* 能否读出中断状态?
|
||||
* 这个些问题,都由capability里面的"Message Control"决定。
|
||||
|
||||
pcie0_intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
};
|
||||
```
|
||||
MSI capability格式如下:
|
||||
|
||||

|
||||
|
||||
#### 3.3 格式解析
|
||||
|
||||
MSI Capability格式的含义如下:
|
||||
|
||||
* Capability ID:对于MSI capability,它的ID为05H
|
||||
|
||||
```c
|
||||
pci_scan_single_device
|
||||
pci_device_add
|
||||
pcibios_add_device
|
||||
dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
|
||||
ret = of_irq_parse_pci(dev, &oirq);
|
||||
return irq_create_of_mapping(&oirq);
|
||||
* Next Pointer:下一个capability的位置,00H表示这是最后一个capability
|
||||
|
||||
of_irq_parse_pci
|
||||
rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
|
||||
* Message Control
|
||||
|
||||
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);
|
||||
initial_match_array[0] = laddr[0] = b/d/f;
|
||||
initial_match_array[1] = laddr[1] = 0;
|
||||
initial_match_array[2] = laddr[2] = 0;
|
||||
initial_match_array[3] = pin;
|
||||
|
||||
// map后
|
||||
out_irq->args[0] = pin;
|
||||
out_irq->args_count = 1;
|
||||
out_irq->np = newpar; // 指向pcie0_intc
|
||||
-------------------
|
||||
irq = platform_get_irq_byname(pdev, "legacy");
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing legacy IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | -------------------------- | ------------------------------------------------------------ |
|
||||
| 8 | Per-vector masking capable | 是否支持屏蔽单个中断(vector):<br />1: 支持<br />0: 不支持<br />这是只读位。 |
|
||||
| 7 | 64 bit address capable | 是否支持64位地址:<br />1: 支持<br />0: 不支持<br />这是只读位。 |
|
||||
| 6:4 | Multiple Message Enable | 系统软件可以支持多少个MSI中断?<br />PCI设备可以表明自己想发出多少个中断,<br />但是到底能发出几个中断?<br />由系统软件决定,它会写这些位,表示能支持多少个中断:<br />000: 系统分配了1个中断<br />001: 系统分配了2个中断<br />010: 系统分配了4个中断<br />011: 系统分配了8个中断<br />100: 系统分配了16个中断<br />101: 系统分配了32个中断<br />110: 保留值<br />111: 保留值<br />这些位是可读可写的。 |
|
||||
| 3:1 | Multiple Message Capable | PCI设备可以表明自己想发出多少个中断:<br />000: 设备申请1个中断<br />001: 设备申请2个中断<br />010: 设备申请4个中断<br />011: 设备申请8个中断<br />100: 设备申请16个中断<br />101: 设备申请32个中断<br />110: 保留值<br />111: 保留值<br />这些位是只读的。 |
|
||||
| 0 | MSI Enable | 使能MSI:<br />1: 使能<br />0: 禁止 |
|
||||
|
||||
rockchip_pcie_legacy_int_handler
|
||||
reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS);
|
||||
reg = (reg & PCIE_CLIENT_INTR_MASK) >> PCIE_CLIENT_INTR_SHIFT;
|
||||
* Message Address/Message Uper Address:地址
|
||||
|
||||
hwirq = ffs(reg) - 1;
|
||||
|
||||
virq = irq_find_mapping(rockchip->irq_domain, hwirq);
|
||||
generic_handle_irq(virq);
|
||||
```
|
||||
* 32位地址保存在Message Address中
|
||||
* 64位地址:低32位地址保存在Message Address中,高32位地址保存在Message Uper Address中
|
||||
* 这些地址是系统软件初始化PCI设备时分配的,系统软件把分配的地址写入这些寄存器
|
||||
* 这些地址属于PCI地址空间
|
||||
|
||||
* Message Data:数据
|
||||
|
||||
* 这个寄存器只有15位,PCI设备发出中断时数据是32位的,其中高16位数据为0
|
||||
* 这个寄存器的数值是系统软件初始化设备时写入的
|
||||
* 当PCI设备想发出中断是会发起一次写传输:
|
||||
* 往Message Address寄存器表示的地址,写入Message Data寄存器的数据
|
||||
* 如果可以发出多个中断的话,发出的数据中低位可以改变
|
||||
* 比如"Multiple Message Enable"被设置为"010"表示可以发出4个中断
|
||||
* 那么PCI设备发出的数据中bit1,0可以修改
|
||||
|
||||
* Mask Bits/Pending Bits: 屏蔽位/挂起位,这是可选的功能,PCI设备不一定实现它
|
||||
* Mask Bits:每一位用来屏蔽一个中断,被系统软件设置为1表示禁止对应的中断
|
||||
* Pending Bits:每一位用来表示一个中断的状态,这是软件只读位,它的值为1表示对应中断发生了,待处理
|
||||
|
||||
|
||||
#### 5.2 MSI
|
||||
|
||||
在具体PCIe设备里设置中断,比如:
|
||||
### 4. MSI-X中断机制
|
||||
|
||||
`nvme_setup_io_queues in pci.c (drivers\nvme\host) : vecs = pci_enable_msi_range(pdev, 1, min(nr_io_queues, 32));`
|
||||
MSI机制有几个缺点:
|
||||
|
||||
```c
|
||||
pci_enable_msi_range
|
||||
msi_capability_init
|
||||
|
||||
```
|
||||
* 每个设备的中断数最大是32,太少了
|
||||
* 需要系统软件分配连续的中断号,很可能失败,也就是说设备想发出N个中断,但是系统软件分配给它的中断少于N个
|
||||
* 通过MSI发出中断时,地址是固定的
|
||||
|
||||
于是引入了MSI-X机制:Enhanced MSI interrupt support,它解决了MSI的缺点:
|
||||
|
||||
* 可以支持多大2048个中断
|
||||
* 系统软件可以单独设置每个中断,不需要分配连续的中断号
|
||||
* 每个中断可以单独设置:PCI设备使用的"地址/数据"可以单独设置
|
||||
|
||||
|
||||
|
||||
#### 5.3 MSI-X
|
||||
假设MSI-X可以支持很多中断,每个中断的"地址/数据"都不一样。提问:在哪里描述这些信息?
|
||||
|
||||
```c
|
||||
nvme_setup_io_queues in pci.c (drivers\nvme\host) : vecs = pci_enable_msix_range(pdev, dev->entry, 1, nr_io_queues);
|
||||
* "地址/数据":
|
||||
* 不放在配置空间,空间不够
|
||||
* 放在PCI设备的内存空间:哪个内存空间?哪个BAR?内存空间哪个位置(偏移地址)?
|
||||
* 系统软件可以读写这些内存空间
|
||||
* 中断的控制信息
|
||||
* 使能/禁止?
|
||||
* 地址是32位还是64位?
|
||||
* 这些控制信息也是保存在PCI设备的内存空间
|
||||
* 中断的状态信息(挂起?)
|
||||
* 这些信息也是保存在PCI设备的内存空间
|
||||
|
||||
nvme_setup_io_queues
|
||||
pci_enable_msix_range
|
||||
pci_enable_msix
|
||||
msix_capability_init
|
||||
msix_setup_entries
|
||||
base = msix_map_region(dev, msix_table_size(control));
|
||||
phys_addr = pci_resource_start(dev, bir) + table_offset;
|
||||
entry->mask_base = base;
|
||||
---------------------------
|
||||
pci_device_add
|
||||
pci_set_msi_domain
|
||||
pci_dev_msi_domain
|
||||
pci_msi_get_device_domain
|
||||
of_msi_map_get_device_domain
|
||||
__of_msi_map_rid
|
||||
```
|
||||
|
||||
|
||||
#### 4.1 MSI-X capability
|
||||
|
||||
一个PCI设备是否支持MSI-X,需要读取配置空间的capability来判断: 有MSI-X capability的话就支持MSI-X机制。
|
||||
|
||||
MSI-X capability格式如下:
|
||||
|
||||
#### 5.4 ITS的初始化
|
||||

|
||||
|
||||
文件:`drivers\irqchip\irq-gic-v3.c`
|
||||
|
||||
```c
|
||||
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
|
||||
gic_of_init
|
||||
gic_init_bases
|
||||
its_init(node, &gic_data.rdists, gic_data.domain);
|
||||
its_probe(np, parent_domain);
|
||||
err = of_address_to_resource(node, 0, &res);
|
||||
its_base = ioremap(res.start, resource_size(&res));
|
||||
|
||||
inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
|
||||
#### 4.2 MSI-X capability格式解析
|
||||
|
||||
```
|
||||
格式解析如下:
|
||||
|
||||
对于同一个设备树节点:
|
||||
* Capability ID:对于MSI-X capability,它的ID为11H
|
||||
|
||||
```shell
|
||||
its: interrupt-controller@fee20000 {
|
||||
compatible = "arm,gic-v3-its";
|
||||
msi-controller;
|
||||
reg = <0x0 0xfee20000 0x0 0x20000>;
|
||||
};
|
||||
```
|
||||
* Next Pointer:下一个capability的位置,00H表示这是最后一个capability
|
||||
|
||||
它有3个IRQ Domain:
|
||||
* Message Control
|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its.c
|
||||

|
||||
| 位 | 名 | 描述 |
|
||||
| ---- | ------------- | ------------------------------------------------------------ |
|
||||
| 15 | MSI-X Enable | 是否使能MSI-X:<br />1: 使能<br />0: 禁止<br />注意: MSI-X和MSI不能同时使能。 |
|
||||
| 14 | Function Mask | 相当于MSI-X中断总开关:<br />1: 所有中断禁止<br />0: 有各个中断的Mask位决定是否使能对应中断 |
|
||||
| 13 | 保留 | |
|
||||
| 10:0 | Table Size | 系统软件读取这些位,算出MSI-X Table的大小,也就是支持多少个中断<br />读出值为"N-1",表示支持N个中断 |
|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its-pci-msi.c
|
||||
* Table Offset/Table BIR :BIR意思为"BAR Indicator register",表示使用哪个BAR。
|
||||
|
||||
```c
|
||||
its_pci_msi_init
|
||||
pci_msi_create_irq_domain
|
||||
info->flags |= MSI_FLAG_ACTIVATE_EARLY;
|
||||
domain = msi_create_irq_domain(fwnode, info, parent);
|
||||
```
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | ------------ | ------------------------------------------------------------ |
|
||||
| 31:3 | Table Offset | MSI-X Table保存在PCI设备的内存空间里,<br />在哪个内存空间?有下面的"Table BIR"表示。<br />在这个内存空间的哪个位置?由当前这几位表示。 |
|
||||
| 2:0 | Table BIR | 使用哪个内存空间来保存MSI-X Table?<br />也就是系统软件使用哪个BAR来访问MSI-X Table?<br />取值为0~5,表示BAR0~BAR5 |
|
||||
|
||||
|
||||
* PBA Offset/PBA BIR:PBA意思为"Pending Bit Array",用来表示MSI-X中断的挂起状态。
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | ---------- | ------------------------------------------------------------ |
|
||||
| 31:3 | PBA Offset | PBA保存在PCI设备的内存空间里,<br />在哪个内存空间?有下面的"PBA BIR"表示。<br />在这个内存空间的哪个位置?由当前这几位表示。 |
|
||||
| 2:0 | PBA BIR | 使用哪个内存空间来保存PBA?<br />也就是系统软件使用哪个BAR来访问PBA?<br />取值为0~5,表示BAR0~BAR5 |
|
||||
|
||||

|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its-platform-msi.c
|
||||

|
||||
|
||||
#### 4.3 MSI-X Table
|
||||
|
||||
PCI设备可以往某个地址写入某个数据,从而触发MSI-X中断。
|
||||
|
||||
#### 5.5 PCIe设备发出中断时写哪个地址
|
||||
这些"地址/数据"信息,是由系统软件分配的,系统软件要把"地址/数据"发给PCI设备。
|
||||
|
||||

|
||||
PCI设备在哪里保存这些信息?
|
||||
|
||||
```c
|
||||
msi_domain_activate
|
||||
irq_chip_write_msi_msg
|
||||
its_irq_compose_msi_msg
|
||||
msg->address_lo = addr & ((1UL << 32) - 1);
|
||||
msg->address_hi = addr >> 32;
|
||||
msg->data = its_get_event_id(d);
|
||||
|
||||
```
|
||||
* 在PCI设备的内存空间
|
||||
* 在哪个内存空间?由MSI-X capability的"Table BIR"决定
|
||||
* 在这个内存空间的哪个位置?由MSI-X capability的"Table Offset"决定
|
||||
|
||||
MSI-X Table格式如何?如下图所示:
|
||||
|
||||

|
||||
|
||||
调用过程:`kernel/irq/msi.c`
|
||||
上图中,Msg Data、Msg Addr Msg Upper Addr含义与MSI机制相同:PCI设备要发出MSI-X中断时,往这个地址写入这个数据。如果使用32位地址的话,写哪个地址由Msg Addr寄存器决定;如果使用64位地址的话,写哪个地址由Msg Addr和Msg Upper Addr这两个寄存器决定。
|
||||
|
||||
```c
|
||||
msi_domain_activate
|
||||
irq_chip_write_msi_msg
|
||||
```
|
||||
Vector Control寄存器中,只有Bit0有作用,表示"Mask Bit"。系统软件写入1表示禁止对应中断,写入0表示使能对应中断。
|
||||
|
||||
|
||||
|
||||
##### 5.5.1 分配中断
|
||||
#### 4.4 PBA
|
||||
|
||||
```c
|
||||
msix_capability_init/msi_capability_init
|
||||
pci_msi_setup_msi_irqs
|
||||
pci_msi_domain_alloc_irqs
|
||||
msi_domain_alloc_irqs
|
||||
__irq_domain_alloc_irqs
|
||||
irq_domain_alloc_irqs_recursive
|
||||
ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
|
||||
its_irq_domain_alloc
|
||||
err = its_alloc_device_irq(its_dev, &hwirq);
|
||||
*hwirq = dev->event_map.lpi_base + idx;
|
||||
irq_domain_set_hwirq_and_chip
|
||||
irq_data->hwirq = hwirq;
|
||||
irq_data->chip = chip ? chip : &no_irq_chip;
|
||||
irq_domain_activate_irq(irq_data);
|
||||
domain->ops->activate(domain, irq_data);
|
||||
msi_domain_activate
|
||||
irq_chip_compose_msi_msg(irq_data, &msg)
|
||||
// 构造msg,里面含有MSI或msi-x的addr/val
|
||||
its_irq_compose_msi_msg
|
||||
msg->address_lo = addr & ((1UL << 32) - 1);
|
||||
msg->address_hi = addr >> 32;
|
||||
// its_get_event_id:
|
||||
// d->hwirq - its_dev->event_map.lpi_base;
|
||||
msg->data = its_get_event_id(d);
|
||||
// 设置msi-x的entry地址
|
||||
irq_chip_write_msi_msg(irq_data, &msg);
|
||||
data->chip->irq_write_msi_msg(data, msg);
|
||||
pci_msi_domain_write_msg
|
||||
__pci_write_msi_msg(desc, msg);
|
||||
PBA意思为"Pending Bit Array",用来表示MSI-X中断的挂起状态。它的格式如下:
|
||||
|
||||
__pci_write_msi_msg(desc, msg);
|
||||
// 对于MSI-X
|
||||
writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR);
|
||||
writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR);
|
||||
writel(msg->data, base + PCI_MSIX_ENTRY_DATA);
|
||||
这些"挂起"信息,是由PCI设备设置,系统软件只能读取这些信息。
|
||||
|
||||
// 对于MSI
|
||||
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
|
||||
pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
|
||||
msg->address_lo);
|
||||
PCI设备在哪里保存这些信息?
|
||||
|
||||
* 在PCI设备的内存空间
|
||||
* 在哪个内存空间?由MSI-X capability的"PBA BIR"决定
|
||||
* 在这个内存空间的哪个位置?由MSI-X capability的"PBA Offset"决定
|
||||
|
||||
// 为PCI设备确定hwirq
|
||||
PBA格式如下:每一位对应一个中断,值为1表示中断发生了、等待处理。
|
||||
|
||||
its_domain_ops.alloc
|
||||
its_irq_domain_alloc
|
||||
its_alloc_device_irq
|
||||
*hwirq = dev->event_map.lpi_base + idx;
|
||||

|
||||
|
||||
|
||||
```
|
||||
|
||||
### 5. MSI/MSI-X操作流程
|
||||
|
||||
#### 5.1 扫描设备
|
||||
|
||||
扫描设备,读取capability,确定是否含有MSI capability、是否含有MSI-X capability。
|
||||
|
||||
|
||||
|
||||
#### 5.2 配置设备
|
||||
|
||||
一个设备,可能都支持INTx、MSI、MSI-X,这3中方式只能选择一种。
|
||||
|
||||
##### 5.2.1 MSI配置
|
||||
|
||||
系统软件读取MSI capability,确定设备想申请多少个中断。
|
||||
|
||||
系统软件确定能分配多少个中断给这个设备,并把"地址/数据"写入MSI capability。
|
||||
|
||||
如果MSI capability支持中断使能的话,还需要系统软件设置MSI capability来使能中断。
|
||||
|
||||
注意:如果支持多个MSI中断,PCI设备发出中断时,写的是同一个地址,但是数据的低位可以发生变化。
|
||||
|
||||
比如支持4个MSI中断时,通过数据的bit1、bit0来表示是哪个中断。
|
||||
|
||||
|
||||
|
||||
##### 5.2.2 MSI-X配置
|
||||
|
||||
MSI-X机制中,中断相关的更多信息保存在设备的内存空间。所以要使用MSI-X中断,要先配置设备、分配内存空间。
|
||||
|
||||
然后,系统软件读取MSI-X capability,确定设备需要多少个中断。
|
||||
|
||||
系统软件确定能分配多少个中断给这个设备,并把多个"地址/数据"写入MSI-X Table。
|
||||
|
||||
注意:PCI设备要发出MSI-X中断时,它会往"地址"写入"数据",这些"地址/数据"一旦配置后是不会变化的。MSI机制中,数据可以变化,MSI-X机制中数据不可以变化。
|
||||
|
||||
使能中断:设置总开关、MSI-X Table中某个中断的开关。
|
||||
|
||||
|
||||
|
||||
注意:MSI-X Table中,每一项都可以保存一个"地址/数据",Table中"地址/数据"可以相同,也就是说:PCI设备发出的中断可以是同一个。
|
||||
|
||||
|
||||
|
||||
#### 5.3 设备发出中断
|
||||
|
||||
PCI设备发出MSI中断、MSI-X中断时,都是发起"数据写"传输,就是往指定地址写入指定数据。
|
||||
|
||||
PCI控制器接收到数据后,就会触发CPU中断。
|
||||
|
||||
|
||||
|
||||
#### 5.4 中断函数
|
||||
|
||||
系统软件执行中断处理函数。
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.6 IRQ Domain创建流程
|
||||
|
||||
* `drivers\irqchip\irq-gic-v3-its.c`
|
||||
*
|
||||
BIN
IMX6ULL/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/105_capabilities.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 26 KiB |
BIN
IMX6ULL/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/108_msi-x_table.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
IMX6ULL/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/109_pba.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
@@ -446,6 +446,11 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
|
||||
```shell
|
||||
10_RK3399_PCIe_Host驱动分析_设备枚举
|
||||
```
|
||||
* 2022.01.17 发布"PCI和PCIe子系统"
|
||||
|
||||
```shell
|
||||
11_INTx_MSI_MSIX三种中断机制分析
|
||||
```
|
||||
|
||||
## 6. 联系方式
|
||||
|
||||
|
||||
408
STM32MP157/doc_pic/10_PCI_PCIe/11_INTx_MSI_MSIX三种中断机制分析.md
Normal file
@@ -0,0 +1,408 @@
|
||||
## INTx_MSI_MSIX三种中断机制分析
|
||||
|
||||
参考资料:
|
||||
|
||||
* 《PCI_SPEV_V3_0.pdf》6.8节
|
||||
|
||||
* [PCIe中MSI和MSI-X中断机制](https://blog.csdn.net/pieces_thinking/article/details/119431791)
|
||||
|
||||
* [PCIe学习笔记之MSI/MSI-x中断及代码分析](https://blog.csdn.net/yhb1047818384/article/details/106676560/)
|
||||
|
||||
* [msix中断分析](https://blog.csdn.net/weijitao/article/details/46566789)
|
||||
|
||||
* [MSI中断与Linux实现](https://www.cnblogs.com/gunl/archive/2011/06/09/2076892.html)
|
||||
|
||||
* [ARM GICv3中断控制器](https://blog.csdn.net/yhb1047818384/article/details/86708769)
|
||||
|
||||
开发板资料:
|
||||
|
||||
* https://wiki.t-firefly.com/zh_CN/ROC-RK3399-PC-PLUS/
|
||||
|
||||
本课程分析的文件:
|
||||
|
||||
* `linux-4.4_rk3399\drivers\pci\host\pcie-rockchip.c`
|
||||
|
||||
|
||||
|
||||
### 1. PCI设备的INTx中断机制
|
||||
|
||||
传统PCI设备的引脚中有4条线:INTA#、INTB#、INTC#、INTD#,"#"表示低电平有效,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
PCI设备就像普通的设备一样,通过物理引脚发出中断信号:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
在PCI设备的配置空间,它声明:通过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(虚拟中断号)写入此寄存器。软件完全可以自己记录中断信息,没必要依赖这个寄存器。
|
||||
|
||||
|
||||
|
||||
INTx中断是电平触发,处理过程如下:
|
||||
|
||||
* PCI设备发出中断:让INTx引脚变低
|
||||
* 软件处理中断,清除中断:写PCI设备的某个寄存器,导致PCI设备取消中断
|
||||
* PCI设备取消中断:让INTx引脚变高
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. PCIe设备的INTx中断机制
|
||||
|
||||
PCIe设备的配置空间也同样有这2个寄存器:Interrupt Pin、Interrupt Line,它们的作用跟PCI设备完全一样。
|
||||
|
||||
PCI总线上有INTA#~INTD#这些真实存在的引脚,但是PCIE总线上并没有这些引脚,PCIe设备怎么兼容INTx中断机制?
|
||||
|
||||
PCIe设备通过"INTx模拟"(PCI Compatible INTx Emulation)来实现传统的INTx中断,当设备需要发出中断时,它会发出特殊的TLP包:
|
||||
|
||||

|
||||
|
||||
TLP头部中,Message Code被用来区分发出的是哪类TLP包,为例"INTx模拟",有两类TLP包:
|
||||
|
||||
* Assert_INTx
|
||||

|
||||
* Deassert_INTx
|
||||

|
||||
|
||||
|
||||
|
||||
跟传统PCI设备类似,这个"INTx模拟"的处理过程也是一样的:
|
||||
|
||||
* PCIe设备发出中断:发出Assert_INTx的TLP包
|
||||
* 软件处理中断,清除中断:写PCIe设备的某个寄存器,导致PCIe设备取消中断
|
||||
* PCIe设备取消中断:发出Deassert_INTx的TLP包
|
||||
|
||||
|
||||
|
||||
硬件框图如下:
|
||||
|
||||

|
||||
|
||||
对于软件开发人员来说,他感觉不到变化:
|
||||
|
||||
* PCI设备通过真实的引脚传输中断
|
||||
* PCIe设备通过TLP实现虚拟的引脚传输中断
|
||||
|
||||
|
||||
|
||||
PCIe控制器内部接收到INTx的TLP包后,就会向GIC发出中断,最终导致CPU接收到中断信号。
|
||||
|
||||
对应的中断程序执行时,会读取PCIe控制器的寄存器,分辨发生的是INTA#~INTD#这4个中断的哪一个。
|
||||
|
||||
|
||||
|
||||
### 3. MSI中断机制
|
||||
|
||||
在PCI系统中,使用真实的引脚发出中断已经带来了不方便:
|
||||
|
||||
* 电路板上需要布线
|
||||
* 只有4条引脚,多个PCI设备共享这些引脚,中断处理效率低。
|
||||
|
||||
在PCI系统中,就已经引入了新的中断机制:MSI,Message Signaled Interrupts。
|
||||
|
||||
在初始PCI设备时,可以告诉它一个地址(主控芯片的地址)、一个数据:
|
||||
|
||||
* PCI设备想发出中断时,往这个地址写入这个数据就可以触发中断
|
||||
* 软件读到这个数据,就知道是哪个设备发出中断了
|
||||
|
||||
|
||||
|
||||
流程及硬件框图如下:
|
||||
|
||||
* 写哪个地址可以触发中断?可能是PCI/PCIe控制器里面的某个地址,也可能是GIC的某个地址
|
||||
* 初始化PCI/PCIe设备时,把该地址(cpu_addr)转换为pci_addr,告知PCI/PCIe设备(写入它的配置空间)
|
||||
* PCI/PCIe设备要发出中断时,会发起一个"写内存传输":往pci_addr写入数据value
|
||||
* 这导致cpu_addr被写入数据value,触发中断
|
||||
|
||||

|
||||
|
||||
上图中的"pci_addr/value"保存在哪里?保存在设备的配置空间的capability里。
|
||||
|
||||
|
||||
|
||||
#### 3.1 capability
|
||||
|
||||
capability的意思是"能力",PCI/PCIe设备可以提供各种能力,所以在配置空间里有寄存器来描述这些capability:
|
||||
|
||||
* 配置空间里有第1个capability的位置:Capabilities Pointer
|
||||
* 它指向第1个capability的多个寄存器,这些寄存器也是在配置空间里
|
||||
* 第1个capability的寄存器里,也会指示第2个capability在哪里
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Capability示例图如下:
|
||||
|
||||
* 配置空间0x34位置,存放的是第1个capability的位置:假设是 A4H
|
||||
* 在配置空间0xA4位置,找到第1个capability,capability的寄存器有如下约定
|
||||
* 第1个字节表示ID,每类capability都有自己的ID
|
||||
* 第2个字节表示下一个capability的位置,如果等于0表示这是最后一个capability
|
||||
* 其他寄存器由capability决定,所占据的寄存器数量由capability决定
|
||||
* 第1个capability里面,它表示下一个capability在5CH
|
||||
* 在配置空间0x5C位置,找到第2个capability
|
||||
* 第1个字节表示ID,第2个字节表示下一个capability的位置(图里是E0H)
|
||||
* 其他字节由capability本身决定
|
||||
* 在配置空间0xE0位置,找到第3个capability
|
||||
* 第1个字节表示ID
|
||||
* 第2个字节表示下一个capability的位置,这里是00H表示没有更多的capability了
|
||||
* 其他字节由capability本身决定
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.2 MSI capability
|
||||
|
||||
一个PCI设备是否支持MSI,需要读取配置空间的capability来判断: 有MSI capability的话就支持MSI机制。
|
||||
|
||||
在配置空间中,MSI capability用来保存:pci_addr、data。表示:PCI设备往这个pci_addr写入data,就可以触发中断。
|
||||
|
||||
有如下问题要确认:
|
||||
|
||||
* pci_addr是32位、还是64位?
|
||||
* 能触发几个中断?通过地址来分辨,还是通过数据来分辨?
|
||||
* 这些中断能否屏蔽?
|
||||
* 能否读出中断状态?
|
||||
* 这个些问题,都由capability里面的"Message Control"决定。
|
||||
|
||||
MSI capability格式如下:
|
||||
|
||||

|
||||
|
||||
#### 3.3 格式解析
|
||||
|
||||
MSI Capability格式的含义如下:
|
||||
|
||||
* Capability ID:对于MSI capability,它的ID为05H
|
||||
|
||||
* Next Pointer:下一个capability的位置,00H表示这是最后一个capability
|
||||
|
||||
* Message Control
|
||||
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | -------------------------- | ------------------------------------------------------------ |
|
||||
| 8 | Per-vector masking capable | 是否支持屏蔽单个中断(vector):<br />1: 支持<br />0: 不支持<br />这是只读位。 |
|
||||
| 7 | 64 bit address capable | 是否支持64位地址:<br />1: 支持<br />0: 不支持<br />这是只读位。 |
|
||||
| 6:4 | Multiple Message Enable | 系统软件可以支持多少个MSI中断?<br />PCI设备可以表明自己想发出多少个中断,<br />但是到底能发出几个中断?<br />由系统软件决定,它会写这些位,表示能支持多少个中断:<br />000: 系统分配了1个中断<br />001: 系统分配了2个中断<br />010: 系统分配了4个中断<br />011: 系统分配了8个中断<br />100: 系统分配了16个中断<br />101: 系统分配了32个中断<br />110: 保留值<br />111: 保留值<br />这些位是可读可写的。 |
|
||||
| 3:1 | Multiple Message Capable | PCI设备可以表明自己想发出多少个中断:<br />000: 设备申请1个中断<br />001: 设备申请2个中断<br />010: 设备申请4个中断<br />011: 设备申请8个中断<br />100: 设备申请16个中断<br />101: 设备申请32个中断<br />110: 保留值<br />111: 保留值<br />这些位是只读的。 |
|
||||
| 0 | MSI Enable | 使能MSI:<br />1: 使能<br />0: 禁止 |
|
||||
|
||||
* Message Address/Message Uper Address:地址
|
||||
|
||||
* 32位地址保存在Message Address中
|
||||
* 64位地址:低32位地址保存在Message Address中,高32位地址保存在Message Uper Address中
|
||||
* 这些地址是系统软件初始化PCI设备时分配的,系统软件把分配的地址写入这些寄存器
|
||||
* 这些地址属于PCI地址空间
|
||||
|
||||
* Message Data:数据
|
||||
|
||||
* 这个寄存器只有15位,PCI设备发出中断时数据是32位的,其中高16位数据为0
|
||||
* 这个寄存器的数值是系统软件初始化设备时写入的
|
||||
* 当PCI设备想发出中断是会发起一次写传输:
|
||||
* 往Message Address寄存器表示的地址,写入Message Data寄存器的数据
|
||||
* 如果可以发出多个中断的话,发出的数据中低位可以改变
|
||||
* 比如"Multiple Message Enable"被设置为"010"表示可以发出4个中断
|
||||
* 那么PCI设备发出的数据中bit1,0可以修改
|
||||
|
||||
* Mask Bits/Pending Bits: 屏蔽位/挂起位,这是可选的功能,PCI设备不一定实现它
|
||||
* Mask Bits:每一位用来屏蔽一个中断,被系统软件设置为1表示禁止对应的中断
|
||||
* Pending Bits:每一位用来表示一个中断的状态,这是软件只读位,它的值为1表示对应中断发生了,待处理
|
||||
|
||||
|
||||
|
||||
### 4. MSI-X中断机制
|
||||
|
||||
MSI机制有几个缺点:
|
||||
|
||||
* 每个设备的中断数最大是32,太少了
|
||||
* 需要系统软件分配连续的中断号,很可能失败,也就是说设备想发出N个中断,但是系统软件分配给它的中断少于N个
|
||||
* 通过MSI发出中断时,地址是固定的
|
||||
|
||||
于是引入了MSI-X机制:Enhanced MSI interrupt support,它解决了MSI的缺点:
|
||||
|
||||
* 可以支持多大2048个中断
|
||||
* 系统软件可以单独设置每个中断,不需要分配连续的中断号
|
||||
* 每个中断可以单独设置:PCI设备使用的"地址/数据"可以单独设置
|
||||
|
||||
|
||||
|
||||
假设MSI-X可以支持很多中断,每个中断的"地址/数据"都不一样。提问:在哪里描述这些信息?
|
||||
|
||||
* "地址/数据":
|
||||
* 不放在配置空间,空间不够
|
||||
* 放在PCI设备的内存空间:哪个内存空间?哪个BAR?内存空间哪个位置(偏移地址)?
|
||||
* 系统软件可以读写这些内存空间
|
||||
* 中断的控制信息
|
||||
* 使能/禁止?
|
||||
* 地址是32位还是64位?
|
||||
* 这些控制信息也是保存在PCI设备的内存空间
|
||||
* 中断的状态信息(挂起?)
|
||||
* 这些信息也是保存在PCI设备的内存空间
|
||||
|
||||
|
||||
|
||||
#### 4.1 MSI-X capability
|
||||
|
||||
一个PCI设备是否支持MSI-X,需要读取配置空间的capability来判断: 有MSI-X capability的话就支持MSI-X机制。
|
||||
|
||||
MSI-X capability格式如下:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 4.2 MSI-X capability格式解析
|
||||
|
||||
格式解析如下:
|
||||
|
||||
* Capability ID:对于MSI-X capability,它的ID为11H
|
||||
|
||||
* Next Pointer:下一个capability的位置,00H表示这是最后一个capability
|
||||
|
||||
* Message Control
|
||||
|
||||
| 位 | 名 | 描述 |
|
||||
| ---- | ------------- | ------------------------------------------------------------ |
|
||||
| 15 | MSI-X Enable | 是否使能MSI-X:<br />1: 使能<br />0: 禁止<br />注意: MSI-X和MSI不能同时使能。 |
|
||||
| 14 | Function Mask | 相当于MSI-X中断总开关:<br />1: 所有中断禁止<br />0: 有各个中断的Mask位决定是否使能对应中断 |
|
||||
| 13 | 保留 | |
|
||||
| 10:0 | Table Size | 系统软件读取这些位,算出MSI-X Table的大小,也就是支持多少个中断<br />读出值为"N-1",表示支持N个中断 |
|
||||
|
||||
* Table Offset/Table BIR :BIR意思为"BAR Indicator register",表示使用哪个BAR。
|
||||
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | ------------ | ------------------------------------------------------------ |
|
||||
| 31:3 | Table Offset | MSI-X Table保存在PCI设备的内存空间里,<br />在哪个内存空间?有下面的"Table BIR"表示。<br />在这个内存空间的哪个位置?由当前这几位表示。 |
|
||||
| 2:0 | Table BIR | 使用哪个内存空间来保存MSI-X Table?<br />也就是系统软件使用哪个BAR来访问MSI-X Table?<br />取值为0~5,表示BAR0~BAR5 |
|
||||
|
||||
* PBA Offset/PBA BIR:PBA意思为"Pending Bit Array",用来表示MSI-X中断的挂起状态。
|
||||
| 位 | 域 | 描述 |
|
||||
| ---- | ---------- | ------------------------------------------------------------ |
|
||||
| 31:3 | PBA Offset | PBA保存在PCI设备的内存空间里,<br />在哪个内存空间?有下面的"PBA BIR"表示。<br />在这个内存空间的哪个位置?由当前这几位表示。 |
|
||||
| 2:0 | PBA BIR | 使用哪个内存空间来保存PBA?<br />也就是系统软件使用哪个BAR来访问PBA?<br />取值为0~5,表示BAR0~BAR5 |
|
||||
|
||||
|
||||
|
||||
#### 4.3 MSI-X Table
|
||||
|
||||
PCI设备可以往某个地址写入某个数据,从而触发MSI-X中断。
|
||||
|
||||
这些"地址/数据"信息,是由系统软件分配的,系统软件要把"地址/数据"发给PCI设备。
|
||||
|
||||
PCI设备在哪里保存这些信息?
|
||||
|
||||
* 在PCI设备的内存空间
|
||||
* 在哪个内存空间?由MSI-X capability的"Table BIR"决定
|
||||
* 在这个内存空间的哪个位置?由MSI-X capability的"Table Offset"决定
|
||||
|
||||
MSI-X Table格式如何?如下图所示:
|
||||
|
||||

|
||||
|
||||
上图中,Msg Data、Msg Addr Msg Upper Addr含义与MSI机制相同:PCI设备要发出MSI-X中断时,往这个地址写入这个数据。如果使用32位地址的话,写哪个地址由Msg Addr寄存器决定;如果使用64位地址的话,写哪个地址由Msg Addr和Msg Upper Addr这两个寄存器决定。
|
||||
|
||||
Vector Control寄存器中,只有Bit0有作用,表示"Mask Bit"。系统软件写入1表示禁止对应中断,写入0表示使能对应中断。
|
||||
|
||||
|
||||
|
||||
#### 4.4 PBA
|
||||
|
||||
PBA意思为"Pending Bit Array",用来表示MSI-X中断的挂起状态。它的格式如下:
|
||||
|
||||
这些"挂起"信息,是由PCI设备设置,系统软件只能读取这些信息。
|
||||
|
||||
PCI设备在哪里保存这些信息?
|
||||
|
||||
* 在PCI设备的内存空间
|
||||
* 在哪个内存空间?由MSI-X capability的"PBA BIR"决定
|
||||
* 在这个内存空间的哪个位置?由MSI-X capability的"PBA Offset"决定
|
||||
|
||||
PBA格式如下:每一位对应一个中断,值为1表示中断发生了、等待处理。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 5. MSI/MSI-X操作流程
|
||||
|
||||
#### 5.1 扫描设备
|
||||
|
||||
扫描设备,读取capability,确定是否含有MSI capability、是否含有MSI-X capability。
|
||||
|
||||
|
||||
|
||||
#### 5.2 配置设备
|
||||
|
||||
一个设备,可能都支持INTx、MSI、MSI-X,这3中方式只能选择一种。
|
||||
|
||||
##### 5.2.1 MSI配置
|
||||
|
||||
系统软件读取MSI capability,确定设备想申请多少个中断。
|
||||
|
||||
系统软件确定能分配多少个中断给这个设备,并把"地址/数据"写入MSI capability。
|
||||
|
||||
如果MSI capability支持中断使能的话,还需要系统软件设置MSI capability来使能中断。
|
||||
|
||||
注意:如果支持多个MSI中断,PCI设备发出中断时,写的是同一个地址,但是数据的低位可以发生变化。
|
||||
|
||||
比如支持4个MSI中断时,通过数据的bit1、bit0来表示是哪个中断。
|
||||
|
||||
|
||||
|
||||
##### 5.2.2 MSI-X配置
|
||||
|
||||
MSI-X机制中,中断相关的更多信息保存在设备的内存空间。所以要使用MSI-X中断,要先配置设备、分配内存空间。
|
||||
|
||||
然后,系统软件读取MSI-X capability,确定设备需要多少个中断。
|
||||
|
||||
系统软件确定能分配多少个中断给这个设备,并把多个"地址/数据"写入MSI-X Table。
|
||||
|
||||
注意:PCI设备要发出MSI-X中断时,它会往"地址"写入"数据",这些"地址/数据"一旦配置后是不会变化的。MSI机制中,数据可以变化,MSI-X机制中数据不可以变化。
|
||||
|
||||
使能中断:设置总开关、MSI-X Table中某个中断的开关。
|
||||
|
||||
|
||||
|
||||
注意:MSI-X Table中,每一项都可以保存一个"地址/数据",Table中"地址/数据"可以相同,也就是说:PCI设备发出的中断可以是同一个。
|
||||
|
||||
|
||||
|
||||
#### 5.3 设备发出中断
|
||||
|
||||
PCI设备发出MSI中断、MSI-X中断时,都是发起"数据写"传输,就是往指定地址写入指定数据。
|
||||
|
||||
PCI控制器接收到数据后,就会触发CPU中断。
|
||||
|
||||
|
||||
|
||||
#### 5.4 中断函数
|
||||
|
||||
系统软件执行中断处理函数。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
STM32MP157/doc_pic/10_PCI_PCIe/11_INTx_MSI_MSIX三种中断机制分析.tif
Normal file
251
STM32MP157/doc_pic/10_PCI_PCIe/12_INTx_MSI_MSIX中断源码分析.md
Normal file
@@ -0,0 +1,251 @@
|
||||
## MSI和MSIX原理分析
|
||||
|
||||
参考资料:
|
||||
|
||||
* 《PCI_SPEV_V3_0.pdf》6.8节
|
||||
* [PCIe中MSI和MSI-X中断机制](https://blog.csdn.net/pieces_thinking/article/details/119431791)
|
||||
* [msix中断分析](https://blog.csdn.net/weijitao/article/details/46566789)
|
||||
* [MSI中断与Linux实现](https://www.cnblogs.com/gunl/archive/2011/06/09/2076892.html)
|
||||
* [ARM GICv3中断控制器](https://blog.csdn.net/yhb1047818384/article/details/86708769)
|
||||
|
||||
开发板资料:
|
||||
|
||||
* https://wiki.t-firefly.com/zh_CN/ROC-RK3399-PC-PLUS/
|
||||
|
||||
本课程分析的文件:
|
||||
|
||||
* `linux-4.4_rk3399\drivers\pci\host\pcie-rockchip.c`
|
||||
|
||||
|
||||
|
||||
### 1. capability
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 5. 代码分析
|
||||
|
||||
#### 5.1 使用中断线
|
||||
|
||||
```c
|
||||
pci_scan_single_device
|
||||
pci_device_add
|
||||
pcibios_add_device
|
||||
dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
|
||||
|
||||
|
||||
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);
|
||||
initial_match_array[0] = laddr[0] = b/d/f;
|
||||
initial_match_array[1] = laddr[1] = 0;
|
||||
initial_match_array[2] = laddr[2] = 0;
|
||||
initial_match_array[3] = pin;
|
||||
|
||||
-------------------
|
||||
irq = platform_get_irq_byname(pdev, "legacy");
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing legacy IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rockchip_pcie_legacy_int_handler
|
||||
reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS);
|
||||
reg = (reg & PCIE_CLIENT_INTR_MASK) >> PCIE_CLIENT_INTR_SHIFT;
|
||||
|
||||
hwirq = ffs(reg) - 1;
|
||||
|
||||
virq = irq_find_mapping(rockchip->irq_domain, hwirq);
|
||||
generic_handle_irq(virq);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.2 MSI
|
||||
|
||||
在具体PCIe设备里设置中断,比如:
|
||||
|
||||
`nvme_setup_io_queues in pci.c (drivers\nvme\host) : vecs = pci_enable_msi_range(pdev, 1, min(nr_io_queues, 32));`
|
||||
|
||||
```c
|
||||
pci_enable_msi_range
|
||||
msi_capability_init
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.3 MSI-X
|
||||
|
||||
```c
|
||||
nvme_setup_io_queues in pci.c (drivers\nvme\host) : vecs = pci_enable_msix_range(pdev, dev->entry, 1, nr_io_queues);
|
||||
|
||||
nvme_setup_io_queues
|
||||
pci_enable_msix_range
|
||||
pci_enable_msix
|
||||
msix_capability_init
|
||||
msix_setup_entries
|
||||
base = msix_map_region(dev, msix_table_size(control));
|
||||
phys_addr = pci_resource_start(dev, bir) + table_offset;
|
||||
entry->mask_base = base;
|
||||
---------------------------
|
||||
pci_device_add
|
||||
pci_set_msi_domain
|
||||
pci_dev_msi_domain
|
||||
pci_msi_get_device_domain
|
||||
of_msi_map_get_device_domain
|
||||
__of_msi_map_rid
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.4 ITS的初始化
|
||||
|
||||
文件:`drivers\irqchip\irq-gic-v3.c`
|
||||
|
||||
```c
|
||||
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
|
||||
gic_of_init
|
||||
gic_init_bases
|
||||
its_init(node, &gic_data.rdists, gic_data.domain);
|
||||
its_probe(np, parent_domain);
|
||||
err = of_address_to_resource(node, 0, &res);
|
||||
its_base = ioremap(res.start, resource_size(&res));
|
||||
|
||||
inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
|
||||
|
||||
```
|
||||
|
||||
对于同一个设备树节点:
|
||||
|
||||
```shell
|
||||
its: interrupt-controller@fee20000 {
|
||||
compatible = "arm,gic-v3-its";
|
||||
msi-controller;
|
||||
reg = <0x0 0xfee20000 0x0 0x20000>;
|
||||
};
|
||||
```
|
||||
|
||||
它有3个IRQ Domain:
|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its.c
|
||||

|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its-pci-msi.c
|
||||
|
||||
```c
|
||||
its_pci_msi_init
|
||||
pci_msi_create_irq_domain
|
||||
info->flags |= MSI_FLAG_ACTIVATE_EARLY;
|
||||
domain = msi_create_irq_domain(fwnode, info, parent);
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
* drivers\irqchip\irq-gic-v3-its-platform-msi.c
|
||||

|
||||
|
||||
|
||||
|
||||
#### 5.5 PCIe设备发出中断时写哪个地址
|
||||
|
||||

|
||||
|
||||
```c
|
||||
msi_domain_activate
|
||||
irq_chip_write_msi_msg
|
||||
its_irq_compose_msi_msg
|
||||
msg->address_lo = addr & ((1UL << 32) - 1);
|
||||
msg->address_hi = addr >> 32;
|
||||
msg->data = its_get_event_id(d);
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
调用过程:`kernel/irq/msi.c`
|
||||
|
||||
```c
|
||||
msi_domain_activate
|
||||
irq_chip_write_msi_msg
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 5.5.1 分配中断
|
||||
|
||||
```c
|
||||
msix_capability_init/msi_capability_init
|
||||
pci_msi_setup_msi_irqs
|
||||
pci_msi_domain_alloc_irqs
|
||||
msi_domain_alloc_irqs
|
||||
__irq_domain_alloc_irqs
|
||||
irq_domain_alloc_irqs_recursive
|
||||
ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
|
||||
its_irq_domain_alloc
|
||||
err = its_alloc_device_irq(its_dev, &hwirq);
|
||||
*hwirq = dev->event_map.lpi_base + idx;
|
||||
irq_domain_set_hwirq_and_chip
|
||||
irq_data->hwirq = hwirq;
|
||||
irq_data->chip = chip ? chip : &no_irq_chip;
|
||||
irq_domain_activate_irq(irq_data);
|
||||
domain->ops->activate(domain, irq_data);
|
||||
msi_domain_activate
|
||||
irq_chip_compose_msi_msg(irq_data, &msg)
|
||||
// 构造msg,里面含有MSI或msi-x的addr/val
|
||||
its_irq_compose_msi_msg
|
||||
msg->address_lo = addr & ((1UL << 32) - 1);
|
||||
msg->address_hi = addr >> 32;
|
||||
// its_get_event_id:
|
||||
// d->hwirq - its_dev->event_map.lpi_base;
|
||||
msg->data = its_get_event_id(d);
|
||||
// 设置msi-x的entry地址
|
||||
irq_chip_write_msi_msg(irq_data, &msg);
|
||||
data->chip->irq_write_msi_msg(data, msg);
|
||||
pci_msi_domain_write_msg
|
||||
__pci_write_msi_msg(desc, msg);
|
||||
|
||||
__pci_write_msi_msg(desc, msg);
|
||||
// 对于MSI-X
|
||||
writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR);
|
||||
writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR);
|
||||
writel(msg->data, base + PCI_MSIX_ENTRY_DATA);
|
||||
|
||||
// 对于MSI
|
||||
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
|
||||
pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
|
||||
msg->address_lo);
|
||||
|
||||
|
||||
// 为PCI设备确定hwirq
|
||||
|
||||
its_domain_ops.alloc
|
||||
its_irq_domain_alloc
|
||||
its_alloc_device_irq
|
||||
*hwirq = dev->event_map.lpi_base + idx;
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.6 IRQ Domain创建流程
|
||||
|
||||
* `drivers\irqchip\irq-gic-v3-its.c`
|
||||
*
|
||||
BIN
STM32MP157/doc_pic/10_PCI_PCIe/12_INTx_MSI_MSIX中断源码分析.tif
Normal file
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 68 KiB |
BIN
STM32MP157/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/104_msi_msix.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 66 KiB |
BIN
STM32MP157/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/109_pba.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 15 KiB |
BIN
STM32MP157/doc_pic/10_PCI_PCIe/pic/10_PCI_PCIe/96_pci_intx.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 39 KiB |