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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章