Linux驱动开发之设备树

设备树是一种用来描述硬件的数据结构,在Linux驱动程序中用来替代Platform_device等结构体用来描述设备的板级信息,Linux设备驱动程序通过特定的API接口从设备树中获取设备信息来对设备进行初始化和操作.

设备树可以用来描述CPU、描述时钟、描述中断控制器、描述IO控制器、描述SPI总线控制器、描述I2C总线控制器、描述存储设备等任何设备信息。

设备树是一个树状结构,由节点、属性构成,属性是成对出现的键值对,每个节点除了属性外还会包含其他的子节点。每个设备树有且只有一个根节点,每个节点中包含节点的属性和其他的子节点。

设备树有三种设备文件,DTS:供开发人员阅读编写的设备树源码,DTC:设备树源码的编译工具,需要手动安装,DTB:编译的设备树二进制文件,由uboot传递给内核,供内核使用。

设备树可以应用其他的设备树文件,一般会把公共的设备文件写到后缀名位.dtsi的文件中,其他的设备树文件使用include "xxx.dtsi"进行引用。

设备树节点格式:

node-name@unit-address{

属性 1 = …

属性 2 = …

属性 13= …

子节点…

}

node-name:设备树节点名

@unit-address:设备树单元地址,与reg属性一一对应,如果节点中不包含reg,则可以省略不写。

属性:节点的设备信息是由进行描述的,驱动通过特定API函数去获取节点中的属性值。

设备树基本概念:

cpu0: cpu@0 {                  

compatible = "arm,cortex-a7";

device_type = "cpu";

reg = <0>;

}

设备标签: cpu0:cpu@0中的cpu0是节点标签,用于向节点中追加数据。

&cpu0 { 

dc-supply = <®_gpio_dvfs>;

clock-frequency = <800000000>

};

向节点中追加数据使用&+节点标签的方式进行属性的追加。

设备树别名:属性aliases用于为设备节点取别名。

aliases {

can0 = &flexcan1;

can1 = &flexcan2;

ethernet0 = &fec1;

ethernet1 = &fec2;

};

在实际应用中,设备别名很少用到,一般用于在设备树目录中快速查找设备树节点,设备标签经常使用。

chosen属性用于向内核传递参数,一般用于uboot传递启动参数到内核。

chosen {

stdout-path = &uart1;

};

节点属性介绍:

compatible属性用于设备信息和驱动程序的匹配。

intc: interrupt-controller@a01000 {

compatible = "arm,cortex-a7-gic";

#interrupt-cells = <3>;

interrupt-controller;

reg = <0xa01000 0x1000>,

<0xa02000 0x100>;

};

compatible 属性:用于指定设备的制造商和型号

model = "Seeed i.MX6 ULL NPi Board";

status属性:用于指定设备的操作状态。

sound: sound {

status = "disabled";

};

#address-cells属性:用于指定子节点 reg 属性“地址字段”所占的长度(单元格数量)。

# size-cells属性:用于指定子节 点 reg 属性“大小字段”所占的长度(单元格数量)。

reg 属性:用于描述设备资源在其父总线定义的地址空间内的地址。通常情况下用于表示一块寄存器的起始地址 (偏移地址)和长度。

reg属性用法 :属性值类型:地址、长度数据对。

soc {

#address-cells = <1>;

#size-cells = <1>;

compatible = "simple-bus";

interrupt-parent = <&gpc>;

ranges;

ocrams: sram@900000 {

compatible = "fsl,lpm-sram";

reg = <0x900000 0x4000>;

};

};

ranges属性:用于子节点地址空间和父地址空间的转换,常见格式是 ranges = (字地址, 父地址, 转 换长度)。

ranges属性用法:属性值类型:任意数量的 < 子地址、父地址、地址长度 > 编码

soc {

#address-cells = <1>;

#size-cells = <1>;

compatible = "simple-bus";

interrupt-parent = <&gpc>;

ranges;   //无需转换

busfreq {

};

}

驱动获取设备树节点属性:

内核提供了一组函数用于从设备节点获取资源(设备节点中定义的属性)的函数,这些函数以 of_ 开头,称

为 OF 操作函数。

常用的 OF 函数介绍如下:

1.通过节点路径查找节点API:

struct device_node *of_find_node_by_path(const char *path)

参数“path”,代表节点在设备树中的路径。

如果查找失败则返回 NULL,否则返回 device_node 类型的结构体指 针,它保存着设备节点的信息。

struct device_node {

const char *name; //节点名

const char *type; //设备类型

phandle phandle;

const char *full_name; //完整名字

struct fwnode_handle fwnode;

struct property *properties; //属性

struct property *deadprops;

/* removed properties */

struct device_node *parent; //父节点

struct device_node *child; //子节点

struct device_node *sibling;

#if defined(CONFIG_OF_KOBJ)

struct kobject kobj;

#endif

unsigned long _flags;

void *data;

#if defined(CONFIG_SPARC)

const char *path_component_name;

unsigned int unique_id;

struct of_irq_controller *irq_trans;

#endif

};

得到 device_node 结构体之后我们就可以使用其他 of 函数获取节点的详细信息。

2. 根据节点名查找节点API:

struct device_node *of_find_node_by_name(struct device_node *from,const char *name)

