底软驱动 | U-boot驱动模型

U-boot驱动模型

1. 基本概念

1.1 全局数据global_data

某些情况下,u-boot在某些只读存储器上运行,如ROM,NorFlash等;在其重定位到RAM之前,无法写入数据或者通过全局变量传递数据,而global_data(也称GD)则可以解决这个问题

简单来说,u-boot把GD放在RAM区,使用它来存储全局数据, 以解决上述场景中无法使用全局变量的问题

GD数据结构

// include/asm-generic/global_data.h
typedef struct global_data {
    bd_t *bd;  // 保存开发板的相关参数
    unsigned long env_addr;     // 环境变量地址
    unsigned long ram_top;      // RAM空间的顶端地址
    unsigned long relocaddr;    // u-boot重定位后的地址
    phys_size_t ram_size;       // 物理ram的size
    unsigned long irq_sp;       // 中断的栈地址
    unsigned long start_addr_sp;    // stack地址
    unsigned long reloc_off;    // uboot的relocation的偏移
    struct global_data *new_gd; // 重定位后的GD结构体
    const void *fdt_blob;       // dtb地址
    void *new_fdt;              // 重定位后dtb地址
    unsigned long fdt_size;     // dtb的长度
    struct udevice *cur_serial_dev; // 当前使用串口设备
    ......
} gd_t;

初始化GD

// crt0.S
// 设置C运行时环境
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 设置栈顶SP指针,只是预设,并不是最终的栈顶地址
bic sp, sp, #7 // 8byte对齐
mov r0, sp  // SP放入r0
bl  board_init_f_alloc_reserve // 参数为r0,返回后,r0中存放的是GD的地址
mov sp, r0  // r0放入sp中
mov r9, r0  // r0放入r9中,即r0和r9存放的都是GD的地址
bl  board_init_f_init_reserve  // 对GD初始化,r0为参数

// 给GD分配空间,传入的是r0,即栈顶地址
ulong board_init_f_alloc_reserve(ulong top)
{
    // 自顶向下分配CONFIG_SYS_MALLOC_F_LEN大小内存, 用于在relocation前用于给malloc函数提供内存池(给堆用的)
    top -= CONFIG_SYS_MALLOC_F_LEN;

    // 继续向下分配sizeof(struct global_data)大小的内存给GD使用,向下16byte对齐
    top = rounddown(top-sizeof(struct global_data), 16);

    // 返回GD地址
    return top;
}

// 初始化GD分配的空间, 即清空global_data区域
// 传入的参数为GD的基地址
void board_init_f_init_reserve(ulong base)
{
    // 清零
    memset(gd_ptr, '\0', sizeof(*gd));

    // 获取了early malloc的内存池的地址(给堆使用)
    base += roundup(sizeof(struct global_data), 16);

    // 写入到gd->malloc_base中
    gd->malloc_base = base;

    // 获取early malloc的内存池的末尾地址
    base += CONFIG_SYS_MALLOC_F_LEN;
}

使用GD
根据上面可得GD的基地址存放在r9中,需要GD的时候,直接从r9寄存器中取的其地址即可

// arch/arm/include/asm/global_data.h
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r9")

// common/board_r.c
DECLARE_GLOBAL_DATA_PTR

static int initr_reloc(void)
{
    // 直接使用gd变量即可
    gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
}

1.2 驱动模型引入

在U-boot中引入驱动模型(driver model),为驱动的定义和范文接口提供统一的方法,提高驱动间的兼容性以及访问的标准性,u-boot中的驱动模型(DM)和kernel中的设备驱动模型类似,但是也有所区别

通过宏定义CONFIG_DM使能驱动模型,对应的实际驱动设备则需要通过使能CONFIG_DM_SERIAL来使能;后面以serial驱动为例

1.2.1 uclass/udevice/drivers三者之间的关联

uclass可以理解为具有相同属性的device对外操作的接口, 它与上层接口直接通讯,其驱动为uclass_driver,给上层提供接口
udevice对具体设备的抽象,对应的驱动是driver; driver负责和硬件通讯,为uclass提供实际的操作集
udevice如何和uclass绑定:udevice对应的driver_iduclass对应的uclass_driver_id是否匹配
hardware对应的driver绑定对应的udevice,udevice绑定uclassuclass有其对应的uclass_driver

