uboot的驅動模型,簡稱dm, 具體細節建議參考./doc/driver-model/README.txt 關於dm的三個概念: uboot的驅動模型,簡稱dm, 具體細節建議參考./doc/driver-model/README.txt 關於dm的三個概念: uclass:一組同類型的devic ...
uboot的驅動模型,簡稱dm, 具體細節建議參考./doc/driver-model/README.txt 關於dm的三個概念: uclass:一組同類型的devices,uclass為同一個group的device,提供一個相同的介面。比如:I2C、GPIO等 driver:上層的介面,英文原文解釋是“some code which talks to a peripheral and presents a higher-level interface to it.” device:driver的一個實例,綁定到一個具體的埠或者外設。(driver和device是不是可以類比於程式與進程,進程是程式的一個實例) 每一類uclass,需要在代碼中用下麵的方式來定義,以spi-uclass為例:
UCLASS_DRIVER(spi) = { .id = UCLASS_SPI, .name = "spi", .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = spi_post_bind, .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), .child_post_bind = spi_child_post_bind, };
通過對巨集定義UCLASS_DRIVER的展開
/* Declare a new uclass_driver */ #define UCLASS_DRIVER(__name) \ ll_entry_declare(struct uclass_driver, __name, uclass) #define ll_entry_declare(_type, _name, _list) \ _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ __attribute__((unused, \ section(".u_boot_list_2_"#_list"_2_"#_name)))這樣我們就能得到一個結構體, struct uclass_driver _u_boot_list_2_uclass_2_spi 並且存在 .u_boot_list_2_uclass_2_spi段。 但是我們如何通過ID UCLASS_SPI來找到對應的uclass結構體呢?
struct uclass_driver *lists_uclass_lookup(enum uclass_id id) { // 會根據.u_boot_list_2_uclass_1的段地址來得到uclass_driver table的地址 struct uclass_driver *uclass = ll_entry_start(struct uclass_driver, uclass); // 獲得uclass_driver table的長度 const int n_ents = ll_entry_count(struct uclass_driver, uclass); struct uclass_driver *entry; for (entry = uclass; entry != uclass + n_ents; entry++) { if (entry->id == id) return entry; } return NULL; }可以通過函數lists_uclass_lookup(enum uclass_id id)來查找。 另外,driver也是類似
/* Declare a new U-Boot driver */ #define U_BOOT_DRIVER(__name) \ ll_entry_declare(struct driver, __name, driver) #define ll_entry_declare(_type, _name, _list) \ _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ __attribute__((unused, \ section(".u_boot_list_2_"#_list"_2_"#_name))) U_BOOT_DRIVER(tegra114_spi) = { .name = "tegra114_spi", .id = UCLASS_SPI, .of_match = tegra114_spi_ids, .ops = &tegra114_spi_ops, .ofdata_to_platdata = tegra114_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra114_spi_priv), .probe = tegra114_spi_probe, };這樣我們就能得到一個結構體,
ll_entry_declare(struct driver, tegra114_spi, driver) struct driver _u_boot_list_2_driver_2_tegra114_spi __aligned(4) \ __attribute__((unused, \ section(".u_boot_list_2_driver_2_tegra114_spi")))
存儲在段,.u_boot_list_2_driver_2_tegra114_spi 但是這些段,在uboot實際載入的時候,又是如何載入到鏈表中去的呢! 首先,還是初始化列表init_sequence_f里的函數initf_dm
static int initf_dm(void) { #if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN) int ret; ret = dm_init_and_scan(true); if (ret) return ret; #endif #ifdef CONFIG_TIMER_EARLY ret = dm_timer_init(); if (ret) return ret; #endif return 0; }dm_init_and_scan(),代碼分析如下
int dm_init_and_scan(bool pre_reloc_only) { int ret; /*創建udevice和uclass空鏈表,創建根設備(root device)*/ ret = dm_init(); if (ret) { debug("dm_init() failed: %d\n", ret); return ret; } /*掃描U_BOOT_DEVICE定義的設備,與U_BOOT_DRIVER定義的driver進行查找,並綁定相應driver*/ ret = dm_scan_platdata(pre_reloc_only); if (ret) { debug("dm_scan_platdata() failed: %d\n", ret); return ret; } if (CONFIG_IS_ENABLED(OF_CONTROL)) { /*掃描由FDT設備樹文件定義的設備,與U_BOOT_DRIVER定義的driver進行查找,並綁定相應driver*/ ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); if (ret) { debug("dm_scan_fdt() failed: %d\n", ret); return ret; } } ret = dm_scan_other(pre_reloc_only); if (ret) return ret; return 0; }分三個部分: dm_init():創建udevice和uclass空鏈表,創建根設備(root device) dm_scan_platdata():調用函數lists_bind_drivers,掃描U_BOOT_DEVICE定義的設備,與U_BOOT_DRIVER定義的driver進行查找,創建udevice,並綁定相應driver。 dm_scan_fdt():掃描由FDT設備樹文件定義的設備,與U_BOOT_DRIVER定義的driver進行查找,創建udevice,並綁定相應driver。
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) { /*從分段,.u_boot_list_2_driver_info中來查找*/ struct driver_info *info = ll_entry_start(struct driver_info, driver_info); const int n_ents = ll_entry_count(struct driver_info, driver_info); struct driver_info *entry; struct udevice *dev; int result = 0; int ret; for (entry = info; entry != info + n_ents; entry++) { /*將driver_info列表裡面的name,依次與driver列表裡面的名字,進行匹配查找,然後進行綁定*/ ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev); if (ret && ret != -EPERM) { dm_warn("No match for driver '%s'\n", entry->name); if (!result || ret != -ENOENT) result = ret; } } return result; } int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) { struct driver *drv; /*從driver list中查找info的名字*/ drv = lists_driver_lookup_name(info->name); if (!drv) return -ENOENT; if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return -EPERM; /*創建udevice,綁定*/ return device_bind(parent, drv, info->name, (void *)info->platdata, -1, devp); }U_BOOT_DEVICE的巨集定義,註意與U_BOOT_DRIVER的區別:
#define U_BOOT_DEVICE(__name) \ ll_entry_declare(struct driver_info, __name, driver_info)