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