uclass和udevice是动态生成的

  1. udevice在解析fdt中的设备的时候自动生成,然后udevice找到对应的driver
  2. driver中保存了uclass_id, 根据它找到uclass_driver_id
  3. uclass链表中查找对应的uclass是否已经生成,若没有生成,则动态生成
  4. 重点是解析设备树,生成udevice, 并找到对应的driver

u-boot驱动模型

1.2.2 全局数据GD中和DM相关部分

typedef struct global_data {
   // dts中的根节点,第一个创建的udevice
   struct udevice  *dm_root;

   // relocation之前的根设备
   struct udevice  *dm_root_f;

  // uclass的链表, 挂的是有udevice的uclass
   struct list_head uclass_root;  
} gd_t;

数据结构


2. uclass_id

每种uclass有对应的ID号,定义在其uclass_driver中,这个和ID号和其对应的udevice中的driver中的uclass_id一样

// include/dm/uclass-id.h
enum uclass_id {
    UCLASS_ROOT = 0,
    UCLASS_DEMO,
    UCLASS_CLK,     
    UCLASS_PINCTRL,    
    UCLASS_SERIAL,   
}

3. uclass

3.1 数据结构

struct uclass {
    // uclass的私有数据指针
    void *priv;  
    // 对应的uclass driver
    struct uclass_driver *uc_drv;
    // 链表头,挂接它所有相关联的udevice
    struct list_head dev_head;
    // 链表节点,用于把uclass连接到uclass_root链表上
    struct list_head sibling_node;
};

3.2 生成

有对应uclass driver并且下面挂有udevice的uclass才会被uboot自动生成

3.3 存放位置

所有生成的都会挂接到gd->uclass_root链表上

3.4 获取对应的API

// 根据uclass_id遍历gd->ulcass_root链表
int uclass_get(enum uclass_id key, struct uclass **ucp);

4. uclass_driver

4.1 数据结构

// include/dm/uclass.h,每个uclass都有对应的uclass_driver
struct uclass_driver {
    // 该uclass_driver的名字
    const char *name;
    // 对应的uclass id
    enum uclass_id id;

    /* 以下函数指针主要是调用时机的区别 */
    int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用
    int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用
    int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用
    int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用
    int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用
    int (*child_post_bind)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备被绑定到该udevice之后调用
    int (*child_pre_probe)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备进行probe之前调用
    int (*init)(struct uclass *class); // 安装该uclass的时候调用
    int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用
    int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据
    const void *ops; //操作集合
};

4.2 生成

  • 通过UCLASS_DRIVER定义uclass_driver
// 以serial-uclass为例
UCLASS_DRIVER(serial) = {
    .id        = UCLASS_SERIAL,
    .name        = "serial",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,   
    .post_probe    = serial_post_probe,
    .pre_remove    = serial_pre_remove,
    .per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};
  • 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)))
  • 最终会得到如下结构体,并且存放在.u_boot_list_2_uclass_2_serial段中
struct uclass_driver  _u_boot_list_2_uclass_2_serial = {
    .id        = UCLASS_SERIAL,   // 设置对应的uclass id
    .name        = "serial",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,   
    .post_probe    = serial_post_probe,
    .pre_remove    = serial_pre_remove,
    .per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
}

4.3 存放位置

通过查看u-boot.map可以得到:

.u_boot_list_2_uclass_1
                0x23e368e0        0x0 drivers/built-in.o
.u_boot_list_2_uclass_2_gpio
                0x23e368e0       0x48 drivers/gpio/built-in.o
                0x23e368e0                _u_boot_list_2_uclass_2_gpio  // gpio uclass driver的符号
.u_boot_list_2_uclass_2_root
                0x23e36928       0x48 drivers/built-in.o
                0x23e36928                _u_boot_list_2_uclass_2_root // root uclass drvier的符号
.u_boot_list_2_uclass_2_serial
                0x23e36970       0x48 drivers/serial/built-in.o
                0x23e36970                _u_boot_list_2_uclass_2_serial // serial uclass driver的符号
.u_boot_list_2_uclass_2_simple_bus
                0x23e369b8       0x48 drivers/built-in.o
                0x23e369b8                _u_boot_list_2_uclass_2_simple_bus
.u_boot_list_2_uclass_3
                0x23e36a00        0x0 drivers/built-in.o
                0x23e36a00                . = ALIGN (0x4)
  • 即所有的uclass driver都会放在.u_boot_list_2_uclass_1和.u_boot_list_2_uclass_3的区间中,这个列表也称为uclass_driver table**

4.4 获取对应的API

  • 先获取uclass_driver table,然后遍历获得对应的uclass_driver
