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

简介:

转自:http://www.linuxidc.com/Linux/2013-07/86839.htm

转自:http://blog.sina.com.cn/s/blog_636a55070101mced.html

在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()来获取指定的资源类型。


比如获取想获取中断号,


    irq = platform_get_irq(pdev, 0);
 
int platform_get_irq(struct platform_device *dev, unsigned int num) 
{
    struct resource *r = 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

=====================================================================================

=====================================================================================

 

platform_get_resource函数源码如下:

struct resource *platform_get_resource(struct platform_device *dev,

                                   unsigned int type, unsigned int num)

{

       int i;

 

       for (i = 0; i < dev->num_resources; i++) {

              struct resource *r = &dev->resource[i];

 

              if (type == resource_type(r) && num-- == 0)

                     return r;

       }

       return NULL;

}

函数分析:

struct resource *r = &dev->resource[i];

这行代码使得不管你是想获取哪一份资源都从第一份资源开始搜索。

if (type == resource_type(r) && num-- == 0)

这行代码首先通过type == resource_type(r)判断当前这份资源的类型是否匹配,如果匹配则再通过num-- == 0判断是否是你要的,如果不匹配重新提取下一份资源而不会执行num-- == 0这一句代码。

通过以上两步就能定位到你要找的资源了,接着把资源返回即可。如果都不匹配就返回NULL。

实例分析:

下面通过一个例子来看看它是如何拿到设备资源的。

设备资源如下:

static struct resource s3c_buttons_resource[] = {

       [0]={

              .start = S3C24XX_PA_GPIO,

              .end   = S3C24XX_PA_GPIO + S3C24XX_SZ_GPIO - 1,

              .flags = IORESOURCE_MEM,

       },

       [1]={

              .start = IRQ_EINT8,

              .end   = IRQ_EINT8,

              .flags = IORESOURCE_IRQ,

       },

       [2]={

              .start = IRQ_EINT11,

              .end   = IRQ_EINT11,

              .flags = IORESOURCE_IRQ,

       },

       [3]={

              .start = IRQ_EINT13,

              .end   = IRQ_EINT13,

              .flags = IORESOURCE_IRQ,

       },

       [4]={

              .start = IRQ_EINT14,

              .end   = IRQ_EINT14,

              .flags = IORESOURCE_IRQ,

       },

       [5]={

              .start = IRQ_EINT15,

              .end   = IRQ_EINT15,

              .flags = IORESOURCE_IRQ,

       },

       [6]={

              .start = IRQ_EINT19,

              .end   = IRQ_EINT19,

              .flags = IORESOURCE_IRQ,

       }

};

驱动中通过下面代码拿到第一份资源:

struct resource *res;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

函数进入for里面,i=0,num_resources=7,拿出resource[0]资源。resource_type(r)提取出该份资源 的资源类型并与函数传递下来的资源类型进行比较,匹配。Num=0(这里先判断是否等于0再自减1)符合要求,从而返回该资源。

获取剩下资源的代码如下:

for(i=0; i<6; i++){

              buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);

             if(buttons_irq == NULL){

                  dev_err(dev,"no irq resource specified\n");

                   ret = -ENOENT;

                   goto err_map;

              }

              button_irqs[i] = buttons_irq->start; 

}

分析如下:

For第一次循环:

buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);

在拿出第一份资源进行resource_type(r)判断资源类型时不符合(此时num-- == 0这句没有执行),进而拿出第二份资源,此时i=1,num_resources=7,num传递下来为0,资源类型判断时候匹配,num也等于0,从而确定资源并返回。

For第二次循环:

buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,1);

拿出第二份资源的时候resource_type(r)资源类型匹配,但是num传递下来时候为1,执行num-- == 0时不符合(但num开始自减1,这导致拿出第三份资源时num==0),只好拿出第三份资源。剩下的以此类推。

 

总结:

struct resource *platform_get_resource(struct platform_device *dev,

                                   unsigned int type, unsigned int num)

unsigned int type决定资源的类型,unsigned int num决定type类型的第几份资源(从0开始)。即使同类型资源在资源数组中不是连续排放也可以定位得到该资源。

比如第一份IORESOURCE_IRQ类型资源在resource[2],而第二份在resource[5],那

platform_get_resource(pdev,IORESOURCE_IRQ,0);

可以定位第一份IORESOURCE_IRQ资源;

platform_get_resourc

 












本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/4800848.html,如需转载请自行联系原作者


相关文章
|
15天前
|
Java Linux
Springboot 解决linux服务器下获取不到项目Resources下资源
Springboot 解决linux服务器下获取不到项目Resources下资源
|
16天前
|
Linux 编译器 开发者
Linux设备树解析:桥接硬件与操作系统的关键架构
在探索Linux的庞大和复杂世界时🌌,我们经常会遇到许多关键概念和工具🛠️,它们使得Linux成为了一个强大和灵活的操作系统💪。其中,"设备树"(Device Tree)是一个不可或缺的部分🌲,尤其是在嵌入式系统🖥️和多平台硬件支持方面🔌。让我们深入了解Linux设备树是什么,它的起源,以及为什么Linux需要它🌳。
Linux设备树解析:桥接硬件与操作系统的关键架构
|
1天前
|
固态存储 Ubuntu Linux
Linux(29) 多线程快速解压缩|删除|监视大型文件
Linux(29) 多线程快速解压缩|删除|监视大型文件
7 1
|
1天前
|
Ubuntu Linux 数据安全/隐私保护
Linux(24) 如何在Ubuntu中操作rootfs.img文件
Linux(24) 如何在Ubuntu中操作rootfs.img文件
2 0
|
5天前
|
安全 Linux 开发工具
Linux中可引起文件时间戳改变的相关命令
【4月更文挑战第12天】Linux中可引起文件时间戳改变的相关命令
12 0
|
7天前
|
Linux Shell 开发工具
Linux文件常用操作
Linux文件常用操作(几乎覆盖所有日常使用)
77 0
|
8天前
|
Linux 内存技术 Perl
【ZYNQ】制作从 QSPI Flash 启动 Linux 的启动文件
【ZYNQ】制作从 QSPI Flash 启动 Linux 的启动文件
|
14天前
|
Linux
linux 超过4个G的文件传不上去的解决办法
linux 超过4个G的文件传不上去的解决办法
9 0
|
14天前
|
Linux 索引
linux 文件查找 和文件管理常用命令
linux 文件查找 和文件管理常用命令
22 0
|
15天前
|
网络协议 Linux SDN
虚拟网络设备与Linux网络协议栈
在现代计算环境中,虚拟网络设备在实现灵活的网络配置和隔离方面发挥了至关重要的作用🔧,特别是在容器化和虚拟化技术广泛应用的今天🌐。而Linux网络协议栈则是操作系统处理网络通信的核心💻,它支持广泛的协议和网络服务🌍,确保数据正确地在网络中传输。本文将深入分析虚拟网络设备与Linux网络协议栈的关联,揭示它们如何共同工作以支持复杂的网络需求。