uboot驱动之udevice

struct udevice --> driver的一个实例
该结构保存关于设备的一些信息,这个设备是驱动绑定的某个端口或外设,在本质上它也还是个driver实例。
可通过调用bind函数创建一个device,使用U_BOOT_DEVICE()宏实现(在这种情况下,platdata为非空)或在设备树中创建一个节点(在这种情况下of_offset大于0)。在后一种情况下,是将设备树信息保存到platdata中去的,保存操作的函数为驱动的ofdata_to_platdata方法(当设备是一个设备树节点形式时,该方法会在probe方法调用之前被调用)。
platdata,priv和uclass_priv结构可通过driver来申请,或通过设置driver和uclass_driver结构体的成员变量auto_alloc_size来让驱动模型自动实现。
Uboot驱动模型中有三个概念:
1. uclass: 表示同一种类型的设备,提供相同接口,比如I2C,SPI,GPIO等
2.driver: 表示一个读取设备数据的接口给更高层
3.udevice: 表示driver的实例,是绑定到具体端口或外设的

比如SPI UCLASS_DRIVER:
 

UCLASS_DRIVER(spi) = {
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.post_bind	= dm_scan_fdt_dev,
#endif
	.post_probe	= spi_post_probe,
	.child_pre_probe = spi_child_pre_probe,
	.per_device_auto_alloc_size = sizeof(struct dm_spi_bus),
	.per_child_auto_alloc_size = sizeof(struct spi_slave),
	.per_child_platdata_auto_alloc_size =
			sizeof(struct dm_spi_slave_platdata),
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.child_post_bind = spi_child_post_bind,
#endif
};

ll_entry_declare宏会声明一个结构体变量以进入到section,变量会被放入连接器生成的数组中。这是一个基本构建块,它是一种对连接器生成数组更为高级的使用方式。用户期望通过这个宏来构建自己的宏封装。
使用该宏的变量必须是编译时初始化的,非运行时。
使用该宏需要注意的点如下:
_type: 为入口类型,这里是struct uclass_driver,不能是static类型,否则虽然入口能生成且可被遍历,但是在map文件中list且不能通过name来获取到。 
_name: 为入口名,这里是传入的spi
_list: 为列表名,只能是字符形式。
为防止一个section及其subsection的声明都包含数组元素,需要这些数组元素是同一种类型。
__attribute__
由此可知,上例spi中,结构体变量为:
struct uclass_driver _u_boot_list_2_uclass_2_spi 同时 将它存放在段: .u_boot_list_2_uclass_2_spi 

下面就是通过UCLASS_SPI这个ID号来找到上面声明的uclass结构体了。


ll_entry_start获取某个section的起始位置,这里就是根据.u_boot_list_2_uclass_1的段地址来获取到uclass_driver table的地址。
ll_entry_count获取uclass_driver table的长度。
因此lists_uclass_lookup就是找到id所指的结构体了。
U_Boot Driver与Uclass Driver类似:



同理也是得到一个结构体_uboot_list_2_driver_2_spi_generic_drv 存储在.u_boot_list_2_driver_2_spi_generic_drv。
这些段在uboot实际加载时,是如何进行的?
实际是在u-boot\common\board_f.c的init_sequence_f中调用initf_dm来加载的。
其中dm_init_and_scan:

dm_init: 创建driver, uclass,udevice空链表,根设备root deivce,。

dm_scan_platdata: 扫描U_BOOT_DEVICE定义的设备,与U_BOOT_DRIVER定义的driver进行查找匹配,并绑定相应driver




lists_bind_driver: 从段.u_boot_list_2_driver_info_1中查找, 在for循环中将driver_info列表中的name,依次与driver列表中的名字进行匹配查找,然后绑定device_bind_by_name。
device_bind_by_name,通过lists_driver_lookup_name从driver list查找info的名,在通过device_bind_common创建udevice来绑定。


dm_extended_scan_fdt:扫描由FDT设备树文件定义的设备,与U_BOOT_DRIVER定义的driver进行查找匹配,并绑定相应driver。这里会对compatible所定义的字符串进行匹配查找。

其中U_BOOT_DEVICE定义如下,需要与U_BOOT_DRIVER进行区分:
 

struct udevice {
	const struct driver *driver;
	const char *name;
	void *platdata;
	void *parent_platdata;
	void *uclass_platdata;
	ofnode node;
	ulong driver_data;
	struct udevice *parent;
	void *priv;
	struct uclass *uclass;
	void *uclass_priv;
	void *parent_priv;
	struct list_head uclass_node;
	struct list_head child_head;
	struct list_head sibling_node;
	uint32_t flags;
	int req_seq;
	int seq;
#ifdef CONFIG_DEVRES
	struct list_head devres_head;
#endif
};

@driver: 设备使用的driver
@name: 设备名,一般是FDT node名
@platdata: 设备的配置信息
@parent_platdata:设备的父总线配置信息
@uclass_platdata:设备的uclass配置信息
@node:设备的设备树节点索引
@driver_data:通过该成员将设备和驱动进行匹配的入口参数
@parent: 设备的浮设备,若为顶级设备则为NULL
@priv:设备的私有数据
@uclass:设备的uclass指针
@uclass_priv: 设备的uclass私有数据
@parent_priv: 设备的父私有数据
@uclass_node: uclass通过该成员来连接到它的设备
@child_head: 设备的孩子链表
@sibling_node: 所有设备的列表中的下一个设备
@flags: 设备的flags,包括:DM_FLAG_...
@req_seq: 设备的请求序列号(-1 = any)
@req: 设备的申请的序列号(-1 = none)。当设备探测到时该成员被设置,且在设备的uclass内是独一无二的。
@devres_head: 和设备相关的内存申请列表。当CONFIG_DEVRES使能时,devm_kmalloc()申请的内存会添加到该列表中,以这种方式申请的内存,在设备removed或unbound时,会自动释放。
 

static int rockchip_gpio_probe(struct udevice *dev)
{
	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
	struct rockchip_gpio_priv *priv = dev_get_priv(dev);
	char *end;
	int ret;

	priv->regs = dev_read_addr_ptr(dev);
	ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl);
	if (ret)
		return ret;

	uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK;
	end = strrchr(dev->name, '@');
	priv->bank = trailing_strtoln(dev->name, end);
	priv->name[0] = 'A' + priv->bank;
	uc_priv->bank_name = priv->name;

	return 0;
}

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值