// 根据.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);

// 根据uclass_id从uclass_driver table中得到相应的uclass_driver
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)

5. udevice

5.1 数据结构

// include/dm/device.h, 描述的是设备树内容
struct udevice {
    const struct driver *driver; // 该udevice对应的driver
    const char *name; // 设备名
    void *platdata; // 该udevice的平台数据
    void *parent_platdata; // 提供给父设备使用的平台数据
    void *uclass_platdata; // 提供给所属uclass使用的平台数据
    int of_offset; // 该udevice的dtb节点偏移,代表了dtb里面的这个节点node
    ulong driver_data; // 驱动数据
    struct udevice *parent; // 父设备
    void *priv; // 私有数据的指针
    struct uclass *uclass; // 所属uclass
    void *uclass_priv; // 提供给所属uclass使用的私有数据指针
    void *parent_priv; // 提供给其父设备使用的私有数据指针
    struct list_head uclass_node; // 挂接到它所属uclass的链表上
    struct list_head child_head; // 链表头,挂接它下面的子设备
    struct list_head sibling_node; // 挂接到父设备的链表上
    uint32_t flags; // 标识
    int req_seq;  // 请求的seq
    int seq;     // 分配的seq
};

5.2 生成

uboot解析dtb以后动态生成

5.3 存放位置

  1. 挂接到对应的uclass上,即挂接到uclass->dev_head
  2. 挂接到父设备的子设备链表中, 即挂接到udevice->child_head, 最上层的父设备是gd->dm_root

5.4 获取对应的API

  • 通过uclass获取对应的udevice

不应该是dtb解析获得的吗?的确是通过dtb解析获得

int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udevice
int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp); // 通过设备名从uclass中获取udevice
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
                   struct udevice **devp);
int uclass_resolve_seq(struct udevice *dev);

6. driver

6.1 数据结构

// include/dm/device.h
struct driver {
    char *name;    // 驱动名
    enum uclass_id id;  // 对应的uclass id
    const struct udevice_id *of_match;    // compatible字符串的匹配表,用于和device tree里面的设备节点匹配
    int (*bind)(struct udevice *dev);   // 用于绑定目标设备到该driver中
    int (*probe)(struct udevice *dev);   // 用于probe目标设备激活
    int (*remove)(struct udevice *dev); // 用于remove目标设备禁用
    int (*unbind)(struct udevice *dev); // 用于解绑目标设备到该driver中
    int (*ofdata_to_platdata)(struct udevice *dev); // 在probe之前,解析对应udevice的dts节点,转化成udevice的平台数据
    int (*child_post_bind)(struct udevice *dev); // 如果目标设备的一个子设备被绑定之后调用
    int (*child_pre_probe)(struct udevice *dev); // 在目标设备的一个子设备被probe之前调用
    int (*child_post_remove)(struct udevice *dev); // 在目标设备的一个子设备被remove之后调用
    int priv_auto_alloc_size; //需要分配多少空间作为其udevice的私有数据
    int platdata_auto_alloc_size; //需要分配多少空间作为其udevice的平台数据
    int per_child_auto_alloc_size;  // 对于目标设备的每个子设备需要分配多少空间作为父设备的私有数据
    int per_child_platdata_auto_alloc_size; // 对于目标设备的每个子设备需要分配多少空间作为父设备的平台数据
    const void *ops;    /* driver-specific operations */ // 操作集合的指针,提供给uclass使用,没有规定操作集的格式,由具体uclass决定
};

6.2 生成

  • 通过U_BOOT_DRIVER定义一个driver
// 以serial为例
U_BOOT_DRIVER(serial_s5p) = {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,
    // 定义为设备树中的compatible属性
    .of_match = s5p_serial_ids,
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};
  • U_BOOT_DRIVER宏定义如下,生成的结构体保存在.u_boot_list_2_driver_2_serial_s5p段中**
// 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)))

// 生成结构体
struct driver _u_boot_list_2_driver_2_serial_s5p= {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,
    .of_match = s5p_serial_ids,
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};

6.3 存放位置

