U-BOOT DM驱动模型
U-BOOT DM驱动模型
Before driver model
- U-Boot has 10 useful design principles (e.g. small, fast, simple, portable)
- Huge community, over 1000 boards supported by the end of 2011
- But Ad-hoc driver model started to bite
- Drivers were invoked through direct C calls
- i2c_read() is implemented by whichever driver is compiled in
- CONFIG option select which I2C driver to use, clock speed, bus number, etc.
- Hard to scale
- Multiple I2C drivers must be munged into a single driver
- Or an ad-hoc framework created to handle this requirements
- Configuration becoming unwieldy
- 6000 CONFIG options at its peak
- Kconfig conversion helps, but that’s still a lot of options
dm-u-boot.pdf
U-Boot Mini Summit talk on driver model at ELCE 2014
Why driver model?
- Device init and access is ad-hoc
– scsi_init(), mmc_init(), nand_init() - Many subsystems only allow one driver
– But I have USB2 and USB3! - Communication between subsystems is tricky
– How does an I2C expander or PMIC provide a GPIO? - Hard to answer simple questions
– How many GPIOs? What is my serial console? - Board file functions provide the glue
– What GPIO provides my MMC card detect?
Architecture 体系结构
DM (Driver Model)是 U-Boot 中的驱动框架。
udevice 描述具体的某一个硬件设备。
- Instance of a driver
- Created from some platform-specific information bound to a driver
driver 是与这个设备匹配的驱动。
- Code to talk to a peripheral type (e.g. Ethernet switch, I2C controller, LCD)
uclass 是同一类设备的抽象,提供管理同一类设备的抽象接口。
- A way of grouping devices which operate the same way
Device tree and hierarchy
- 设备树和层次结构
Memory allocation
Sequence numbers
- 序列号
像 Kernel 中的驱动三要素 device 、bus 、driver 一样,DM 也有自己的三要素:udevice、uclass、driver。以 serial 驱动为例:
Device tree configuration
Device sequence
Automatic memory allocation
Architecture 2
- Binding and probing
- Bing creates the device but does not touch hardware
- Probing activates the device ready for use
- Avoid private data structures
- Everything out in the open
- SPL support
- fdtgrep
- Simple malloc()
- Drop device removal code, warnings, etc.
Driver model benefits (en)
- Consistent view of devices regardless of their type
- Multiple driver can be used with the same subsystem
- Drivers can be created which use others driver for their transport layer
- The lifecycle of a device is clear and consistent
- Devices can be bound automatically
- Then probed automatically when used
- Supports device tree for configuration
- Thus sharing this with Linux and potentially other projects
- Avoids recreating the same information again in a different format
Driver model benefits (zh)
- 一致的设备视图,无论其类型如何
- 同一个子系统可以使用多个驱动程序
- 可以创建使用其他驱动程序作为传输层的驱动程序
- 设备生命周期清晰一致
- 设备可自动绑定
- 然后使用时自动探测
- 支持设备树配置
- 因此与 Linux 和潜在的其他项目共享这个
- 避免以不同的格式重新创建相同的信息
DM Limitations
- A driver can be in only one uclass
- Multi-function devices must use separate child devices
- Uses flattened device tree (FDT,即 device tree, dts)
- Driver model uses the device tree offset
- Overlays and other mutations are not supported
Comparisons with Linux
Classes
Buses
Binding and probing
Memory allocation
Relocation and SPL //重定位 & SPL
device visibility
Locking
Data structure size
- Core structure sizes are moderate
- struct uclass
- struct udevice
- struct driver
- Core structure sizes are moderate
automatic memory allocation
A few examples
Example 1: Requesting GPIOs
Example 2: Enabling power
重要数据结构
udevice
1 | /** |
有三种途径生成一个 udevice:
dts 设备节点
UBOOTDEVICE(__name) 宏申明
调用 ‘bind’ API, device_bind_xxx
Device Model Start-up sequence
DM 启动顺序, core/root.c
- dm_init_and_scan():
- dm_init()
- Creates an empty list of devices and uclasses
- Binds and probes a root device
- dm_scan_platdata()
- Scans available platform data looking devices to be created
- Platform data may only be used when memory constrains prohibit device tree
- dm_scan_fdt()
- Scan device tree and bind driver to nodes to create devices
根据当前 U-Boot 的编程哲学,基本大部分设备都是通过 dts 来描述,还有少部分设备因为特殊原因,可以通过 U_BOOT_DEVICE(_name) 宏申明。
匹配过程
在UBoot DM 初始化阶段(initfdm 和 initrdm),通过调用 dm_init_and_scan(boolpre_reloc_only) 根据名称 (UBOOT_DEVICE 中和 driver 的 name,或者 dts 和 driver 的 compatible) 匹配到对应的 driver,
然后调用 device_bind_common 函数生成 udevice,udevice 会和 driver 绑定,
并根据 driver 中的uclass id 找到对应的 uclass driver,并生成相应的 uclass, 并把该设备挂到 uclass 的设备节点之下。
最后调用 driver 的 bind 函数。
1 | | -> dm_init_and_scan |
还有部分特殊的驱动,他们并不存在实际意义上的设备,比如 MMC 子系统中的 mmcblk 驱动,
该驱动主要是把所有的 mmc 设备注册到更上一层的 blk 子系统中,向 blk 层提供操作 mmc 设备的 blkops,向下通过mmc uclass 提供的统一接口控制 mmc 设备。显然,这个驱动位于抽象层,它不和具体的硬件设备直接交互,并不适合用一个 dts(dts 是用来描述具体的硬件信息的) 节点或者 UBOOTDEVICE(_name) 宏来为这个驱动显示的申明设备。这种情形下一般通过主动调用 device_bind_xxx 系列 API 来完成驱动和设备已经更上一层 uclass 之间的 bind。
A:生成 udevice。
B:绑定 udevice 和 driver。
C:把设备挂到 uclass 的dev_head 链表下。
D:调用设备驱动的 bind 接口。
uclass
1 | /** |
这里主要的成员是 uclassdriver 和 devhead 链表。
- dev_head 是一个链表头, 用来链接该类下的所有设备。可以通过 uclass_foreach_dev(dev,uc) 遍历该class 下的所有设备。
- uclass_driver 是针对某一类设备提供的通用操作接口,然后通过 udevice->driver->ops 操作到具体的硬件设备。
uclassdriver 通过 UCLASSDRIVER(name) 宏申明, 在 device_bind_common 中根据 设备对应的驱动 driver 中的 uclass id 找到 uclassdriver,并生成相应的 uclass, 并把设备挂到该 uclass 的设备节点 dev_head 下。
以 pwm backlight 为例:
通过 UBOOTDRIVER 的 id 可以看出,该设备(pwm backlight)驱动属于 UCLASSPANELBACKLIGHT 类。
这里定义了 backlight 的 UCLASS_DRIVER。该 uclass driver 提供了 backlight_enable(struct udevice*dev) 和 backlight_set_brightness(struct udevice*dev,intpercent)
两个通用的 API 供应用调用,可以看到他们都需要传递对应设备的 udevice ,然后通过 backlight_get_ops(dev) 拿到对该设备的操作接口。
#define backlight_get_ops(dev) ((struct backlight_ops *)(dev)->driver->ops)
driver
1 | /** |
通过 UBOOTDRIVER(__name) 宏声明。如果 driver 实现了 bind 接口,该bind 将在 device_bind_common 中 device 和 driver 匹配上后被调用, 而且在 device_bind_common 中会完成 udevice 和 driver 的绑定。
driver 一般都有对应的 probe 接口,通过 device_probe(structudevice*dev) 调用,需要注意的是driver 的 bind 接口调用的比 probe 接口早, 大部分在 dm_init_and_scan 中就被调用了。
driver 一般会提供 ops 操作接口,供上一层调用。
需要说明的是,driver 一般都不需要把自己注册到 uclass 中,而是在 device_bind _common 阶段实现driver 、uclass、device 三者的对接,然后 uclass 层通过 udevice->driver->ops 获取对应 driver 的操作接口。
设备驱动的使用
一般应用层的代码要使用某个设备的时候,首先需要通过 uclass_get_device_xxx 系列 API 拿到该设备的 udevice, 然后通过该设备的 uclass 提供的 API 操作该设备。
uclass_get_device_xxx 拿到该设备的 udevice 后会调用该设备的 probe 接口。
以前面提到的 pwm backlight 为例:
1 | /** |