Linux 获取设备树源文件(DTS)里描述的资源

本文介绍Linux平台机制中如何使用platform_driver_register()注册设备驱动,并通过platform_get_resource()获取设备资源,如中断号、内存地址等。进一步讨论了如何使用ioremap()映射IO内存资源供用户空间使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在linux使用platform_driver_register() 注册  platform_driver 时, 需要在 platform_driver 的probe() 里面知道设备的中断号, 内存地址等资源。

这些资源的描述信息存放在 resource 数据结构中, 相同的资源存放在一个树形树形数据结构中, 通过父节点, 兄弟节点, 子节点相连。 比如中断资源, IO端口资源, IO内存资源, DMA资源有不同资源树。 

Linux使用 struct resource 来描述一个resouce

struct resource {
    resource_size_t start;      //资源范围的开始
    resource_size_t end;        //资源范围的结束
    const char *name;   	//资源拥有者名
    unsigned long flags;	//资源属性标识
    struct resource *parent, *sibling, *child;   //资源树的父节点, 兄弟节点, 字节点指针
};

resource_size_t 由系统决定 为uint32_t 或uint64_t 。
 

在platform机制里, 使用platform_get_resource()来获取指定的资源类型。 


比如获取想获取中断号, 


   >platform_get_resource(dev, IORESOURCE_IRQ, num);
 
    return r ? r->start : -ENXIO;
}
EXPORT_SYMBOL_GPL(platform_get_irq);     
              

platform_get_irq() 会返回一个start, 即可用的中断号。


之后便可使用request_irq() 来注册中断服务函数。 


再比如想要获取IO内存资源:

struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);


即可得到一个IO内存资源节点指针, 包括了地址的开始,结束地址等, 该IO内存的长度可用 resource_size() 来获取, 但这段资源只是一个描述, 想真正使用这段IO内存, 还要经过先申请, 再映射的过程。例如可使用devm_request_mem_region()申请出使用这段IO内存, 再使用ioremap() 将其映射出来, 供用户空间使用。

devm_request_mem_region(&pdev->dev, res_mem->start, resource_size(res_mem),
                     res_mem->name))

addr_start = ioremap(res_mem->start, resource_size(res_mem));


ioremap() 的返回值即为该资源的虚拟地址IO内存的资源是在设备树源(Device Tree Source)文件(以.dts结尾)里给出的,.dts文件就是用来描述目标板硬件信息的, 在uboot启动后, 使用uboot提供的特定API将其获取出来, 如fdt_getprop(), fdt_path_offset(), 这些API包含在uboot 的头文件<libfdt.h> 里面。
 
uboot将.dts文件里的描述解析出来, 再对相应寄存器赋值, 在linux启动后, 使用  platform_get_resource() 即可获取到这些给定的资源, 在驱动里使用。


例如一个在.dts文件中关于gpio资源的描述:

  gpio: gpio-controller@1070000000800 {
            #gpio-cells = <2>;
            compatible = "cavium,octeon-3860-gpio";
            reg = <0x10700 0x00000800 0x0 0x100>;

            gpio-controller;

…

根据其描述, 可知道gpio控制器的IO内存起始地址为:0x107900000800, 长度为0x100.

即从 0x107900000800 到 0x1079000008ff.

在目标板里使用  cat /proc/iomem  可以看到:


1070000000800-10700000008ff : /soc@0/gpio-controller@1070000000800


关于i2c 的描述:

        twsi0: i2c@1180000001000 {
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "cavium,octeon-3860-twsi";
            reg = <0x11800 0x00001000 0x0 0x200>;

            interrupts = <0 45>;
            clock-rate = <100000>;

IO内存起始地址为: 0x118000001000, 长度为0x200. 

从 0x118000001000 到 0x1180000011ff.

在目标板里使用  cat /proc/iomem  可以看到:


1180000001000-11800000011ff : /soc@0/i2c@1180000001000

### 从设备树源文件 (DTS) 提取 GPIO 数组 在 Linux 系统中,为了从设备树源文件(DTS)获取 GPIO 数组,在 U-Boot 和内核阶段有不同的方法来实现这一操作。 #### 在U-Boot 阶段获取 GPIO 数组 对于 U-Boot 来说,可以利用 `libfdt` 库中的函数如 `fdt_getprop()` 或者其他相关 API 函数来访问设备树属性。具体来说,如果想要获得某个节点下的 GPIO 属性,则可以通过路径定位该节点并调用相应的接口取得其值[^2]。 ```c #include <libfdt.h> // 假设已经获得了指向 dtb 的指针 void *blob; int offset = fdt_path_offset(blob, "/path/to/node"); if(offset >= 0){ const char* prop; int len; // 获取 gpio 属性的内容及其长度 prop = fdt_getprop(blob, offset,"gpios",&len); } ``` 这段代码展示了如何通过指定路径找到对应的设备树节点,并尝试从中读取出名为 "gpios" 的属性数据以及它的大小。 #### 在Linux 内核阶段获取 GPIO 数组 当进入 Linux 内核环境之后,通常会编写平台驱动程序(platform driver),此时可以直接借助于内建的支持机制轻松地处理来自 DTS 文件的信息。例如,使用 `platform_get_resource()` 可以方便地获取到由 DTS 描述的各种资源;而对于像 GPIO 这样的特殊配置项,则有专门针对它们的操作接口可供选用,比如 `of_gpio_simple_xlate_specifier_to_data()` 函数用于转换 OF 格式的 GPIO 规格说明符至实际的数据形式。 下面是一个简单的例子展示怎样在一个字符设备驱动模块初始化过程中查找并打印出某节点下定义的所有 GPIOs: ```c static struct of_device_id mygpio_match[] __initdata = { { .compatible = "vendor,some-device", }, {}, }; MODULE_DEVICE_TABLE(of, mygpio_match); static int __init mygpio_init(void) { struct device_node *np; struct property *pp; np = of_find_matching_node(NULL, mygpio_match); if (!np) { pr_err("Failed to find matching node\n"); return -ENODEV; } for_each_property_of_node(np, pp) { if(strcmp(pp->name, "gpios") == 0){ unsigned long flags; u32 gc[4]; if(!of_parse_phandle_with_args(np, "gpios", "#gpio-cells", 0, &gc)){ printk(KERN_INFO "Found gpios: %lu\n", gc[0]); } } } return 0; } module_init(mygpio_init); ``` 此示例首先匹配了一个兼容字符串为 `"vendor,some-device"` 的节点,接着遍历这个节点所有的属性直到发现名称为"gpios"的那个为止。一旦找到了目标属性,就进一步解析其中的具体内容并将结果输出显示出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值