u-boot的DM驱动模型

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 driverU_BOOT_DRIVER:用于注册硬件驱动,为编写底层硬件驱动提供了一个框架。适配不同的硬件时编写的就是这个驱动。
      struct uclass_driverUCLASS_DRIVER:用于维护一类驱动,是在U_BOOT_DRIVER注册的驱动层之上的一个框架,主要是为u-boot的上层应用提供一个统一的接口。大部分框架u-boot已具备,开发人员一般不用编写。
  • 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;
      };
      
  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值