内核获取设备树资源

内核获取设备树node数据


device_node转换成platform_device

内核如何将dtb转换为device_node

  • 设备树替换了平台总线模型当中对硬件资源描述的device部分。所以设备树也是对硬件资源进行描述的文件。
  • 在平台总线模型中,device部分是用platform_device结构体来描述硬件资源的。所以内核最终会将内核认识的device_node树转换platform_device。
  • 但是并不是所有的节点都会被转换成platform_device,只有满足要求的才会转换成platform_device,转换成platform_device的节点可以在/sys/bus/platform/devices下查看。

满足转换的要求:

  1. 根节点下包含compatible属性的子节点
  2. 节点中compatible属性包含simple-bus,simple-mfd,isa其中之一的节点下包含compatible属性的子节点
  3. 如果节点的compatible属性包含arm,primecell值,则对应的节点会被转换成不会被转换成amba设备。不会被转换成platform_device。

如何获取设备树的节点信息:

  • 内核中使用device_node结构体来描述一个节点,这个结构体定义在文件include/linux/of.h,一个device_node就表示一个节点
    在匹配时,使用of_match_table做匹配的规则中的compatible属性,来匹配设备树中的节点。

  • 内核中的Driver和Device匹配优先级:

      • 匹配优先级: name < id_table < of_match_table

  举个栗子:

alt text

实例分析:获取常规属性值

  测试用例:
alt text

of_find_node by_name函数:
函数原型: struct device node *of find node by name(struct device node *from, const char *name)

函数作用:通过节点的名字来查找指定的节点。
函数参数和返回值:
form: 从哪个节点开始查找,如果是NULL表示从根节点开始查找,name: 要查找的节点的名字
返回值: 成功返回查找到节点,失败返回NULL。

struct device_node *my_node;
//在probe函数函数中调用
my_node = of_find_node(NULL,"myled");
printk("name is %s",my_node->name);

of_find_property函数:
函数原型: const struct property *of_find_property(struct device_node *np, const char *name,int *lenp)

函数作用:通过节点的名字来查找指定的属性。
函数参数和返回值:
np: 要查找的节点,name: 要查找的属性的名字,lenp:属性值的字节数
返回值: 成功返回查找到的属性,失败返回NULL。

int size;
struct property *my_property;
//在probe函数函数中调用
my_property = of_find_property(my_node,"compatible",&size);
printk("my_property name is %s",my_property->name);

of_property_count_elems_of_size函数:
函数原型: int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem size)

函数作用:获取属性中元素的数量。
函数参数和返回值:
np:设备节点,propname:需要获取那个属性的元素数量,elem_size:单个元素尺寸
返回值:成功返回等到元素的数量

int num;
num = of_property_count_elems_of_size(my_node,"reg",4);
printk("reg num is %d\n",num);
//num为2,返回的是reg中的数值个数

of_property_read_u64_index函数
函数原型: of_property_read_u64_index(const struct device_node *np, const char *propname, u32 index, u64 *out_value)

函数作用: 从指定的属性中获取指定标号的u64类型的数据值
函数参数和返回值:
np:设备节点。
propname:要读取的属性名字
index:读取这个属性下第几个值,index从0开始
out value:读到的值
返回值: 成功返回0

u32 value_u32;
u64 value_u64;
of_property_read_u64_index(my_node,"reg",0,&value_u64);
printk("value_u64 is %llu\n",value_u64);
//value_u64 is 0xfdd0000
of_property_read_u32_index(my_node,"reg",1,&value_u32);
printk("value_u32 is %u\n",value_u32);
//value_u32 is 0xfdd000000000004

//使用不同的数据类型,获取到的数据是不同的

读取字符串:
static inline int_of_property_read_string(const struct device_node *np, const char *propname, const char **out string)

函数作用: 读取属性中的字符串
函数参数和返回值:
np:设备节点。propname:要读取的属性名字。out_string:读取到的字符串
返回值:成功返回0

char *value_compatible;
of_property_read_string(my_node,"compatible",&value_compatible);
printk("value_compatible is %s\n",value_compatible);
//value_compatible is "my_devicetree"

irq_of_parse_and_map函数
函数原型: unsigned int irq_of_parse_and_map(struct device_node *np,int index)

函数作用: 从interrupts中获取中断号
函数参数和返回值:
np:设备节点。index:索引值 (设备树中可同时描述多个中断资源,根据索引号(从0开始),找到需要的中断号)
返回值:成功返回中断号,失败返回-1

实例分析 : GPIO0的PB5引脚的中断功能,低电平触发

alt text
获取中断的中断号触发方式

int irq_num;
struct irq_data *my_irq_data;
u32 trigger_type;
//在probe函数中调用
int probe(struct platform_device *pdev){
    my_node = of_find_node_by_name(NULL,"myirq");
    //获取中断号
    irq_num = irq_of_parse_and_map(my_node,0);
    //获取中断的结构体信息
    my_irq_data = irq_get_irq_data(irq_num);
    //获取触发方式
    trigger_type = irq_get_trigger_type(my_irq_data);
    printk("trigger_type is %d\n",trigger_type);
    /*
    trigger_type = my_irq_data->hwirq_type;
    printk("trigger_type is %d\n",trigger_type);
    */

    return 0;
}

注:获取设备树资源的of操作函数集,虽然大都在probe中使用,但并不局限于此,可以在其他地方使用。例如:deiver中使用compatible = "my_devicetree"来匹配myled节点数据,但仍然可以在其probe函数中使用设备树资源获取函数得到myirq节点的数据,而经常在probe中使用,为了程序的逻辑性 : 在匹配到对应的device后再获取资源

特殊属性ranges:地址映射

ranges两种格式:

  1. 一种带参数 ranges= <child-bus-address parent-bus-address length>
    按照规则映射
  2. 一种不带参数 ranges;
    1:1映射,内存区域,表示不需要映射
  3. 没有ranges属性
    表示设备只能被父节点访问

参数:

  • child-bus-address:子地址物理空间的起始地址。由 ranges 所在节点“#address-cells”属性决定该地址所占的字长
  • parent-bus-address:父地址物理空间的起始地址。由 ranges 的父节“#address-cells”属性决定该地址所占的字长
  • length:映射的大小,由ranges 的父节点“#size-cells”属性决定该地址所占的字长

实例分析:如何读取数据格式

alt text

  作用:将子地址空间映射到父地址的空间。

  使用场景:当子地址的物理空间和父地址的物理空间不在同一个总线上时,使用ranges属性来描述子地址到父地址的映射关系。
  比如一些非内存映射的设备,可以类比为CPU不能直接访问的设备(内存映射设备类比可被CPU直接访问,1:1映射),需要地址映射才可以访问:好比I2c控制器下面挂载的I2c从设备,是通过I2c的协议通讯的,是串行通信方式,而CPU和I2c控制器是并行通信方式,所以CPU不能直接访问I2c控制器,需要通过I2c协议进行访问。I2c下的设备,只能被其控制器访问。
alt text

  使用方法:在子地址的节点中使用ranges属性来描述子地址到父地址的映射关系。

ranges=<0x0 0x20 0x100>       

把子地址空间(0x0–(0x0+0x100))映射到父地址空间 (0x10–(0x20+0x100))

  • 18
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想和我重名?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值