Linux驱动开发避坑:为什么你的platform_get_resource拿不到正确的IRQ和内存地址?

张开发
2026/4/17 5:58:14 15 分钟阅读

分享文章

Linux驱动开发避坑:为什么你的platform_get_resource拿不到正确的IRQ和内存地址?
Linux驱动开发深度解析platform_get_resource获取IRQ与内存地址的底层机制与避坑指南在嵌入式Linux驱动开发中获取设备资源是驱动初始化的关键步骤。许多开发者在使用platform_get_resource或platform_get_resource_byname时会遇到IRQ或内存地址获取失败的情况这往往源于对内核资源管理机制的误解。本文将深入剖析设备树解析、资源注册与获取的完整流程帮助开发者避开这些坑。1. 设备树与platform_device的创建过程Linux内核启动早期会解析设备树Device Tree并创建对应的platform_device结构。这个过程涉及多个关键步骤// 设备树节点转换为platform_device的简化流程 of_platform_bus_create() → of_platform_device_create_pdata() → of_device_alloc()在of_device_alloc函数中内核会处理设备树节点的reg和interrupts属性for (i 0; i num_reg; i, res) { rc of_address_to_resource(np, i, res); WARN_ON(rc); } if (of_irq_to_resource_table(np, res, num_irq) ! num_irq) pr_debug(not all legacy IRQ resources mapped for %s\n, np-name);这里已经可以看出内存地址reg和中断号irq的处理方式完全不同reg直接通过of_address_to_resource转换为resource结构irq需要经过of_irq_to_resource_table进行映射转换注意设备树中的中断号通常不是最终在系统中使用的虚拟中断号需要经过中断控制器的映射转换。2. 内存地址资源的获取机制当开发者调用platform_get_resource(dev, IORESOURCE_MEM, index)时内核实际上返回的是在of_device_alloc阶段已经处理好的resource结构。这个过程的详细路径如下of_address_to_resource() → __of_address_to_resource()关键点在于__of_address_to_resource函数int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) { memset(r, 0, sizeof(*r)); r-start be32_to_cpup(addrp); r-end r-start size - 1; r-flags flags; r-name name ? name : dev-full_name; return 0; }常见的内存地址获取问题通常源于设备树中reg属性格式错误#address-cells和#size-cells设置不当未正确指定reg-names导致platform_get_resource_byname失败3. 中断资源的特殊处理流程与内存资源不同中断号需要经过更复杂的转换过程。platform_get_resource(dev, IORESOURCE_IRQ, index)的背后是of_irq_to_resource_table() → of_irq_to_resource() → of_irq_get()of_irq_get函数完成了从设备树中断号到系统中断号的映射int of_irq_get(struct device_node *dev, int index) { struct of_phandle_args oirq; int ret; ret of_irq_parse_one(dev, index, oirq); if (ret) return ret; return irq_create_of_mapping(oirq); }中断获取失败的常见原因包括设备树中interrupt-parent设置错误中断控制器驱动未正确初始化中断号超出控制器支持范围未正确设置#interrupt-cells属性4. PCIe设备的特殊资源处理PCIe设备的资源处理更为复杂涉及ranges属性的解析pcie0: pcie0xd4288000 { ranges 0x81000000 0 0 0xE0010000 0 0x00010000 /* I/O */ 0x82000000 0 0xE0020000 0xE0020000 0 0x04000000; /* MEM */ };内核通过专门的PCIe主机驱动解析这些资源int __init xxx_pcie_host_init(struct falcon_pcie_port *pp) { struct of_pci_range_parser parser; struct of_pci_range range; if (of_pci_range_parser_init(parser, np)) { dev_err(pp-dev, missing ranges property\n); return -EINVAL; } for_each_of_pci_range(parser, range) { unsigned long restype range.flags IORESOURCE_TYPE_BITS; if (restype IORESOURCE_IO) { /* 处理I/O空间 */ } if (restype IORESOURCE_MEM) { /* 处理内存空间 */ } } }PCIe资源获取的常见陷阱ranges属性格式错误未正确设置bus-range内存空间对齐问题未考虑PCI域(domain)的影响5. 实战调试技巧与问题排查当platform_get_resource获取资源失败时可以按照以下步骤排查检查设备树节点dtc -I fs /proc/device-tree | less确认reg、interrupts等属性是否存在且格式正确查看platform_device资源// 在驱动probe函数中添加 for (i 0; i dev-num_resources; i) { dev_info(dev-dev, Resource %d: %pr\n, i, dev-resource[i]); }中断映射调试// 检查of_irq_get的返回值 int irq of_irq_get(dev-of_node, 0); if (irq 0) { dev_err(dev, Failed to get IRQ: %d\n, irq); return irq; }内存映射验证struct resource *res platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pdev-dev, Failed to get MEM resource\n); return -ENXIO; } void __iomem *base devm_ioremap_resource(pdev-dev, res); if (IS_ERR(base)) { return PTR_ERR(base); }使用devicetree工具验证dtc -I dtb -O dts -o extracted.dts /boot/dtbs/$(uname -r)/board.dtb6. 高级话题资源获取的替代方案除了标准的platform_get_resource系列函数内核还提供了其他资源获取方式直接解析设备树属性u32 reg[2]; of_property_read_u32_array(dev-of_node, reg, reg, 2);使用设备树辅助函数void __iomem *base of_iomap(node, 0); int irq irq_of_parse_and_map(node, 0);ACPI资源获取struct resource *res_acpi; acpi_dev_get_resources(adev, resource_list, NULL, NULL);每种方法都有其适用场景和限制条件开发者需要根据具体需求选择最合适的方式。在调试一个PCIe网卡驱动时发现中断无法正常工作。通过dump设备树节点发现中断号正确但进一步检查发现中断控制器驱动加载顺序有问题导致中断映射失败。调整驱动加载顺序后问题解决。这个案例说明资源获取问题有时需要从整个系统角度排查。

更多文章