新版u-boot都支持设备树,和linux一样,u-boot这里也建立了一个驱动模型。比如, of_match来匹配,probe来识别等。
u-boot里面的驱动主要使用宏:
/* 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宏都会指向u_boot_list_2_xx段,展开后,相当于定义了一个struct driver类型的变量
对于struct driver结构体来看:
struct driver {
char *name;
enum uclass_id id;
const struct udevice_id *of_match;
int (*bind)(struct udevice *dev);
int (*probe)(struct udevice *dev);
int (*remove)(struct udevice *dev);
int (*unbind)(struct udevice *dev);
int (*ofdata_to_platdata)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*child_post_remove)(struct udevice *dev);
int priv_auto_alloc_size;
int platdata_auto_alloc_size;
int per_child_auto_alloc_size;
int per_child_platdata_auto_alloc_size;
const void *ops; /* driver-specific operations */
uint32_t flags;
};
看起来和linux确实很像。
再回来看链接脚本
arch/arm/cpu/u-boot.lds
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
再看一个实例, exynos4412里面的eMMC
static const struct udevice_id exynos_dwmmc_ids[] = {
{ .compatible = "samsung,exynos4412-dw-mshc" },
{ }
};
U_BOOT_DRIVER(exynos_dwmmc_drv) = {
.name = "exynos_dwmmc",
.id = UCLASS_MMC,
.of_match = exynos_dwmmc_ids,
.bind = exynos_dwmmc_bind,
.ops = &dm_dwmci_ops,
.probe = exynos_dwmmc_probe,
.priv_auto_alloc_size = sizeof(struct dwmci_exynos_priv_data),
.platdata_auto_alloc_size = sizeof(struct exynos_mmc_plat),
};
驱动以{ .compatible = "samsung,exynos4412-dw-mshc" },来匹配的,再看下设备树里面
dwmmc@12550000 {
compatible = "samsung,exynos4412-dw-mshc";
samsung,bus-width = <8>;
samsung,timing = <2 1 0>;
samsung,removable = <0>;
fifoth_val = <0x203f0040>;
bus_hz = <400000000>;
div = <0x3>;
index = <4>;
};
设备驱动模型调用流程是怎么样的呢?
init_sequence_f[] // comman/board_f.c
initf_dm
dm_init_and_scan
dm_init
dm_scan_platdata
dm_scan_fdt
dm_scan_other
dm_timer_init
再dm_init
int dm_init(void)
{
int ret;
if (gd->dm_root) {
dm_warn("Virtual root driver already exists!\n");
return -EINVAL;
}
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
if (ret)
return ret;
ret = device_probe(DM_ROOT_NON_CONST);
if (ret)
return ret;
return 0;
}
device_bind_by_name里面根据名字绑定
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
const struct driver_info *info, struct udevice **devp)
{
lists_driver_lookup_name(info->name);
。。。
return device_bind_common(parent, drv, info->name,
其中device_bind_common是核心,其与uclass建立关系
里面调用uclass_bind_device
在初始化里面主要调用dm_scan_platdata来解析设备树信息并保存
int dm_scan_platdata(bool pre_reloc_only)
{
int ret;
ret = lists_bind_drivers(DM_ROOT_NON_CONST, pre_reloc_only);
if (ret == -ENOENT) {
dm_warn("Some drivers were not found\n");
ret = 0;
}
return ret;
}
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
{
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++) { // 扫描
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;
}
然后再调用dm_scan_fdt_node分解子节点。