通过查看u-boot.map可以得到:

 .u_boot_list_2_driver_1
                0x00000000178743a0        0x0 drivers/built-in.o
 .u_boot_list_2_driver_2_gpio_mxc
                0x00000000178744b0       0x44 drivers/gpio/built-in.o
                0x00000000178744b0                _u_boot_list_2_driver_2_gpio_mxc
 .u_boot_list_2_driver_2_gpio_regulator
                0x00000000178744f4       0x44 drivers/power/regulator/built-in.o
                0x00000000178744f4                _u_boot_list_2_driver_2_gpio_regulator
 .u_boot_list_2_driver_2_i2c_generic_chip_drv
                0x0000000017874538       0x44 drivers/i2c/built-in.o
                0x0000000017874538                _u_boot_list_2_driver_2_i2c_generic_chip_drv
 .u_boot_list_2_driver_2_i2c_mxc
                0x000000001787457c       0x44 drivers/i2c/built-in.o
                0x000000001787457c                _u_boot_list_2_driver_2_i2c_mxc
 .u_boot_list_2_driver_2_imx6_pinctrl
                0x00000000178745c0       0x44 drivers/built-in.o
                0x00000000178745c0                _u_boot_list_2_driver_2_imx6_pinctrl
......
 .u_boot_list_2_driver_3
                0x00000000178748f0        0x0 drivers/built-in.o
  • 即所有driver都会放在.u_boot_list_2_driver_1和.u_boot_list_2_driver_3的区间中,这个列表也称为driver table

6.4 获取对应的API

  • 先获取driver table,然后遍历获得对应的driver
// 根据.u_boot_list_2_driver_1的段地址获得driver table的地址
struct driver *drv =
    ll_entry_start(struct driver, driver);

// 获得driver table的长度
const int n_ents = ll_entry_count(struct driver, driver);

// 根据driver name从driver table中得到相应的driver
struct driver *lists_driver_lookup_name(const char *name)

7. 相关API

7.1 uclass

// 从gd->uclass_root链表获取对应的uclass
// uclass是从gd->uclass_root链表中获得, 参数有uclass_id
int uclass_get(enum uclass_id key, struct uclass **ucp);

7.2 uclass_driver

// 根据uclass id从uclass_driver table中获取
// uclass_driver是从uclass_driver 中获得,参数是uclass_id
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)

7.3 udevice

#define uclass_foreach_dev(pos, uc) \
    list_for_each_entry(pos, &uc->dev_head, uclass_node)

#define uclass_foreach_dev_safe(pos, next, uc)  \
    list_for_each_entry_safe(pos, next, &uc->dev_head, uclass_node)

// 通过name获取driver,调用device_bind对udevice初始化,与对应uclass、driver绑定
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
                const struct driver_info *info, struct udevice **devp)

// 初始化udevice,与对应uclass、driver绑定
int device_bind(struct udevice *parent, const struct driver *drv,
        const char *name, void *platdata, int of_offset,
        struct udevice **devp)

// 在uclass的设备链表中绑定新的udevice
int uclass_bind_device(struct udevice *dev)
{
    uc = dev->uclass;
    list_add_tail(&dev->uclass_node, &uc->dev_head);
}

// 通过索引从uclass中获取udevice,注意,在获取的过程中就会对设备进行probe
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);
// 通过设备名从uclass中获取udevice
int uclass_get_device_by_name(enum uclass_id id, const char *name,
                  struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
                   struct udevice **devp);
......
int uclass_resolve_seq(struct udevice *dev);

7.4 driver

// 根据name从driver table中获取
struct driver *lists_driver_lookup_name(const char *name)

流程

// 根据驱动跑一遍

1. DM的初始化

1.1 初始化DM

  • 创建根设备root的udevice,放在gd->dm_root中

根设备其实是一个虚拟设备,主要为其他设备提供一个挂载点

  • 初始化uclass链表gd->uclass_root
#define DM_UCLASS_ROOT_NON_CONST	(gd->uclass_root)
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); // 初始化uclass

#define DM_ROOT_NON_CONST		(gd->dm_root) dm_root是struct udevice*类型

1.2 udevice和uclass的解析

  1. 创建udevice和uclass
  2. 绑定udevice和uclass
  3. 绑定udevice和driver
  4. 绑定uclass和uclass_driver
  5. 调用部分的driver
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); // dm的根udevice

//
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
			const struct driver_info *info, struct udevice **devp)

/* This is the root driver - all drivers are children of this */
U_BOOT_DRIVER(root_driver) = {
	.name	= "root_driver",
	.id	= UCLASS_ROOT,
	.priv_auto_alloc_size = sizeof(struct root_priv),
};

/* This is the root uclass */
UCLASS_DRIVER(root) = {
	.name	= "root",
	.id	= UCLASS_ROOT,
};


2. 入口说明

dts节点中的u-boot,dm-pre-reloc属性,当设置了这个属性时,则表示这个设备在relocate之前就需要使用

