rk3288 内核对设备树的处理和使用

内核对设备树的处理

从源码dts文件开始:
处理过程

  1. dts在PC机上被编译为dtb文件
  2. u-boot把dtb文件传给内核
  3. 内核解析dtb文件,把每一个节点都转换为device_node结构体
  4. 对于某些device_node结构体,会被转换为platform_device结构体

哪些设备树节点会被转化为platform_device

  1. 根节点下含有compatible属性的子节点

  2. 含有特定compatible属性的节点的子节点
    如果一个节点属性有"simple-bus",“simpile-mfd”, “isa”, “arm,armba-bus”,
    那么它的子节点(需要包含compatible属性)也可以转换成platform_device。

  3. 总线I2C、SPI节点下的子节点:不转换为platform_device。
    某个总线下到子节点,应该交给对应的总线驱动程序来处理,他们呢不应该被转换为platform_device。

/ {												//是否会转换成platform_device
	mytest {									//Yes(条件1)
 		compatile = "mytest", "simple-bus";
 		mytest@0 {								//Yes(条件2)
 			compatile = "mytest_0";
 		};
 	};
 
	i2c {										//Yes(条件1)
		compatile = "samsung,i2c";
		at24c02 {								//No(条件3)
			compatile = "at24c02"; 
 		};
	};
	
	spi {										//Yes(条件1)
		compatile = "samsung,spi"; 
 		flash@0 {								//No(条件3)
			compatible = "winbond,w25q32dw";
			spi-max-frequency = <25000000>;
			reg = <0>;
 		};
 	};
};

如何转换成platform_device

  • platform_device中含有resource数组,它来自device_node的reg,interrupts属性。
  • platform_device.dev.of_node指向device_node,可以通过它获得其他属性。
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)								//1.最先比较是否强制选择某个driver
		return !strcmp(pdev->driver_override, drv->name);	//可以设置driver_override强制选择某个platform_driver

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))					//2.比较platform_device.dev.of_node和
		return 1;											//platfrom_driver.driver.of_match_table

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))					
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)										//3.接下来比较platform_device.name和platform_driver.id_table[i].name
		return platform_match_id(pdrv->id_table, pdev) != NULL;	//id_table可能有多个

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);			//4.最后比较platfrom_device.name和platform_driver.driver.name
}

//如果一个platform_driver支持设备树,它的platform_driver.driver.of_match_table是一个数组。类型如下:
struct of_device_id {
	char	name[32];				//3.最后如果有,就和dev的name属性比较
	char	type[32];				//2.其次如果有,就比较type值,与dev的device_type属性比较
	char	compatible[128];		//1.如果of_match_table中含有compatible值,就跟dev的compatible属性比较。返回成功或失败
	const void *data;
};									//设备树中不再建议使用device_type和name属性。

dtb中每个节点都会被转化为device_node结构体

struct device_node {
	const char *name;								//来自设备树节点的name属性
	const char *type;								//来自设备树节点的type属性
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;					//含有compatible属性
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
	struct	kobject kobj;
	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
};

struct property {
	bool deleted;
	char *name;
	struct data val;

	struct property *next;

	struct label *labels;
};
类型头文件说明
处理DTBof_fdt.h dtbof_fdt.h dtb文件相关操作函数,一般用不到。内核中已经转换成device_node树
处理device_nodeof.h提供设备树的一般处理函数, 比如 of_property_read_u32, of_get_child_count
-of_address.h地址相关的函数, 比如 of_get_address ,of_match_device
-of_dma.h设备树中 DMA 相关属性的函数
-of_gpio.hGPIO 相关的函数
-of_graph.hGPU 相关驱动中用到的函数, 从设备树中获得 GPU 信息
-of_iommu.h很少用到
-of_irq.h中断相关的函数
-of_mdio.hMDIO (Ethernet PHY) API
-of_net.hOF helpers for network devices.
-of_pci.hPCI 相关函数
-of_pdt.h很少用到
-of_reserved_mem.hreserved_mem 的相关函数
处理platform_deviceof_platform.h把 device_node 转换为 platform_device 时用到的函数, 比如 of_device_alloc, of_find_device_by_node
-of_device.h设备相关的函数, 比如 of_match_device

