从设备树到驱动:platform_get_resource如何解析reg与irq资源

张开发
2026/4/28 7:33:32 15 分钟阅读
从设备树到驱动:platform_get_resource如何解析reg与irq资源
1. 设备树与驱动开发的基础概念在嵌入式Linux开发中设备树Device Tree和驱动程序的配合使用是一个非常重要的环节。设备树就像是一张硬件地图它详细描述了系统中所有硬件设备的配置信息。而驱动程序则是操作这些硬件的软件接口。我刚开始接触设备树时常常困惑于硬件描述和驱动代码之间是如何衔接的。特别是在处理内存映射reg和中断irq这类关键资源时经常遇到驱动无法正确获取资源的问题。后来通过研究platform_get_resource这个关键API才真正理解了内核是如何把设备树中的硬件描述转换成驱动可用的资源。设备树中关于资源描述最常见的有两种reg属性描述设备的内存映射区域包括起始地址和长度interrupts属性描述设备使用的中断号举个例子一个简单的设备树节点可能长这样my_device { compatible vendor,my-device; reg 0x10000000 0x1000; interrupts 15; };这个节点描述了一个设备它的寄存器空间从0x10000000开始大小是0x1000字节使用的中断号是15。驱动需要获取这些信息才能正确操作硬件。2. platform_get_resource的工作原理2.1 资源获取的基本流程platform_get_resource是驱动开发者最常用的API之一它的作用是从platform_device中获取指定的资源。在实际项目中我发现这个API背后其实隐藏着复杂的处理逻辑。先来看一个典型的使用场景struct resource *res; res platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pdev-dev, Failed to get MEM resource\n); return -ENODEV; }这段代码尝试获取platform_device的第一个内存资源。内核处理这个请求的完整流程可以分为几个关键步骤内核启动时解析设备树创建platform_device结构体将设备树中的reg和interrupts属性转换为标准的resource结构驱动调用platform_get_resource获取这些资源内核根据资源类型和索引返回对应的resource结构2.2 内存资源的解析细节对于内存资源IORESOURCE_MEM内核主要通过of_address_to_resource函数来完成转换。这个函数会处理设备树中的reg属性将其转换为resource结构。我曾在调试一个PCIe驱动时发现设备无法正常工作最终发现是因为reg属性解析出错。通过深入研究发现of_address_to_resource内部会调用__of_address_to_resource这个函数做了几件重要的事情解析reg属性的地址和大小处理地址映射和转换如果需要设置resource结构的start和end字段处理可选的reg-names属性一个典型的reg属性可能包含多个地址范围reg 0x10000000 0x1000, // 寄存器区域1 0x20000000 0x2000; // 寄存器区域2驱动可以通过索引来获取不同的区域res1 platform_get_resource(pdev, IORESOURCE_MEM, 0); // 获取第一个区域 res2 platform_get_resource(pdev, IORESOURCE_MEM, 1); // 获取第二个区域3. 中断资源的处理机制3.1 中断号的获取与转换中断资源的处理比内存资源更为复杂。在设备树中中断通常通过interrupts属性描述但驱动最终需要的是Linux内核的虚拟中断号。platform_get_irq是获取中断号的主要接口它的典型用法如下int irq platform_get_irq(pdev, 0); if (irq 0) { dev_err(pdev-dev, Failed to get IRQ resource\n); return irq; }这个函数背后调用了of_irq_get来完成实际的转换工作。我曾在调试一个GPIO中断时发现设备树中定义的中断号与驱动获取到的并不相同。这是因为内核在启动过程中会对中断号进行重新映射。3.2 中断资源的早期处理在内核启动的早期阶段of_platform_bus_create函数会处理设备树节点创建对应的platform_device。对于中断资源它会调用of_irq_to_resource_table函数static void of_device_alloc(struct device_node *np, ...) { // 处理内存资源 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); }这个函数最终会调用of_irq_get来获取实际的中断号并填充resource结构。值得注意的是对于中断资源resource的start和end字段都会被设置为相同的中断号。4. PCIe设备树资源的特殊处理4.1 ranges属性的解析PCIe设备的资源处理有其特殊性主要体现在设备树中的ranges属性上。一个典型的PCIe节点可能包含如下定义pcie0: pcie0xd4288000 { #address-cells 3; #size-cells 2; reg 0xd4210000 0x800, 0xd4288000 0x1000; reg-names pciephy, pciectrl; ranges 0x81000000 0 0 0xE0010000 0 0x00010000 0x82000000 0 0xE0020000 0xE0020000 0 0x04000000; interrupts 18; };ranges属性描述了PCI地址空间到CPU地址空间的映射关系。在内核中这个属性的解析是通过of_pci_range_parser_init和for_each_of_pci_range函数完成的。4.2 PCIe资源转换实例在PCIe主机控制器驱动中通常会看到这样的资源处理代码struct of_pci_range range; struct of_pci_range_parser parser; 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空间 of_pci_range_to_resource(range, np, pp-io); pp-io.start range.pci_addr global_io_offset; pp-io.end pp-io.start range.size - 1; } if (restype IORESOURCE_MEM) { // 处理内存空间 of_pci_range_to_resource(range, np, pp-mem); pp-mem.start range.cpu_addr; pp-mem.end pp-mem.start range.size - 1; } }这段代码展示了如何将设备树中的ranges属性转换为驱动可用的资源信息。for_each_of_pci_range宏会遍历所有的地址范围根据flags字段区分I/O空间和内存空间然后分别处理。5. 实际开发中的经验与技巧5.1 常见问题排查在开发过程中经常会遇到资源获取失败的情况。根据我的经验可以从以下几个方面排查检查设备树节点确认reg和interrupts属性是否正确定义验证compatible字符串确保驱动和设备树的compatible匹配检查资源索引platform_get_resource的索引要从0开始顺序使用查看内核启动日志搜索设备节点名看是否有资源分配错误我曾经遇到过一个案例驱动无法获取中断资源最终发现是因为设备树中的interrupt-parent设置不正确。这种情况下内核日志通常会给出相关提示。5.2 调试技巧为了更直观地了解资源分配情况可以使用以下方法查看/sys文件系统cat /sys/devices/platform/device/resource这个文件会显示设备的所有资源信息使用devicetree编译器dtc -I fs /sys/firmware/devicetree/base可以查看内核实际使用的设备树添加调试打印在驱动probe函数中添加资源信息的打印dev_info(pdev-dev, Registers: %pr\n, platform_get_resource(pdev, IORESOURCE_MEM, 0)); dev_info(pdev-dev, IRQ: %d\n, platform_get_irq(pdev, 0));5.3 性能优化建议在处理大量资源时需要注意以下几点避免重复获取资源在probe函数中获取资源后保存到设备私有数据结构中合理使用devm_接口自动管理资源释放减少错误可能性注意资源冲突特别是中断共享时要正确设置IRQF_SHARED标志在开发一个复杂的多功能设备驱动时我最初每次操作都重新获取资源导致性能下降。后来改为在probe时一次性获取并保存所有资源性能得到了显著提升。

更多文章