// 只对带有“u-boot,dm-pre-reloc”属性的节点进行解析,情况少
dm_init_and_scan(true);

// 对所有节点进行解析,重点说明
dm_init_and_scan(false);

2.1 dm_init_and_scan说明

initf_dm

// driver/core/root.c
int dm_init_and_scan(bool pre_reloc_only)
{
    ret = dm_init();    // DM的初始化

    ret = dm_scan_platdata(pre_reloc_only);  // 从平台设备中解析udevice和uclass

    if (CONFIG_IS_ENABLED(OF_CONTROL)) {
        ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); // 从dtb中解析udevice和uclass
    }

    ret = dm_scan_other(pre_reloc_only);
}

2.2 dm_init

  • 创建根设备root的udevice,放在gd->dm_root中, 即创建设备链表头

  • 初始化uclass链表gd->uclass_root, 即创建uclass链表头

  • driver数组,从u-boot.map中查找

/**
 * ll_entry_start() - Point to first entry of linker-generated array
 * @_type:	Data type of the entry
 * @_list:	Name of the list in which this entry is placed
 *
 * This function returns (_type *) pointer to the very first entry of a
 * linker-generated array placed into subsection of .u_boot_list section
 * specified by _list argument.
 *
 * Since this macro defines an array start symbol, its leftmost index
 * must be 2 and its rightmost index must be 1.
 *
 * Example:
 * struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub);
 */
#define ll_entry_start(_type, _list)
({ static char start[0] __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_1"))); (struct driver *)&start;})

ll_entry_start(struct driver, driver)

dm_init

// driver/core/root.c
#define DM_ROOT_NON_CONST       (((gd_t *)gd)->dm_root) // 宏定义根设备指针gd->dm_root
#define DM_UCLASS_ROOT_NON_CONST    (((gd_t *)gd)->uclass_root) // 宏定义gd->uclass_root,uclass的链表

// root_info就实现了一个.name属性,其他为默认值
static const struct driver_info root_info = {
	.name		= "root_driver",
};

int dm_init(void)
{
    // 初始化uclass链表
    INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);

    // DM_ROOT_NON_CONST是指根设备udevice,root_info是表示根设备的设备信息
    // 查找和设备信息匹配的driver,创建对应的udevice,和对应uclass绑定,并把udevice放在DM_ROOT_NON_CONST链表中
    ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);

    // 对根设备执行probe操作
    ret = device_probe(DM_ROOT_NON_CONST);
}

// 1. 根据driver的name找到driver,其中名字为"root_driver"
int device_bind_by_name(struct udevice *parent(NULL), bool pre_reloc_only(false),
			const struct driver_info *info("root_info"), struct udevice **devp(gd->uclass_root))
{
	drv = lists_driver_lookup_name(info->name);  
	return device_bind_common(parent, drv, info->name,
			(void *)info->platdata, 0, -1, platdata_size, devp);
}

// 2. 查询driver列表,找到root_driver,并且返回root_driver的地址
struct driver *lists_driver_lookup_name(const char *name)
{
    // 在u-boot.map文件中找到driver列表的起始地址为.u_boot_list_2_driver_1
	struct driver *drv =
		ll_entry_start(struct driver, driver);

 .u_boot_list_2_driver_1
                0x00000000178743a0        0x0 drivers/built-in.o
 .u_boot_list_2_driver_2_asix_eth
                0x00000000178743a0       0x44 drivers/usb/eth/built-in.o
                0x00000000178743a0                _u_boot_list_2_driver_2_asix_eth
 .u_boot_list_2_driver_2_fecmxc_gem
                0x00000000178743e4       0x44 drivers/net/built-in.o
                0x00000000178743e4                _u_boot_list_2_driver_2_fecmxc_gem
 .u_boot_list_2_driver_2_fixed_regulator
                0x0000000017874428       0x44 drivers/power/regulator/built-in.o
                0x0000000017874428                _u_boot_list_2_driver_2_fixed_regulator
                .......................

    // 找到driver列表的end地址
    .u_boot_list_2_driver_3


    // 计算出一共有对个驱动
	const int n_ents = ll_entry_count(struct driver, driver);
	struct driver *entry;

	for (entry = drv; entry != drv + n_ents; entry++) {
		if (!strcmp(name, entry->name))
			return entry;
	}
}

**********************************

