uboot - 驱动开发 - 驱动模型

说明

  • 类似于linux,为了规范、统一驱动适配和驱动接口调用,uboot定义了一套驱动模型(Driver Model),简称DM。
  • 本文基于:u-boot-2021.10。

优点

  1. 为同一类ip的驱动定义了统一的操作接口,DM在软件层面做了一定的抽象。
  2. 分层设计,将上层使用、设备以及驱动实现区分开来,降低了耦合性。

核心概念/数据结构

  • DM模型抽象出了以下四个概念/数据结构。
  1. uclass
  2. uclass_driver
  3. udevice
  4. driver

uclass

  • uclass(uboot class)是同一类外设(I2C、GPIO等)总的组织结构体,定义如下:

    // file:include/dm/uclass.h
    /**

    • struct uclass - a U-Boot drive class, collecting together similar drivers
    • A uclass provides an interface to a particular function, which is
    • implemented by one or more drivers. Every driver belongs to a uclass even
    • if it is the only driver in that uclass. An example uclass is GPIO, which
    • provides the ability to change read inputs, set and clear outputs, etc.
    • There may be drivers for on-chip SoC GPIO banks, I2C GPIO expanders and
    • PMIC IO lines, all made available in a unified way through the uclass.
    • @priv_: Private data for this uclass (do not access outside driver model)
    • @uc_drv: The driver for the uclass itself, not to be confused with a
    • 'struct driver'
    • @dev_head: List of devices in this uclass (devices are attached to their
    • uclass when their bind method is called)
    • @sibling_node: Next uclass in the linked list of uclasses
      */
      struct uclass {
      void *priv_;
      struct uclass_driver *uc_drv;
      struct list_head dev_head;
      struct list_head sibling_node;
      };
  • uclass 包含

  1. uc_drv: uclass driver。
  2. dev_head: udevice list(设备列表)。
  3. sibling_node: 链表的下一个uclass结构。

uclass_driver

  • uclass_driver是uclass的驱动,并不是具体硬件驱动,做一些uclass通用的的准备/回收等工作。

    // file:include/dm/uclass.h
    struct uclass_driver {
    const char *name;
    enum uclass_id id;
    int (*post_bind)(struct udevice *dev);
    int (*pre_unbind)(struct udevice *dev);
    int (*pre_probe)(struct udevice *dev);
    int (*post_probe)(struct udevice *dev);
    int (*pre_remove)(struct udevice *dev);
    int (*child_post_bind)(struct udevice *dev);
    int (*child_pre_probe)(struct udevice *dev);
    int (*child_post_probe)(struct udevice *dev);
    int (*init)(struct uclass *class);
    int (*destroy)(struct uclass *class);
    int priv_auto;
    int per_device_auto;
    int per_device_plat_auto;
    int per_child_auto;
    int per_child_plat_auto;
    uint32_t flags;
    };

  • 使用宏UCLASS_DRIVER定义一个uclass driver,每一类ip会定义一个,如下:

    //file: drivers/timer/timer-uclass.c
    UCLASS_DRIVER(timer) = {
    .id = UCLASS_TIMER,
    .name = "timer",
    .pre_probe = timer_pre_probe,
    .flags = DM_UC_FLAG_SEQ_ALIAS,
    .post_probe = timer_post_probe,
    .per_device_auto = sizeof(struct timer_dev_priv),
    };

  • 预定义的uclass id。

    //file: include/dm/uclass-id.h
    enum uclass_id {
    /* These are used internally by driver model */
    UCLASS_ROOT = 0,
    UCLASS_DEMO,
    UCLASS_TEST,
    UCLASS_TEST_FDT,
    UCLASS_TEST_FDT_MANUAL,
    UCLASS_TEST_BUS,
    UCLASS_TEST_PROBE,
    UCLASS_TEST_DUMMY,
    UCLASS_TEST_DEVRES,
    UCLASS_TEST_ACPI,
    ...
    }

udevice

  • udevice是具体硬件的实例,例如:dts中配置了两个timer,就会有两个timer udevice,uboot会将udevice和它所属的uclass以及具体的驱动driver绑定起来。

    //file:include/dm/device.h
    struct udevice {
    const struct driver *driver;
    const char *name;
    void *plat_;
    void *parent_plat_;
    void *uclass_plat_;
    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;
    #if !CONFIG_IS_ENABLED(OF_PLATDATA_RT)
    u32 flags_;
    #endif
    int seq_;
    #if !CONFIG_IS_ENABLED(OF_PLATDATA)
    ofnode node_;
    #endif
    #ifdef CONFIG_DEVRES
    struct list_head devres_head;
    #endif
    #if CONFIG_IS_ENABLED(DM_DMA)
    ulong dma_offset;
    #endif
    };

