Linux PCIe 中断相关启动流程
1. PCIe控制器初始化过程
0. 准备配置空间,做outbound配置
1. 枚举 pcie 设备
2.1. 为设备准备物理内存
读写BAR0,获取需求内存大小.
写 BAR0 为 PCIe地址,做outbound设置(映射物理地址到pcie地址)
2.2. 为设备准备legacy硬件中断号,但是不会准备MSI/MSI-X硬件中断号
2. 构造 pci_dev 结构体
3.1 用 legacy irq 对应的 软件中断号 填充 pci_dev 结构体变量的 irq 成员
3.2 填充 pci_dev 结构体变量的 mem 相关成员
3.3 调用 pci_device_register 注册 pci_dev 结构体变量 到 pci总线 设备链表
2. PCIe设备初始化过程
1. 内存设置
获取到设备内存并映射物理内存到虚拟地址
2. 中断设置,这些都是在 msi_domain_alloc_irqs 中完成的 , 该函数的消费者有pci_msi_setup_msi_irqs
2.1. 申请软件中断号
从bitmap 中获取 软件中断号,然后alloc irq_desc , 并填充 handler 进 irq_action 链表
2.2. 根据rid((bus << 8) | (dev << 4) | function) 获取硬件中断号
从 设备配置空间 cap 0x11 中读取到他需要M(Table Size+1)个 msi-x 中断
解析 pcie 节点的 msi-map,做mask和offset,用rid作为输入,申请硬件中断号.
在这里会返回硬件中断号的起始number(x)和数量(n)
假设 x = 8192 , n = 3
那么会有是三个中断号 , 8192 , 8193 , 8194
对于ITS来讲,对应的 event id 为 0 1 2
2.3. 地址值对设置
做硬件配置,对于ITS,设置它的几个表.
构造M个 <addr,val> 对
将 M个 <addr,val> 对 写入 内存.内存基址由 cap msi-x 的 Table offset/Table BIR 指定
将<addr,val>中的地址做inbound设置,做 Bus Master Enable
3.MSI-X Enable 及 开启层层开关,都是在request_irq中 完成的
索引 irq_domain_ops 中的 activate
驱动详细解读
控制器侧驱动
在配置空间侧只做了一个事情:配置了 BAR0 为 0x68100004
设备侧驱动
设备驱动调用如下函数获取硬件和软件中断号.因为是pci设备,所以不能从设备树中获取硬件中断号.
pci_enable_msix_range
申请软件中断号,申请好了,会填充entry 参数的 vector 成员 为软件中断号
驱动可能会调用如下函数来获取MSI/MSI-X软件中断号,最终都会调用到 __pci_enable_msix_range
pci_alloc_irq_vectors
pci_alloc_irq_vectors_affinity
pci_enable_msix_range
pci_enable_msi
__pci_enable_msix_range->__pci_enable_msix->msix_capability_init->
pci_msi_setup_msi_irqs
msi_domain_alloc_irqs
ops->domain_alloc_irqs/__msi_domain_alloc_irqs
msi_domain_prepare_irqs
ops->msi_prepare/its_pci_msi_prepare
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
msi_info->ops->msi_prepare/its_msi_prepare
dev_id = info->scratchpad[0].ul
its_dev = its_create_device(its, dev_id, nvec, true);
its_lpi_alloc(nvecs, &lpi_base, &nr_lpis);
dev->event_map.lpi_base = lpi_base;
dev->event_map.nr_lpis = nr_lpis;
its_send_mapd(dev, 1);
for_each_msi_entry(desc, dev)
ops->set_desc(&arg, desc);/pci_msi_domain_set_desc
__irq_domain_alloc_irqs
virq = irq_domain_alloc_descs
irq_domain_alloc_irqs_hierarchy
IRQ_domain:GICV3-ITS-PCI:domain->ops->alloc/msi_domain_alloc
irq_hw_number_t hwirq = ops->get_hwirq(info, arg);
irq_domain_alloc_irqs_parent/irq_domain_alloc_irqs_hierarchy
IRQ_domain:GICV3-ITS domain->ops->alloc/its_irq_domain_alloc
its_alloc_device_irq(its_dev, nr_irqs, &hwirq)
idx = bitmap_find_free_region(...);
*hwirq = dev->event_map.lpi_base + idx;
its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
fwspec.param[1] = hwirq;
irq_domain_alloc_irqs_parent/irq_domain_alloc_irqs_hierarchy
IRQ_domain:GICV3 domain->ops->alloc/gic_irq_domain_alloc
gic_irq_domain_translate
gic_irq_domain_map/irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq
irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data);
__irq_set_handler(virq, handler, 0, handler_name);
irq_set_handler_data(virq, handler_data);
IRQ_domain:GICV3-ITS irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, &its_irq_chip, its_dev);
irq_data->hwirq = hwirq;
IRQ_domain:GICV3-ITS-PCI:ops->msi_init(domain, info, virq + i, hwirq + i, arg);/msi_domain_ops_init
irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, info->chip_data);
__irq_set_handler(virq, info->handler, 0, info->handler_name);
irq_set_handler_data(virq, info->handler_data);
for_each_msi_vector(desc, i, dev)
irq_domain_activate_irq
__irq_domain_activate_irq
domain->ops->activate/msi_domain_activate
irq_chip_compose_msi_msg
pos->chip->irq_compose_msi_msg /its_irq_compose_msi_msg
irq_chip_write_msi_msg(irq_data, msg)
data->chip->irq_write_msi_msg /pci_msi_domain_write_msg