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_; #endif int seq_; #if CONFIG_IS_ENABLED(OF_REAL) ofnode node_; #endif #ifdef CONFIG_DEVRES struct 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; #else char *name; #endif int 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); #endif unsigned 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); #endif void (*_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; #else struct udevice *dev; #endif int 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; };
-