/* This is the root driver - all drivers are children of this */
U_BOOT_DRIVER(root_driver) = {
	.name	= "root_driver",
	.id	= UCLASS_ROOT, 0
	.priv_auto_alloc_size = sizeof(struct root_priv),
};
**********************************
//  3. 给定udevice parent, 驱动地址,驱动名字,平台数据,驱动数据,偏移,偏移大小和udevice,返回一个int类型
static int device_bind_common(struct udevice *parent, const struct driver *drv,
			      const char *name, void *platdata,
			      ulong driver_data, int of_offset,
			      uint of_platdata_size, struct udevice **devp)
{
    // 根据driver中保存的id找uclass,如果不存在就创建一个uclass
    ret = uclass_get(drv->id, &uc);
*********************************************************

int uclass_get(enum uclass_id id, struct uclass **ucp)
{
	struct uclass *uc;

	*ucp = NULL;
	uc = uclass_find(id);  // 开始是空的,返回0
	if (!uc)
		return uclass_add(id, ucp);
	*ucp = uc;

	return 0;
}



/* This is the root uclass */
UCLASS_DRIVER(root) = {
	.name	= "root",
	.id	= UCLASS_ROOT,
};


// 在uclass driver中找对应的uclass driver
// 根据uclass driver中id和driver中的id对比,如果相等,则返回uclass_driver的起始地址
// 申请1个uclass的内存,这个uc的uc_driver就是刚才返回的uclass_driver的起始地址
//	INIT_LIST_HEAD(&uc->sibling_node);  初始化这uc的两个节点为单头结点,指向它自己
//	INIT_LIST_HEAD(&uc->dev_head);
// 把uc->sibling_node这个节点插入到gd->uclass_root后面
// 直接返回这个uclass

.u_boot_list_2_uclass_1

.u_boot_list_2_uclass_2_root


.u_boot_list_2_uclass_3
*********************************************************


    // 创建一个udevice,和uclass关联
    dev->uclass = uc;
    ......

    // 给udevice一个req_seq
    fdtdec_get_alias_seq(gd->fdt_blob,
        uc->uc_drv->name, of_offset,
        &dev->req_seq)

    // 把udevice链接到uclass中,检查是否需要执行uclass_driver的操作 	
    // 把udevice的uclass_node链接到uclass的dev_head上去
    ret = uclass_bind_device(dev);

    // driver和udevice进行绑定, 函数指针,由具体设备指定
    // 查看driver是否有需要绑定udevice的需求
    ret = drv->bind(dev);
}

// 绑定后激活
int device_probe(struct udevice *dev)
{
    // 根据uclass_id和req_seq,得到dev->seq
    seq = uclass_resolve_seq(dev);

    // probe前操作
    ret = uclass_pre_probe_device(dev);

    // **************************
    // 函数指针,对应实际的udevice的driver
    ret = dev->parent->driver->child_pre_probe(dev);
    ret = drv->ofdata_to_platdata(dev);
    ret = drv->probe(dev);
    // **************************

    // probe后操作
    ret = uclass_post_probe_device(dev);
}

2.3 从平台设备中解析udevice和uclass——dm_scan_platdata

TBD

dm_scan_platdata

2.4 dm_scan_fdt: 从dtb中解析udevice和uclass

dm_scan_fdt

// 以dtb基地址为参数
int dm_scan_fdt(const void *blob, bool pre_reloc_only)
{
    return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
}

// parent=gd->dm_root,表示以root设备作为父设备开始解析
// blob=gd->fdt_blob,对应dtb入口地址
// offset=0,从偏移0的节点开始扫描
int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
             bool pre_reloc_only)
{
    // 遍历每一个dts节点并且调用lists_bind_fdt对其进行解析
    // 获得blob设备树的offset偏移下的节点的第一个子节点
    for (offset = fdt_first_subnode(blob, offset);
        offset > 0;
        // 循环查找下一个子节点
        offset = fdt_next_subnode(blob, offset)) {
        // 节点状态disable的直接忽略
        if (!fdtdec_get_is_enabled(blob, offset))
        // 解析绑定这个节点,dm_scan_fdt的核心
        err = lists_bind_fdt(parent, blob, offset, NULL);
}

// driver/core/lists.c
// 通过blob和offset可以获得对应的设备的dts节点
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
           struct udevice **devp)
{
    // 获取driver table地址
    struct driver *driver = ll_entry_start(struct driver, driver);
    // 获取driver table长度
    const int n_ents = ll_entry_count(struct driver, driver);