参数 from,指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为 NULL

表示从根节点开始查找。

参数 name,要寻找的节点名。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

3. 根据节点类型查找节点API

struct device_node *of_find_node_by_type(struct device_node *from,const char *type)

类似 of_find_node_by_name 函数。

参数 from,指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为 NULL

表示从根节点开始查找。

参数 type,要查找节点的类型,这个类型就是 device_node-> type。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

4. 根据节点类型和 compatible 属性寻找节点API

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

相比 of_find_node_by_name 函数增加了一个 compatible 属性作为筛选条件。

参数 from,指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为 NULL

表示从根节点开始查找。

参数 type,要查找节点的类型,这个类型就是 device_node-> type。

参数 compatible,要查找节点的 compatible 属性。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

5. 根据匹配表寻找节点函数

static inline struct device_node *of_find_matching_node_and_match( struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)

参数 from,指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为 NULL

表示从根节点开始查找。

参数 matches,源匹配表,查找与该匹配表想匹配的设备节点。

结构体 struct of_device_id 如下所示。

可以看到,该结构体包含了更多的匹配参数,也就是说相比前三个寻找节点函数,这个函数匹配的参数更多,

对节点的筛选更细。

参数 match,查找得到的结果。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

6. 寻找父节点API

struct device_node *of_get_parent(const struct device_node *node)

参数 node,指定谁(节点)要查找父节点。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

7. 寻找子节点API

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)

参数 node,指定谁(节点)要查找它的子节点。

参数 prev,前一个子节点,寻找的是 prev 节点之后的节点。这是一个迭代寻找过程,例如寻找第二个子节

点,这里就要填第一个子节点。参数为 NULL 表示寻找第一个子节点。

返回值:device_node 类型的结构体指针,保存获取得到的节点。同样,如果失败返回 NULL。

8 .查找节点属性API

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)

参数 np,指定要获取那个设备节点的属性信息。

参数 name,属性名。

参数 lenp,获取得到的属性值的大小,这个指针作为输出参数,这个参数“带回”的值是实际获取得到的属

性大小。

返回值:获取得到的属性。这是一个结构体,我们把它称为节点属性结构体,如下所示。失败返回 NULL。

 

struct property {

char *name; //属性名

int length; //属性长度

void *value; //属性值

struct property *next; //下一个属性

#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)

unsigned long _flags;

#endif

#if defined(CONFIG_OF_PROMTREE)

unsigned int unique_id;

#endif

#if defined(CONFIG_OF_KOBJ)

struct bin_attribute attr;

#endif

};

9. 读取属性API

//8 位整数读取函数

int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)

//16 位整数读取函数

int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)

//32 位整数读取函数

int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)

//64 位整数读取函数

int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

//8 位整数读取函数

int of_property_read_u8 (const struct device_node *np, const char *propname, u8 *out_values)

//16 位整数读取函数

int of_property_read_u16 (const struct device_node *np, const char *propname, u16 *out_values)

//32 位整数读取函数

int of_property_read_u32 (const struct device_node *np, const char *propname, u32 *out_values)

//64 位整数读取函数

int of_property_read_u64 (const struct device_node *np, const char *propname, u64 *out_values

参数 np,指定要读取那个设备节点结构体,也就是说读取那个设备节点的数据。

参数 propname,指定要获取设备节点的哪个属性。

参数 out_values,这是一个输出参数,是函数的“返回值”,保存读取得到的数据。

参数 sz,这是一个输入参数,它用于设置读取的长度。

返回值,成功返回 0,错误返回错误状态码(非零值),-EINVAL(属性不存在),-ENODATA(没有要读取

的数据),-EOVERFLOW(属性值列表太小)。

10 .读取字符串属性API

int of_property_read_string_index(const struct device_node *np,const char *propname,

int index,const char **out_string)

第一个函数,介绍如下:

参数 np,指定要获取那个设备节点的属性信息。

参数 propname,属性名。

了参数 index,它用于指定读取属性值中第几个字符串,index 从零开始计数。

参数 out_string,获取得到字符串指针。

返回值:成功返回 0,失败返回错误状态码。

11. BOOL型属性读取API

static inline bool of_property_read_bool(const struct device_node *np, const char *propname):

参数 np,指定要获取那个设备节点的属性信息。

参数 propname,属性名。

返回值:true , 属性存在。false, 其他。

12. 物理地址与虚拟地址转换API

void __iomem *of_iomap(struct device_node *np, int index)

参数 np,指定要获取那个设备节点的属性信息。

参数 index,通常情况下 reg 属性包含多段,index 用于指定映射那一段,标号从 0 开始。;

返回值:成功,得到转换得到的地址。失败返回 NULL。

13. 获取设备树中地址API

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)

参数 np,指定要获取那个设备节点的属性信息。

参数 index,通常情况下 reg 属性包含多段,index 用于指定映射那一段,标号从 0 开始。

参数 r,这是一个 resource 结构体,是“输出参数”用于返回得到的地址信息。

返回值,成功返回 0,失败返回错误状态码。

struct resource {

resource_size_t start; //起始地址

resource_size_t end;

//结束地址

const char *name;

//属性名字

unsigned long flags;

unsigned long desc;

struct resource *parent, *sibling, *child;

};

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值