0、本文基于U-Boot 2022.01-v2.07版本进行分析。
1、u-boot编译流程简要分析
2、u-boot启动流程简要分析
3、u-boot增加自定义命令
4、u-boot的DM驱动模型
- 4.1、参考资料
Uboot中的DM驱动模型:这篇文章详细介绍了DM驱动模型的原理。
本文重点整理了几个数据结构和驱动示例讲解。 - 4.2、DM驱动模型相关的数据结构
DM模型主要依赖于下面四种数据结构:- 1)
struct udevice
,具有硬件设备的抽象, 和driver实例相关
定义在include\dm\device.h
中:/*** struct udevice - An instance of a driver** This holds information about a device, which is a driver bound to a* particular port or peripheral (essentially a driver instance).** A device will come into existence through a 'bind' call, either due to* a U_BOOT_DRVINFO() macro (in which case plat is non-NULL) or a node* in the device tree (in which case of_offset is >= 0). In the latter case* we translate the device tree information into plat in a function* implemented by the driver of_to_plat method (called just before the* probe method if the device has a device tree node.** All three of plat, priv and uclass_priv can be allocated by the* driver, or you can use the auto members of struct driver and* struct uclass_driver to have driver model do this automatically.** @driver: The driver used by this device* @name: Name of device, typically the FDT node name* @plat_: Configuration data for this device (do not access outside driver* model)* @parent_plat_: The parent bus's configuration data for this device (do not* access outside driver model)* @uclass_plat_: The uclass's configuration data for this device (do not access* outside driver model)* @driver_data: Driver data word for the entry that matched this device with* its driver* @parent: Parent of this device, or NULL for the top level device* @priv_: Private data for this device (do not access outside driver model)* @uclass: Pointer to uclass for this device* @uclass_priv_: The uclass's private data for this device (do not access* outside driver model)* @parent_priv_: The parent's private data for this device (do not access* outside driver model)* @uclass_node: Used by uclass to link its devices* @child_head: List of children of this device* @sibling_node: Next device in list of all devices* @flags_: Flags for this device DM_FLAG_... (do not access outside driver* model)* @seq_: Allocated sequence number for this device (-1 = none). This is set up* when the device is bound and is unique within the device's uclass. If the* device has an alias in the devicetree then that is used to set the sequence* number. Otherwise, the next available number is used. Sequence numbers are* used by certain commands that need device to be numbered (e.g. 'mmc dev').* (do not access outside driver model)* @node_: Reference to device tree node for this device (do not access outside* driver model)* @devres_head: List of memory allocations associated with this device.* When CONFIG_DEVRES is enabled, devm_kmalloc() and friends will* add to this list. Memory so-allocated will be freed* automatically when the device is removed / unbound* @dma_offset: Offset between the physical address space (CPU's) and the* device's bus address space*/ 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_; #endifint seq_; #if CONFIG_IS_ENABLED(OF_REAL)ofnode node_; #endif #ifdef CONFIG_DEVRESstruct list_head devres_head; #endif #if CONFIG_IS_ENABLED(DM_DMA)ulong dma_offset; #endif };
- 2)
struct driver
,特定udevice的硬件驱动,包含了驱动的绑定、初始化、probe和卸载等函数。使用U_BOOT_DRIVER
来注册。
struct driver
定义在include\dm\device.h
中:/*** struct driver - A driver for a feature or peripheral** This holds methods for setting up a new device, and also removing it.* The device needs information to set itself up - this is provided either* by plat or a device tree node (which we find by looking up* matching compatible strings with of_match).** Drivers all belong to a uclass, representing a class of devices of the* same type. Common elements of the drivers can be implemented in the uclass,* or the uclass can provide a consistent interface to the drivers within* it.** @name: Device name* @id: Identifies the uclass we belong to* @of_match: List of compatible strings to match, and any identifying data* for each.* @bind: Called to bind a device to its driver* @probe: Called to probe a device, i.e. activate it* @remove: Called to remove a device, i.e. de-activate it* @unbind: Called to unbind a device from its driver* @of_to_plat: Called before probe to decode device tree data* @child_post_bind: Called after a new child has been bound* @child_pre_probe: Called before a child device is probed. The device has* memory allocated but it has not yet been probed.* @child_post_remove: Called after a child device is removed. The device* has memory allocated but its device_remove() method has been called.* @priv_auto: If non-zero this is the size of the private data* to be allocated in the device's ->priv pointer. If zero, then the driver* is responsible for allocating any data required.* @plat_auto: If non-zero this is the size of the* platform data to be allocated in the device's ->plat pointer.* This is typically only useful for device-tree-aware drivers (those with* an of_match), since drivers which use plat will have the data* provided in the U_BOOT_DRVINFO() instantiation.* @per_child_auto: Each device can hold private data owned by* its parent. If required this will be automatically allocated if this* value is non-zero.* @per_child_plat_auto: A bus likes to store information about* its children. If non-zero this is the size of this data, to be allocated* in the child's parent_plat pointer.* @ops: Driver-specific operations. This is typically a list of function* pointers defined by the driver, to implement driver functions required by* the uclass.* @flags: driver flags - see DM_FLAGS_...* @acpi_ops: Advanced Configuration and Power Interface (ACPI) operations,* allowing the device to add things to the ACPI tables passed to Linux*/ 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 };
U_BOOT_DRIVER
也定义在include\dm\device.h
中:/* Declare a new U-Boot driver */ #define U_BOOT_DRIVER(__name) \ll_entry_declare(struct driver, __name, driver)
- 3)
struct uclass
,维护一类驱动。
struct uclass
定义在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; };
- 4)
struct uclass_driver
,在编写驱动时,会使用UCLASS_DRIVER
来注册一个uclass_driver
对象。这个uclass驱动维护了这一类硬件驱动的接口,为上层的调用提供了统一的接口。
struct uclass_driver
定义在include\dm\uclass.h
:/*** struct uclass_driver - Driver for the uclass** A uclass_driver provides a consistent interface to a set of related* drivers.** @name: Name of uclass driver* @id: ID number of this uclass* @post_bind: Called after a new device is bound to this uclass* @pre_unbind: Called before a device is unbound from this uclass* @pre_probe: Called before a new device is probed* @post_probe: Called after a new device is probed* @pre_remove: Called before a device is removed* @child_post_bind: Called after a child is bound to a device in this uclass* @child_pre_probe: Called before a child in this uclass is probed* @child_post_probe: Called after a child in this uclass is probed* @init: Called to set up the uclass* @destroy: Called to destroy the uclass* @priv_auto: If non-zero this is the size of the private data* to be allocated in the uclass's ->priv pointer. If zero, then the uclass* driver is responsible for allocating any data required.* @per_device_auto: Each device can hold private data owned* by the uclass. If required this will be automatically allocated if this* value is non-zero.* @per_device_plat_auto: Each device can hold platform data* owned by the uclass as 'dev->uclass_plat'. If the value is non-zero,* then this will be automatically allocated.* @per_child_auto: Each child device (of a parent in this* uclass) can hold parent data for the device/uclass. This value is only* used as a fallback if this member is 0 in the driver.* @per_child_plat_auto: A bus likes to store information about* its children. If non-zero this is the size of this data, to be allocated* in the child device's parent_plat pointer. This value is only used as* a fallback if this member is 0 in the driver.* @flags: Flags for this uclass (DM_UC_...)*/ 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
也定义在include\dm\uclass.h
:
最后做个总结:/* Declare a new uclass_driver */ #define UCLASS_DRIVER(__name) \ll_entry_declare(struct uclass_driver, __name, uclass_driver)
struct driver
和U_BOOT_DRIVER
:用于注册硬件驱动,为编写底层硬件驱动提供了一个框架。适配不同的硬件时编写的就是这个驱动。
struct uclass_driver
和UCLASS_DRIVER
:用于维护一类驱动,是在U_BOOT_DRIVER
注册的驱动层之上的一个框架,主要是为u-boot的上层应用提供一个统一的接口。大部分框架u-boot已具备,开发人员一般不用编写。
- 1)
- 4.3、驱动实例
下面以drivers\mtd\altera_qspi.c
驱动为例进行分析:-
1)
U_BOOT_DRIVER
:和硬件相关
部分代码如下:U_BOOT_DRIVER(altera_qspi) = {.name = "altera_qspi",.id = UCLASS_MTD,.of_match = altera_qspi_ids,.of_to_plat = altera_qspi_of_to_plat,.plat_auto = sizeof(struct altera_qspi_plat),.probe = altera_qspi_probe, };
其中,
U_BOOT_DRIVER
是注册硬件驱动的接口;
.name
:驱动名字;
.id
:指明驱动的类型,用于匹配UCLASS_DRIVER
;
.of_match
:设备树匹配表;
.of_to_plat
:获取设备树的属性的函数;
.plat_auto
:驱动平台私有数据,在设备匹配时会自动为私有数据分配空间;
.probe
:和设备树匹配后,会进入这个函数; -
2)
UCLASS_DRIVER
:为u-boot上层提供统一接口
这部分一般不用开发者编写,这里主要是为了梳理DM的流程。
UCLASS_MTD
类的注册代码在drivers\mtd\mtd-uclass.c
如下:/** Implement a MTD uclass which should include most flash drivers.* The uclass private is pointed to mtd_info.*/UCLASS_DRIVER(mtd) = {.id = UCLASS_MTD,.name = "mtd",.per_device_auto = sizeof(struct mtd_info), };
这里可以看出
UCLASS_MTD
的设备,驱动和上层之间的桥梁是通过struct mtd_info
实现的。
在include\linux\mtd\mtd.h
中定义如下:struct mtd_info { u_char type; uint32_t flags; uint64_t size; // Total size of the MTD/* "Major" erase size for the device. Naïve users may take this* to be the only erase size available, or may use the more detailed* information below if they desire*/ uint32_t erasesize; /* Minimal writable flash unit size. In case of NOR flash it is 1 (even* though individual bits can be cleared), in case of NAND flash it is* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR* it is of ECC block size, etc. It is illegal to have writesize = 0.* Any driver registering a struct mtd_info must ensure a writesize of* 1 or larger.*/ uint32_t writesize;/** Size of the write buffer used by the MTD. MTD devices having a write* buffer can write multiple writesize chunks at a time. E.g. while* writing 4 * writesize bytes to a device with 2 * writesize bytes* buffer the MTD driver can (but doesn't have to) do 2 writesize* operations, but not 4. Currently, all NANDs have writebufsize* equivalent to writesize (NAND page size). Some NOR flashes do have* writebufsize greater than writesize.*/ uint32_t writebufsize;uint32_t oobsize; // Amount of OOB data per block (e.g. 16) uint32_t oobavail; // Available OOB bytes per block/** If erasesize is a power of 2 then the shift is stored in* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.*/ unsigned int erasesize_shift; unsigned int writesize_shift; /* Masks based on erasesize_shift and writesize_shift */ unsigned int erasesize_mask; unsigned int writesize_mask;/** read ops return -EUCLEAN if max number of bitflips corrected on any* one region comprising an ecc step equals or exceeds this value.* Settable by driver, else defaults to ecc_strength. User can override* in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;* see Documentation/ABI/testing/sysfs-class-mtd for more detail.*/ unsigned int bitflip_threshold;// Kernel-only stuff starts here. #ifndef __UBOOT__const char *name; #elsechar *name; #endifint index;/* OOB layout description */const struct mtd_ooblayout_ops *ooblayout;/* ECC layout structure pointer - read only! */struct nand_ecclayout *ecclayout;/* the ecc step size. */unsigned int ecc_step_size;/* max number of correctible bit errors per ecc step */unsigned int ecc_strength;/* Data for variable erase regions. If numeraseregions is zero,* it means that the whole device has erasesize as given above.*/int numeraseregions;struct mtd_erase_region_info *eraseregions;/** Do not call via these pointers, use corresponding mtd_*()* wrappers instead.*/int (*_erase) (struct mtd_info *mtd, struct erase_info *instr); #ifndef __UBOOT__int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, void **virt, resource_size_t *phys);int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len); #endifunsigned long (*_get_unmapped_area) (struct mtd_info *mtd,unsigned long len,unsigned long offset,unsigned long flags);int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf);int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf);int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf);int (*_read_oob) (struct mtd_info *mtd, loff_t from,struct mtd_oob_ops *ops);int (*_write_oob) (struct mtd_info *mtd, loff_t to,struct mtd_oob_ops *ops);int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,size_t *retlen, struct otp_info *buf);int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,size_t len, size_t *retlen, u_char *buf);int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len,size_t *retlen, struct otp_info *buf);int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,size_t len, size_t *retlen, u_char *buf);int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,size_t len, size_t *retlen, u_char *buf);int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,size_t len); #ifndef __UBOOT__int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,unsigned long count, loff_t to, size_t *retlen); #endifvoid (*_sync) (struct mtd_info *mtd);int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); #ifndef __UBOOT__int (*_suspend) (struct mtd_info *mtd);void (*_resume) (struct mtd_info *mtd);void (*_reboot) (struct mtd_info *mtd); #endif/** If the driver is something smart, like UBI, it may need to maintain* its own reference counting. The below functions are only for driver.*/int (*_get_device) (struct mtd_info *mtd);void (*_put_device) (struct mtd_info *mtd);#ifndef __UBOOT__/* Backing device capabilities for this device* - provides mmap capabilities*/struct backing_dev_info *backing_dev_info;struct notifier_block reboot_notifier; /* default mode before reboot */ #endif/* ECC status information */struct mtd_ecc_stats ecc_stats;/* Subpage shift (NAND) */int subpage_sft;void *priv;struct module *owner; #ifndef __UBOOT__struct device dev; #elsestruct udevice *dev; #endifint usecount;/* MTD devices do not have any parent. MTD partitions do. */struct mtd_info *parent;/** Offset of the partition relatively to the parent offset.* Is 0 for real MTD devices (ie. not partitions).*/u64 offset;/** List node used to add an MTD partition to the parent* partition list.*/struct list_head node;/** List of partitions attached to this MTD device (the parent* MTD device can itself be a partition).*/struct list_head partitions; };
-