内核获取设备树node数据
device_node转换成platform_device
- 设备树替换了平台总线模型当中对硬件资源描述的device部分。所以设备树也是对硬件资源进行描述的文件。
- 在平台总线模型中,device部分是用platform_device结构体来描述硬件资源的。所以内核最终会将内核认识的device_node树转换platform_device。
- 但是并不是所有的节点都会被转换成platform_device,只有满足要求的才会转换成platform_device,转换成platform_device的节点可以在
/sys/bus/platform/devices
下查看。
满足转换的要求:
- 根节点下包含compatible属性的子节点
- 节点中compatible属性包含simple-bus,simple-mfd,isa其中之一的节点下包含compatible属性的子节点
- 如果节点的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
-
举个栗子:
实例分析:获取常规属性值
测试用例:
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引脚的中断功能,低电平触发
获取中断的中断号
和触发方式
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两种格式:
- 一种带参数
ranges= <child-bus-address parent-bus-address length>
按照规则映射- 一种不带参数
ranges;
1:1映射,内存区域,表示不需要映射- 没有ranges属性
表示设备只能被父节点访问
参数:
- child-bus-address:子地址物理空间的起始地址。由 ranges 所在节点“#address-cells”属性决定该地址所占的字长
- parent-bus-address:父地址物理空间的起始地址。由 ranges 的父节“#address-cells”属性决定该地址所占的字长
- length:映射的大小,由ranges 的父节点“#size-cells”属性决定该地址所占的字长
实例分析:如何读取数据格式
作用:将子地址空间映射到父地址的空间。
使用场景:当子地址的物理空间和父地址的物理空间不在同一个总线上时,使用ranges属性来描述子地址到父地址的映射关系。
比如一些非内存映射的设备,可以类比为CPU不能直接访问的设备(内存映射设备类比可被CPU直接访问,1:1映射),需要地址映射才可以访问:好比I2c控制器下面挂载的I2c从设备,是通过I2c的协议通讯的,是串行通信方式,而CPU和I2c控制器是并行通信方式,所以CPU不能直接访问I2c控制器,需要通过I2c协议进行访问。I2c下的设备,只能被其控制器访问。
使用方法:在子地址的节点中使用ranges属性来描述子地址到父地址的映射关系。
ranges=<0x0 0x20 0x100>
把子地址空间(0x0–(0x0+0x100))映射到父地址空间 (0x10–(0x20+0x100))