platform_device相关函数

//of_platform.h
extern struct platform_device *of_find_device_by_node(struct device_node *np);			//知道device_node找platform_device

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num); 
//对于设备树节点中的 reg 属性,它对应 IORESOURCE_MEM 类型的资源; 
//对于设备树节点中的 interrupts 属性,它对应 IORESOURCE_IRQ 类型的资源。

找节点

//incldue/linux/of.h
//根据路径找节点
static inline struct device_node *of_find_node_by_path(const char *path);		
//根据name找节点
extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
//根据type找节点
extern struct device_node *of_find_node_by_type(struct device_node *from,const char *type);
//根据compatible找节点,from开始节点,type可以为NULL,指定device_type值,compat指定compatible的值
extern struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
//根据phandle找节点,每个节点都有一个数字id
extern struct device_node *of_find_node_by_phandle(phandle handle);
//根据节点找父节点
extern struct device_node *of_get_parent(const struct device_node *node);
//根据节点找父节点,多调用了of_node_put(node)
extern struct device_node *of_get_next_parent(struct device_node *node);
//取出下一个子节点,prev是上一个子节点如果为NULL就找第一个
extern struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
//取出下一个可用子节点,status是"disabled"就会被跳过
extern struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev);
//根据名字取出子节点
extern struct device_node *of_get_child_by_name(const struct device_node *node, const char *name);

找属性

//incldue/linux/of.h
//找到节点中的属性,np表示节点,name表示名字,lenp用来保存这个属性的长度值
//xxx_pp_name = "hello";那么它的属性值是6
extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);
//根据名字找到节点的属性,np表示节点,name表示名字,lenp用来保存这个属性的长度值
extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp);
//根据名字找到节点的属性,确定它有多少个元素
//np表示节点,找到propname的属性,返回pro->length/elem_size
// xxx_pp_name = <0x50000000 1024> <0x60000000 2048>
// 调用 of_property_count_elems_of_size(np, "xxx_pp_name", 8)时,返回值是 2
// 调用 of_property_count_elems_of_size(np, "xxx_pp_name", 4)时,返回值是 4
extern int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size);
// 读整数u32/u64
// name1 = <0x50000000>;
// name2 = <0x50000000 0x60000000>;
// 调用 of_property_read_u32 (np, “name1”, &val)时,val 将得到值 0x50000000
// 调用 of_property_read_u64 (np, “name2”, &val)时,val 将得到值 0x0x6000000050000000
static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);
extern int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);
// 读整数u32/u64,index为序号
// 调用 of_property_read_u32 (np, "name2", 1, &val)时,val 将得到值 0x0x60000000
extern int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value);

// 读数组
// name2 = <0x50000012 0x60000034>;
// 调用 of_property_read_variable_u8_array (np, “name2”, out_values, 1, 10)时,out_values 中将会保存这 8 个字节: 0x12,0x00,0x00,0x50,0x34,0x00,0x00,0x60
// 调用 of_property_read_variable_u16_array (np, “name2”, out_values, 1, 10)时,out_values中将会保存这 4 个 16 位数值: 0x0012, 0x5000,0x0034,0x6000
// 会返回全部数值,要么没有。长度介于sz_min和sz_max之间
int of_property_read_variable_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz_min, size_t sz_max);

// 读字符串
// 返回np的propname的名字的值,(*out_string)指向这个值
extern int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);

修改设备树文件方法

  • 使用芯片厂家提供的工具
  • 看绑定文档
  • 参考同类型单板的设备树文件
  • 网上搜索
  • 实在没办法时, 只能去研究驱动源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

习惯就好zz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值