    // 遍历driver table中的所有driver
    for (entry = driver; entry != driver + n_ents; entry++) {
        // 判断driver中的compatibile字段和dts节点是否匹配
        ret = driver_check_compatible(blob, offset, entry->of_match,
                          &id);

        // 获取节点名称
        name = fdt_get_name(blob, offset, NULL);

        // 找到对应driver,创建对应udevice和uclass进行绑定
        ret = device_bind_with_driver_data(parent, entry, name,
						   id->data, offset, &dev);
    }
}

int device_bind_with_driver_data(struct udevice *parent,
				 const struct driver *drv, const char *name,
				 ulong driver_data, int of_offset,
				 struct udevice **devp)
{
    // 同上
    return device_bind_common(parent, drv, name, NULL, driver_data,
				  of_offset, 0, devp);
}

int device_bind(struct udevice *parent, const struct driver *drv,
        const char *name, void *platdata, int of_offset,
        struct udevice **devp)
{
    // 同上
    return device_bind_common(parent, drv, name, platdata, 0, of_offset, 0,
				  devp);
}

以上完成dtb的解析,udevice和uclass的创建,以及各个组成部分的绑定
注意:这里只是绑定,即调用了driver的bind函数,但是设备还没有真正激活,也就是还没有执行设备的probe函数

TBD,差一个流程图


3. DM的probe

3.1 device_probe函数

  1. 分配设备的私有数据
  2. 对父设备probe,执行probe device之前uclass需要调用的一些函数
  3. 调用driver的ofdata_to_platdata,将dts信息转化为设备的平台数据
  4. 调用driver的probe函数,执行probe device之后uclass需要调用的一些函数

device_probe

// driver/core/device.c
int device_probe(struct udevice *dev)
{
    // 同上
}


**************************************************************
ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq,
					false, &dup); // dup是个空设备,要修改的



**************************************************************

3.2 通过uclass获取一个udevice并且probe

// driver/core/uclass.c
//通过索引从uclass的设备链表中获取udevice,进行probe
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)

//通过设备名从uclass的设备链表中获取udevice,进行probe
int uclass_get_device_by_name(enum uclass_id id, const char *name,
                  struct udevice **devp)
//通过序号从uclass的设备链表中获取udevice,进行probe                  
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)

//通过dts节点的偏移从uclass的设备链表中获取udevice,进行probe
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
                   struct udevice **devp)

//通过设备的“phandle”属性从uclass的设备链表中获取udevice,进行probe                   
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
                 const char *name, struct udevice **devp)

//从uclass的设备链表中获取第一个udevice,进行probe                 
int uclass_first_device(enum uclass_id id, struct udevice **devp)

//从uclass的设备链表中获取下一个udevice,进行probe
int uclass_next_device(struct udevice **devp)

以上接口主要是获取设备的方法上有所区别,但是probe设备的方法都是一样的

3.3 以uclass_get_device为例

int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
{
    //通过索引从uclass的设备链表中获取对应的udevice
    ret = uclass_find_device(id, index, &dev);

    // 获得device,进行probe
    return uclass_get_device_tail(dev, ret, devp);
}

int uclass_get_device_tail(struct udevice *dev, int ret,
                  struct udevice **devp)
{
    // probe设备
    ret = device_probe(dev);
}

实例


1. serial驱动

1.1 定义uclass_driver

UCLASS_DRIVER(serial) = {
	.id		= UCLASS_SERIAL,  //设置对应的uclass id,并且存放在.u_boot_list_2_uclass_2_serial段中
	.name		= "serial",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
	.post_probe	= serial_post_probe,
	.pre_remove	= serial_pre_remove,
	.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};

1.2 dts文件中serial节点

				serial@02020000 {
					compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
					reg = <0x2020000 0x4000>;
					interrupts = <0x0 0x1a 0x4>;
					clocks = <0x3 0xa0 0x3 0xa1>;
					clock-names = "ipg", "per";
					dmas = <0x8 0x19 0x4 0x0 0x8 0x1a 0x4 0x0>;
					dma-names = "rx", "tx";
					status = "okay";
					pinctrl-names = "default";
					pinctrl-0 = <0xc>;
				};

1.3 定义设备驱动

U_BOOT_DRIVER(serial_s5p) = {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,       // 这里的uclass_id和 uclass_driver中的一致
    .of_match = s5p_serial_ids,   // 设备树种的compatible属性
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};

static const struct udevice_id s5p_serial_ids[] = {
    { .compatible = "samsung,exynos4210-uart" },    //注意这里的compatible要和设备树中的一致
    { }
};