driver

  • 具体硬件的驱动,对应每个硬件的驱动实现,例如:dw wdt(drivers/watchdog/designware_wdt.c), mtk wdt(drivers/watchdog/mtk_wdt.c) 。

    //file: include/dm/device.h
    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 (*of_to_plat)(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;
    int plat_auto;
    int per_child_auto;
    int per_child_plat_auto;
    const void ops; / driver-specific operations */
    uint32_t flags;
    #if CONFIG_IS_ENABLED(ACPIGEN)
    struct acpi_ops *acpi_ops;
    #endif
    };

    • const void *ops; 是该驱动支持的接口。
  • ops 指向该类ip统一驱动接口结构体,在include目录的头文件中有各种ip需要支持的ops接口,例如:

    include/thermal.h //温度传感器需要支持的接口(ops)
    include/wdt.h //wdt需要支持的ops
    include/timer.h //timer需要支持的ops

    • include/timer.h
      struct timer_ops {
      /**
      • @get_count: Get the current timer count
      • @dev: The timer device
      • This function may be called at any time after the driver is probed.
      • All necessary initialization must be completed by the time probe()
      • returns. The count returned by this functions should be monotonic.
      • This function must succeed.
      • Return: The current 64-bit timer count
        */
        u64 (*get_count)(struct udevice *dev);
        };
  • 新思定时器(dw apb timer)定义:

    static const struct timer_ops dw_apb_timer_ops = {
    .get_count = dw_apb_timer_get_count,
    };

    U_BOOT_DRIVER(dw_apb_timer) = {
    .name = "dw_apb_timer",
    .id = UCLASS_TIMER,
    .ops = &dw_apb_timer_ops,
    .probe = dw_apb_timer_probe,
    .of_match = dw_apb_timer_ids,
    .of_to_plat = dw_apb_timer_of_to_plat,
    .remove = dw_apb_timer_remove,
    .priv_auto = sizeof(struct dw_apb_timer_priv),
    };

  • 使用宏U_BOOT_DRIVER定义一个驱动实例

    /* Declare a new U-Boot driver */
    #define U_BOOT_DRIVER(__name)
    ll_entry_declare(struct driver, __name, driver)

联系

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

代码流程

  • 模型的全局数据结构,包含根节点。

    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;

  • 初始化与扫描设备树

    int dm_init_and_scan(bool pre_reloc_only)
    {
    int ret;

      ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE));
      if (ret) {
              debug("dm_init() failed: %d\n", ret);
              return ret;
      }
      if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
              ret = dm_scan(pre_reloc_only);
              if (ret) {
                      log_debug("dm_scan() failed: %d\n", ret);
                      return ret;
              }
      }
    
      return 0;
    

    }

DM模型初始化(dm_init)

int dm_init(bool of_live)
{
    gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST;
    //初始化uclass_root链表头
    INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST);
    //创建一个device dm_root并将其绑定到driver name "root_driver"。
    device_bind_by_name(NULL, false, &root_info,
                      &DM_ROOT_NON_CONST);
    //探测设备udevice dm_root并激活它
    ret = device_probe(DM_ROOT_NON_CONST);
}
  • 待补充

DM模型Device Tree节点的设备初始化(dm_scan)

  • 待补充

绑定udevice和driver(device_bind_common)

  • 待补充

使用

  1. 整体使能

    配置:CONFIG_DM

  2. 不同类型外设需要专门使能

    串口配置:CONFIG_DM_SERIAL
    wdt配置:CONFIG_WDT

相关推荐
应家三千金2 个月前
uboot无法使用nfs下载文件的问题
linux·uboot·nfs
思禾2 个月前
Qemu开发ARM篇-3、qemu运行uboot演示
linux·arm开发·qemu·uboot
口嗨农民工3 个月前
中场接杀放网前小球
linux·开发语言·c·uboot
江上清风山间明月4 个月前
uboot的mmc partconf命令
uboot·mmc·boot·partconf
口嗨农民工5 个月前
uboo对内存操作读写命令的基本使用
linux·ubuntu·c·uboot
LaoZhangGong1239 个月前
Linux第45步_通过搭建“DNS服务器”学习图形化配置工具
linux·经验分享·学习·uboot·stm32mp157
liuxs199810 个月前
linker list
uboot
leon.liao1 年前
uboot - 驱动开发 - dw watchdog
驱动开发·uboot
zhang-ge1 年前
使用QEMU模拟启动uboot
qemu·uboot