1.4 udevice和对应uclass的创建

dm_scan_fdt

fdtdec_get_chosen_node ------>  fdtdec_get_chosen_prop

	chosen { // chosen节点也位于根节点下,该节点用来给内核传递参数(不代表实际硬件)
            // 从串口输出
		stdout-path = "/soc/aips-bus@02000000/spba-bus@02000000/serial@02020000";
	};   

    

1.5 udevice和对应uclass的绑定

核心解析函数lists_bind_fdt

1.6 对应udevice的probe

int serial_init(void)
{
    // console的serial的初始化
    serial_find_console_or_panic();
    gd->flags |= GD_FLG_SERIAL_READY;
}

static void serial_find_console_or_panic(void)
{

    if (CONFIG_IS_ENABLED(OF_CONTROL) && blob) {

        // 获取指定的serial的dts节点

        // 调用uclass_get_device_by_of_offset,通过dts节点的偏移从uclass的设备链表中获取udevice,并且进行probe。
        if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node,
                            &dev)) {
            // 将udevice存储在gd->cur_serial_dev,后续uclass中可以直接通过gd->cur_serial_dev获取到对应的设备并且进行操作
            // 但是注意,这种并不是通用做法!!!
            gd->cur_serial_dev = dev;
            return;
        }
    }
}

1.7 uclass的接口调用

void serial_putc(char ch)
{
    if (gd->cur_serial_dev)
        // 将console对应的serial的udevice作为参数传入
        _serial_putc(gd->cur_serial_dev, ch);
}

static void _serial_putc(struct udevice *dev, char ch)
{
    // 获取设备对应的driver函数的ops操作集
    struct dm_serial_ops *ops = serial_get_ops(dev);
    int err;

    // 以udevice为参数,调用ops中对应的操作函数putc
    do {
        err = ops->putc(dev, ch);
    } while (err == -EAGAIN);
}


2. gpio驱动

2.1 dm-gpio架构

  • gpio的驱动模型架构如图所示:

gpio驱动模型架构

2.1.1 gpio core

a) 为上层提供接口
b) 从dts中获取GPIO属性
c) 从gpio uclass的设备链表中获取对应udevice设备,并使用其操作集

2.1.2 gpio uclass

a) 链接属于该uclass的所有udevice
b) 为udevice的driver提供统一操作集接口

2.1.3 bank和udevice
  • a) 在某些平台,把使用同一组寄存器的GPIO构成一个bank,但是不是所有的都有bank的概念,如果每个GPIO都有自己独立的寄存器,则可看成只有一个bank
  • b) 一个bank对应一个udevice,用bank中的偏移表示具体GPIO号
  • c) udevice的driver根据bank和offset来操作对应寄存器的bit

2.2 基本原理

  • a) 一个bank对应一个udevice,udevice中私有数据中存放着该bank的信息,比如相应寄存器地址等等
  • b) 上层通过调用gpio core的接口从dtsi获取到GPIO属性对应的gpio_desc描述符,用此描述符描述一个GPIO,它包括该GPIO所属的udevice、在bank内的偏移、以及标志位等
  • c) 上层使用gpio_desc描述符来作为调用gpio core的操作接口的参数
  • d) gpio core从gpio_desc描述符提取udevice,并调用其driver中对应的操作集,以bank内的偏移作为其参数(这样driver就能判断出是哪个GPIO了)
  • e) driver中提取udevice的私有数据中的bank信息,并进行相应的操作
  • f)

2.3 driver

2.4 uclass_driver

2.5 uclass

2.6 udevice

2.7 绑定流程

driver为uclass提供操作集,其中保存了uclass_id

根据uclass_id查询uclass driver列表获得uclass
udevice会放在两个地方,一个是uclass对应的udevice链表
一个是Device链表中

driver中有compatibile字段,通过检查它和dts节点中的compatible字段是否相同,获取节点名称,获取driver,生成udevice,uclass,并进行绑定

// 以serial为例, driver
U_BOOT_DRIVER(serial_s5p) = {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,
    .of_match = s5p_serial_ids,
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};

// 以serial-uclass为例,uclass driver
UCLASS_DRIVER(serial) = {
    .id        = UCLASS_SERIAL,
    .name        = "serial",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,   
    .post_probe    = serial_post_probe,
    .pre_remove    = serial_pre_remove,
    .per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};

if(!a): a是假的执行
if(a): a是真的执行

原文链接

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TrustZone_Hcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值