U-Boot VII explica en detalle la arquitectura, la configuración, los comandos y el proceso de inicialización del modelo de controlador

  En abril de 2014, U-Boot se refirió al diseño del modelo de controlador Linux Kernel e introdujo su propia arquitectura de controlador Modelo de controlador (oficialmente denominado DM). Este modelo de controlador (DM) proporciona un método unificado para la definición del controlador y la interfaz de acceso, lo que mejora la compatibilidad entre los controladores y la estandarización del acceso.
inserte la descripción de la imagen aquí
  Todos los códigos involucrados en este artículo están colocados en mi Github personal: https://github.com/ZCShou/BOARD-STM32F769I-EVAL, puede usarlo directamente para aprender y verificar, para evitar la miopía y la falta de mano. El código fuente involucrado en este artículo se usa principalmente U-Boot-v2022.10, ¡y el código fuente de diferentes versiones puede ser bastante diferente! ! !

configuración

  La arquitectura DM debe CONFIG_DM=yhabilitarse , y los controladores periféricos reales correspondientes deben CONFIG_DM_xxxhabilitarse a través de habilitar. Entre ellos, xxx indica un dispositivo periférico específico. Por ejemplo, habilitar CONFIG_DM_SERIALagregará automáticamente el archivo de código fuente correspondiente en el Makefile:
inserte la descripción de la imagen aquí
  En la actualidad, los controladores de la mayoría de los dispositivos se han migrado completamente a la arquitectura DM. Por lo tanto, en el código fuente real , A menudo podemos ver que CONFIG_DM_xxxla interfaz del controlador correspondiente está implementada, pero la versión anterior no está implementada o no tiene la interfaz del controlador anterior (algunos controladores todavía están en el modo de controlador anterior).
inserte la descripción de la imagen aquí

opciones de enlace

  El controlador DM se almacenará uniformemente en el archivo de imagen final después de la compilación, y el controlador de arquitectura DM de cada dispositivo se colocará en una sección separada durante la compilación. Cuando compilamos U-Boot, todos estos controladores __u_boot_list_2_"#_list"_2_"#_nameusarán el área de la sección como nombre. Además, estas áreas de la sección se envolverán__u_boot_list_2_"#_list"_1 con y , para que se pueda calcular el tamaño de todos. Esta información se puede ver :__u_boot_list_2_"#_list"_3__u_boot_list_2_*u-boot.map
inserte la descripción de la imagen aquí

De hecho, no solo el controlador, sino también otras partes (por ejemplo, cmd) se manejan de esta manera.

  La clave para la implementación en el código se encuentra en los valores de macro de UCLASS_DRIVER(__name), U_BOOT_DRIVER(__name)etc. Estos valores de macro eventualmente se referirán a las macros relevantes ./include/linker_lists.hen ll_entry_declare, que es la clave para la implementación.
inserte la descripción de la imagen aquí
  Durante el proceso de inicialización, U-Boot atravesará las secciones anteriores, luego realizará la coincidencia de contenido y creará varios dispositivos y la UCLASS correspondiente a su vez. El siguiente es el método para encontrar el controlador especificado según el nombre del controlador:
inserte la descripción de la imagen aquí

Árbol de dispositivos/datos de plataforma

El controlador debe conocer la información básica del hardware.U-Boot admite dos métodos para proporcionar información básica de configuración del hardware,   Datos de la plataforma (datos de la plataforma, a menudo denominados plato platdata) y Árbol de dispositivos aplanado (árbol de dispositivos, al que a menudo se hace referencia en código ). fdtEntre ellos, los datos de la plataforma son la forma antigua y el árbol de dispositivos es la forma estándar.

Datos de la plataforma

  Los datos de la plataforma pasan información de configuración específica de la plataforma (dirección de registro, velocidad del bus, etc.) al conductor a través de una estructura C, y las definiciones relevantes se encuentran en ./include/dm/platdata.hel archivo Después de inicializar el DM (en realidad, después de la sonda), la información del dispositivo finalmente se almacena en la memoria udevice ->plat_a la que apunta y el conductor puede dev->plat_acceder . El funcionario señaló que a menos que exista una razón necesaria, no utilice el método de datos de la plataforma, sino el método del árbol de dispositivos.

static const struct dm_demo_pdata red_square = {
    
    
        .colour = "red",
        .sides = 4.
};

/* 直接定义(不推荐) */
static const struct driver_info info[] = {
    
    
        {
    
    
                .name = "demo_shape_drv",
                .plat = &red_square,
        },
};
demo1 = driver_bind(root, &info[0]);

/* 使用 U_BOOT_DRVINFO 宏(推荐) */
U_BOOT_DRVINFO(demo0) = {
    
    
	.name = "demo_shape_drv",
	.plat = &red_square,
};

  La recomendación oficial es usar Platform Data solo cuando las limitaciones de memoria no permitan el uso de un árbol de dispositivos. Además, U-Boot proporciona un método para convertir automáticamente el árbol de dispositivos en datos de plataforma, es decir, of-platdatacaracterísticas . Sin embargo, of-platdatasolo está disponible en SPL/TPLla fase .

Árbol de dispositivos

  El árbol de dispositivos proporciona un método más flexible para proporcionar datos de dispositivos y la recomendación oficial es utilizar el método de árbol de dispositivos. U-Boot analizará automáticamente el árbol de dispositivos para obtener información relevante del dispositivo. Estos datos de información se denominan ofdata(abreviatura de datos de Open-Firmware) y la estructura relacionada con el árbol de dispositivos (parte del código) se ./include/dm/of.hdefine ofen el archivo que comienza con etc. Después de inicializar el DM (en realidad, después de la sonda), la información del dispositivo finalmente se almacena en la memoria udevice ->plat_a la que apunta (específicamente a través driverde los miembrosplat_auto y en ).   El árbol de dispositivos de U-Boot proviene del Kernel de Linux. De acuerdo con las instrucciones oficiales, para mantener la compatibilidad sin realizar cambios adicionales en los archivos fuente del árbol del dispositivo original, U-Boot presenta un archivo especial de U-Boot llamado .of_to_plat
inserte la descripción de la imagen aquí
*-u-boot.dtsi

Ningún otro archivo hará referencia directamente a   los recién *-u-boot.dtsiagregados , ya que estos archivos se introducen directamente en el Makefile. Solo de esta manera U-Boot puede evitar por completo la modificación de los archivos del árbol del dispositivo desde el Kernel de Linux. La prioridad incluida de mayor a menor es la siguiente:*.dts*-u-boot.dtsi

  1. <orig_filename>-u-boot.dtsi# <orig_filename> es el nombre correspondiente al .dts a compilar
  2. <CONFIG_SYS_SOC>-u-boot.dtsi
  3. <CONFIG_SYS_CPU>-u-boot.dtsi
  4. <CONFIG_SYS_VENDOR>-u-boot.dtsi
  5. u-boot.dtsi

marco

  El DM de U-Boot usa uclassy udeviceestas dos clases abstractas para administrar todos los controladores de dispositivos, y estas dos clases abstractas corresponden a uclass_drivery driver. udevicese crea driverdinámicamente ; uclassse uclass_drivercrea . Pero sólo cuando se cree udeviceencontrará el correspondiente uclass, por lo que al final sólo se creará si driverexiste uclass.
inserte la descripción de la imagen aquí
  Lo realmente útil es un independiente uclass_drivery driver, uclassse udevicegestionan a través de y respectivamente. En términos de implementación de código, uclassen udevicerealidad nodos de una lista doblemente enlazada, y todos los controladores se administran a través de una lista doblemente enlazada. Y finalmente, la posición de la lista enlazada se indica mediante la variable relevante global_dataen . Un diagrama de bloques simple es el siguiente:
inserte la descripción de la imagen aquí
  El funcionario proporciona una DEMOSTRACIÓN del controlador ( drivers/demo), al abrir CONFIG_DM=y, CONFIG_CMD_DEMO=y, puede agregar esta DEMO a nuestra compilación y luego realizar una prueba de aprendizaje.CONFIG_DM_DEMO=yCONFIG_DM_DEMO_IMX6ULL=y

global_data

  ./include/asm-generic/global_data.hstruct global_dataLa estructura en el archivo administra las variables globales de todo el U-Boot Después de que definamos CONFIG_DM=y, global_datahabrá algunos campos relacionados con DM en el archivo para guardar información relacionada con DM. Consulte los comentarios del código a continuación para obtener más detalles:

struct global_data {
    
    
/* ... 略 ... */
#ifdef CONFIG_DM
	struct udevice *dm_root; /* 指向 DM 的根设备 */
	struct udevice *dm_root_f; /* 指向重定向前的 DM 的根设备 */
	struct list_head uclass_root_s; /* 非只读性内存中的 UCLASS 链表表头 */
	struct list_head *uclass_root;	/* UCLASS 链表表头指针,非只读内存中他就指向上面的 uclass_root_s */
	/* ... 略 ... */
#endif
/* ... 略 ... */

uclass y uclass_driver

  uclassy uclass_driverse definen en ./include/dm/uclass.hel archivo, entre ellos, uclasses una clase abstracta que divide los dispositivos del mismo tipo en un grupo para la gestión de la clasificación; uclass_driverproporciona una interfaz coherente para un grupo de controladores de dispositivos relacionados. Hayuclass una correspondencia uno a uno con . uclass_driver
inserte la descripción de la imagen aquí
  En la expresión, generalmente la usamos uclass_idpara representar una uclass, por ejemplo, UCLASS_ROOT representa la propia ROOT UCLASS, y su controlador correspondiente se llama uclass_driver_root. Los valores disponibles del ID de uclass se definen en ./include/dm/uclass-id.hel archivo enum uclass_id, cabe señalar que el ID se usa realmente en struct uclass_driverel archivo .
inserte la descripción de la imagen aquí

estructura uclase

  uclassAgrupe los dispositivos del mismo tipo en un grupo para la gestión clasificada. Tenga en cuenta que uclassU-Boot lo genera automáticamente durante el proceso de inicialización, y uclass_idno uclassse generarán todos los correspondientes, solo se generarán los correspondientes uclass_drivery coincidentesudevice (detalles en la sección de inicialización a continuación).uclass

struct uclass {
    
    
	void *priv_;					/* uclass 本身使用的私有数据指针。不对外使用。*/
	struct uclass_driver *uc_drv; 	/* 一个 UCLASS 对一个 uclass_driver,这个指针指向对应的 uclass_driver */
	struct list_head dev_head;		/* 本 UCLASS 下对应的 udevice 链表 */
	struct list_head sibling_node;  /* 本 UCLASS 节点本身的前后节点(用于串联 uclass 链表) */
};

  En términos de implementación de código, struct uclassesta estructura es en realidad un nodo de lista enlazada.Después de inicializar DM, todos los dispositivos existentes uclassformarán una gd->uclass_rootlista doblemente enlazada con el encabezado de la lista enlazada. Esta lista enlazada se encadena a través sibling_nodede sus miembros.
inserte la descripción de la imagen aquí
  Cuando se inicializa DM, recorrerá todo automáticamente uclass_driver, y cada vez que encuentre un , uclass_drivercomprobará uclasssi existe el ID correspondiente (la condición de juicio es que ya existe uclass->uc_drv->id 是否等于当前 uclass_driver->id), y si no existe, creará un nuevo con el ID uclass_driveren uclass, y luego asócielo uclass_drivercon . Es decir, uclassse crea uclass_driverdinámicamente .

estructura uclass_driver

  uclass_driverProporciona una interfaz coherente para un conjunto de controladores relacionados, uno uclasspara uclass_driver . En términos de implementación de código, struct uclassel uc_drvin apunta a la uclass actual uclass_driver.

struct uclass_driver {
    
    
	const char *name;						/* uclass driver 的名称,在定义 uclass_driver 时,填写的一个字符串 */
	enum uclass_id id;						/* uclass 的 ID 号,取值见 ./include/dm/uclass-id.h 文件中的 enum uclass_id 定义 */
	int (*post_bind)(struct udevice *dev);  /* 在一个新设备绑定到这个 uclass 后被调用 */
	int (*pre_unbind)(struct udevice *dev); /* 在一个设备从该 uclass 解绑定之前调用 */
	int (*pre_probe)(struct udevice *dev);  /* 在 probe 一个新设备之前调用 */
	int (*post_probe)(struct udevice *dev); /* 在 probe 一个新设备之后调用 */
	int (*pre_remove)(struct udevice *dev); /* 在移除设备之前调用 */
	int (*child_post_bind)(struct udevice *dev);  	/* 在这个 uclass 的 child 绑定到一个设备之后被调用 */
	int (*child_pre_probe)(struct udevice *dev);	/* 在这个 uclass 的 child 被 probed 之前被调用 */
	int (*child_post_probe)(struct udevice *dev);	/* 在这个 uclass 的 child 被 probed 之后被调用 */
	int (*init)(struct uclass *class);				/* 在创建一个新的 uclass 时被调用 */
	int (*destroy)(struct uclass *class);			/* 在 uclass 被销毁时被调用 */
	int priv_auto;				/* 如果非零,它就是 uclass->priv_ 指针中分配的私有数据的大小。如果为 0,则 uclass driver 负责分配所需私有数据的空间 */
	int per_device_auto;		/* 每个 device 都可以将 uclass 拥有的私有数据保存在自己的 dev->uclass_priv_ 中。如果该值是非零,将在 device 初始化时自动分配该值大小的空间 */
	int per_device_plat_auto;	/* 每个 device 都可以将 uclass 拥有的平台数据保存在自己的 dev->uclass_plat_ 中。如果该值是非零,将在 device 初始化时自动分配该值大小的空间 */
	int per_child_auto;			/* 每个子设备可以保存它的 parent 私有数据到 dev->parent_priv_ 中。如果该值是非零,将在 device 初始化时自动分配该值大小的空间。在 udevice 对应的 driver 中,也存在该变量,只有 udevice 对应的 driver 中该值为 0 时,才会使用该值 */
	int per_child_plat_auto;	/* 每个子设备可以保存它的 parent 平台数据到 dev->parent_plat_ 中。如果该值是非零,将在 device 初始化时自动分配该值大小的空间。在 udevice 对应的 driver 中,也存在该变量,只有 udevice 对应的 driver 中该值为 0 时,才会使用该值 */
	uint32_t flags;				/*  这个 uclass 的标志(DM_UC_…) */
};

  En la implementación del código, uclass_driverse deben usar macros UCLASS_DRIVER(__name)para la definición. Además de usar la variable struct uclass_driverde definición __nametambién definirá una sección con el mismo nombre y __namela colocará en la sección con el mismo nombre. Tome serialcomo ejemplo , como se muestra a continuación:
inserte la descripción de la imagen aquí
  uclass_driveres administrado por una lista vinculada struct uclasscomo un nodo, y uclass_drivercada uno debe estar asociado con un struct uclassespecificado (en un nodo de lista vinculada). Cuando no hay un correspondienteuclass_driver , se creará uno automáticamente y luego se asociará .struct uclassstruct uclass
inserte la descripción de la imagen aquí

dispositivos y controladores

  udevicey se driverdefinen en ./include/dm/device.hel archivo, donde udevicese utiliza una clase abstracta para representar un dispositivo (una instancia del controlador); driveres el controlador real correspondiente a un dispositivo. udeviceLa relación con driverpuede ser de muchos a uno (varios dispositivos pueden compartir el mismo driver).
inserte la descripción de la imagen aquí
  A diferencia de UCLASS, ambosudevice tienen miembros para identificarse y los dos nombres pueden ser diferentes o iguales. Por ejemplo, el nombre del dispositivo ROOT y el nombre del controlador ROOT son ambos . El nombre del dispositivo definido en el árbol de dispositivos suele ser un nombre de nodo y el nombre del controlador correspondiente es una cadena con el significado exacto en el código.drivernameroot_driver

dispositivo de estructura

  udeviceContiene información sobre el dispositivo, que es esencialmente una instancia de controlador que debe vincularse a un puerto específico o controlador periférico ( udevicedebe asociarse const struct driver *drivercon driver). udeviceNo se puede asociar con UCLASS en sí mismo y struct driverdebe idasociarse con el UCLASS en el que se encuentra de acuerdo con el atributo de su asociación.

struct udevice {
    
    
	const struct driver *driver;/* 此设备使用的驱动程序 */
	const char *name;			/* 设备名称,通常为 FDT 节点名称 */
	void *plat_;				/* 此设备的配置数据(DM 之外不能访问),这通常由驱动程序制定大小,并且由驱动程序负责填充内容 */
	void *parent_plat_;			/* 该设备的父总线配置数据(DM 之外不能访问) */
	void *uclass_plat_;			/* 此设备对应的 uclass 的配置数据(DM 之外不能访问) */
	ulong driver_data;			/* 驱动程序数据字,用于将此设备与其驱动程序相匹配的条目 */
	struct udevice *parent;		/* 该设备的父设备,顶级设备(例如,ROOT DEVICE)的 parent 为 NULL */
	void *priv_;				/* 此设备的私有数据(DM 之外不能访问) */
	struct uclass *uclass;		/* 指向该设备对应的 uclass 的指针 */
	void *uclass_priv_;			/* 此设备对应的 uclass 的私有数据(DM 之外不能访问) */
	void *parent_priv_;			/* 此设备的父设备的私有数据 */
	struct list_head uclass_node;	/* 由此设备对应的 uclass 用于连接它的设备 */
	struct list_head child_head;	/* 此设备的子设备列表 */
	struct list_head sibling_node;	/* 所有设备列表中的下一个设备 */
#if !CONFIG_IS_ENABLED(OF_PLATDATA_RT)
	u32 flags_;					/* 此设备的标志 DM_FLAG_xx */
#endif
	int seq_;					/* 为该设备分配的序列号(-1 表示没有序列号)。这是在设备绑定时设置的,在设备对应的 uclass 中是唯一的。如果设备在设备树中有别名,则别名用于设置序列号。否则,使用下一个可用号码。序列号用于某些需要对设备进行编号的命令(例如 mmc dev)(DM 之外不能访问)*/
#if CONFIG_IS_ENABLED(OF_REAL)
	ofnode node_;				/* 此设备的设备树节点的引用 */
#endif
#if CONFIG_IS_ENABLED(DEVRES)
	struct list_head devres_head;	/* 与此设备关联的内存分配列表。当 CONFIG_DEVRES 被启用时,devm_kmalloc()和 friends 会添加到这个列表中。这样分配的内存将在移除或解绑设备时自动释放 */
#endif
#if CONFIG_IS_ENABLED(DM_DMA)
	ulong dma_offset;			/* (CPU 的)物理地址空间和设备总线地址空间之间的偏移量 */
#endif
};

  En términos de implementación de código, struct udeviceesta estructura es en realidad un nodo de lista enlazada. Después de inicializar el DM, todos udeviceformarán una lista doblemente enlazada con el encabezado de la lista enlazada. Esta lista enlazada está conectada gd->dm_rootpor el child_heady este miembro. A diferencia desibling_node , también admite la concatenación . Es decir, a estará en múltiples listas enlazadas al mismo tiempo.   Cuando se inicializa DM, recorrerá todos los , y cada vez que encuentre uno, comprobará si existe el correspondiente, y si no existe, creará un nuevo dispositivo con el nombre de . Es decir, se crea dinámicamente .uclassudeviceuclass_nodestruct udevice
inserte la descripción de la imagen aquí
driverdriverudevicedriverudevicedriver

uclassA diferencia de udevice, contiene la relación jerárquica de uno a muchos entre el dispositivo principal y el dispositivo secundario. Esta relación jerárquica corresponde a un nodo en el árbol de dispositivos y múltiples nodos secundarios.

  Se creará un dispositivo mediante una llamada 'bind', ya sea debido a U_BOOT_DRVINFO()la macro (en este caso, platno nula) o debido a un nodo en el árbol de dispositivos (en este caso, of_offset>= 0). En el último caso, traduciremos la información del árbol de dispositivos en el of_to_platmétodo plat(llamado antes del método de sondeo si el dispositivo tiene un nodo de árbol de dispositivos).

  La secuencia para hacer funcionar el dispositivo es bind ➜ of_to_plat(如果使用设备树)➜ probe. En comparación con el kernel de Linux, el modelo de controlador de U-Boot tiene un paso de sondeo/eliminación independiente del enlace/desenlace. Esto se debe a que muchos dispositivos no se usarán en U-Boot y el costo de Probe no vale la pena, así que cuando sea necesario Solo entonces el dispositivo Probe.

Acerca de los números de secuencia de dispositivos

  En la mayoría de los casos, U-Boot comienza a numerar dispositivos desde 0. Este número identifica de manera única un dispositivo dentro de su UCLASS, por lo que dos dispositivos dentro de una UCLASS en particular no pueden tener el mismo número de serie. Los números de serie comienzan en 0, pero se permiten espacios en blanco. Por ejemplo, una placa de desarrollo puede tener I2C1, I2C4, I2C5, pero no I2C0, I2C2, I2C3. La elección de cómo se numeran los dispositivos depende de la placa de desarrollo particular y, en algunos casos, puede ser establecida por el SoC.
inserte la descripción de la imagen aquí
El número de serie del dispositivo se resuelve cuando el dispositivo está vinculado, almacenado en udevice->seq_la variable miembro y no cambiará durante la vida útil del dispositivo.

  La ubicación donde el dispositivo obtiene el número de serie está controlada por DM_SEQ_ALIASun elemento de configuración , y esta opción puede tener diferentes valores en el propio U-Boot y en SPL. Si esta opción no está configurada, los alias se ignoran. Incluso si está habilitada CONFIG_DM_SEQ_ALIAS, una uclass aún debe tener DM_UC_FLAG_SEQ_ALIASel indicador para que sus dispositivos se clasifiquen por alias.

  Cuando se configuran estas opciones, los dispositivos con un alias (por ejemplo, "serial2") obtendrán este número de serie 2. Otros dispositivos obtienen el siguiente número disponible después de todos los alias y todos los números existentes. Esto significa que si solo hay un alias "serial2", los dispositivos serie sin alias se asignarán 3 y más allá, 0 y 1 no se utilizan.

  Si CONFIG_DM_SEQ_ALIASo DM_UC_FLAG_SEQ_ALIAS, todos los dispositivos obtendrán números de serie en orden simple a partir de 0. Para encontrar el siguiente número a asignar, el modelo de controlador busca el número existente más grande y luego usa el siguiente. No trata de llenar los vacíos.

controlador de estructura

  driverContiene métodos para crear nuevos dispositivos y eliminar dispositivos, los dispositivos se configuran con información proporcionada por platdata o nodos de árbol de dispositivos (emparejados al encontrar cadenas compatibles que coincidan con of_match).

struct driver {
    
    
	char *name;			/* 设备名字。在定义 driver 时指定的一个字符串 */
	enum uclass_id id;	/* 标记此驱动属于哪个 uclass 的 id,取值是 ./include/dm/uclass-id.h 中定义的 enum uclass_id  */
	const struct udevice_id *of_match;	/* 要匹配的 compatible 字符串列表 */
	int (*bind)(struct udevice *dev);	/* 绑定 device 到它的 driver 时被调用 */
	int (*probe)(struct udevice *dev);	/* 被调用来探测一个设备,即激活设备 */
	int (*remove)(struct udevice *dev);	/* 被调用来移除一个设备 */
	int (*unbind)(struct udevice *dev);	/* 调用来解除设备与其驱动程序的绑定 */
	int (*of_to_plat)(struct udevice *dev);			/* 在 probe 之前,解析对应 udevice 的 dts 节点,转化成 udevice 的平台数据(存放于 udevice->plat_ 中) */
	int (*child_post_bind)(struct udevice *dev);	/* 在一个新的 child 设备被绑定之后调用 */
	int (*child_pre_probe)(struct udevice *dev);	/* 在探测子设备之前调用。设备已分配内存,但尚未被探测。. */
	int (*child_post_remove)(struct udevice *dev);	/* 在移除子设备后调用。设备已经分配了内存,但是它的 device_remove() 方法已经被调用 */
	int priv_auto;		/* 如果非零,这是在 udevice->priv_ 指针中分配的私有数据的大小。如果为零,则驱动程序负责分配所需的任何数据。 */
	int plat_auto;		/* 如果非零,这是要分配到 udevice->plat_ 指针中的平台数据的大小。这通常只对支持设备树的驱动程序(使用 of_match 的驱动程序)有用,因为使用 platform data 的驱动程序将拥有 U_BOOT_DRVINFO() 实例化中提供的数据 */
	int per_child_auto;	/* 每个设备都可以保存其父设备拥有的私有数据。如果需要,如果该值非零,将自动分配到 udevice->parent_priv_ 指针中。 */
	int per_child_plat_auto;	/* 总线喜欢存储关于其子节点的信息。如果非零,这是该数据的大小,将分配到子对象的 udevice->parent_plat_ 指针中 */
	const void *ops;	/* driver 的具体操作,这通常是一个由 driver 定义的函数指针列表,用于实现 uclass 所需的驱动程序函数。 */
	uint32_t flags;		/* 驱动程序标志-参见' DM_FLAGS_…' */
#if CONFIG_IS_ENABLED(ACPIGEN)
	struct acpi_ops *acpi_ops;	/* 高级配置和电源接口(ACPI)操作,允许设备向传递给Linux的ACPI表中添加东西 */
#endif
};

  struct driverAmbos pertenecen a UCLASS, que representan una clase de dispositivos del mismo tipo. Los elementos comunes de los controladores se pueden implementar en UCLASS, o UCLASS puede proporcionar una interfaz coherente para los controladores dentro de ella. Seudevice asocia con su UCLASS según el atributo en .struct driverid

  En la implementación del código, driverse deben usar macros U_BOOT_DRIVER(__name)para la definición. Además de usar la variable struct uclass_driverde definición __nametambién definirá una sección con el mismo nombre y __namela colocará en la sección con el mismo nombre. Tome serialcomo ejemplo , como se muestra a continuación:
inserte la descripción de la imagen aquí
  driveres administrado por una lista vinculada struct udevicecomo un nodo, y drivercada uno debe estar asociado con un struct udeviceespecificado (en un nodo de lista vinculada). Cuando no hay un correspondientedriver , se creará uno automáticamente y luego se asociará .struct udevicestruct udevice
inserte la descripción de la imagen aquí

estructura udevice_id

  struct udevice_ides para controladores específicos para enumerar las cadenas de compatibilidad admitidas por el controlador. El controlador y el dispositivo en el árbol de dispositivos struct udevice_idespecífico compatiblecoinciden en.

struct udevice_id {
    
    
	const char *compatible;		/* 一个字符串 */
	ulong data;					/* 兼容字符串对应的数据,具体使用方式由驱动决定 */
};

  struct udevice_idLa función de es facilitar la definición de varias cadenas compatibles a la vez y, finalmente, asignarlas struct drivera of_matchlos miembros en . Entre ellos, el miembro compatiblese utiliza para compatiblehacer coincidir en el árbol de dispositivos; datase pasará a struct udevicein driver_data(consulte el proceso de inicialización más adelante).
inserte la descripción de la imagen aquí

Comando MD

  U-Boot proporciona comandos relacionados con DM en ./cmd/dm.cel archivo y puede ver información relacionada con DM en la interfaz de comandos de U-Boot. Después de ingresar al modo de línea de comandos de U-Boot, ingrese helpo ?, y luego presione Enter para ver todos los comandos compatibles actualmente con U-Boot de manera predeterminada. También puede ingresar help 命令名o ? 命令名para ver el uso detallado del comando, por ejemplo, help dmse imprimirá la introducción detallada de dmeste .
inserte la descripción de la imagen aquí

dm compat

  dm compatSe utiliza para mostrar las cadenas de compatibilidad asociadas con cada controlador (puede encontrar estas cadenas en el archivo de árbol de dispositivos de cada placa), una por línea si hay más de una.
inserte la descripción de la imagen aquí
El significado de cada columna es el siguiente:

nombre de la columna significado
Conductor El nombre del conductor, el valor driver->namede
Compatible Cadena de compatibilidad del controlador, el valor driver->of_matchde . Si el Compatibleárbol coincide aquí, significa que el dispositivo del nodo del árbol de dispositivos utiliza este controlador

dm devres

  dm devresSe utiliza para mostrar una lista de registros de devres (recursos de dispositivo) para un dispositivo. Algunos controladores usan la API de devres para asignar memoria de modo que cuando se quita el dispositivo, la memoria se libera automáticamente (no se requiere código en los remove()métodos ).

Esta función debe definirse CONFIG DEVRESpara habilitarla.

dm drivers

  dm driversSe utiliza para mostrar todos los controladores disponibles, la UCLASS correspondiente al controlador y la lista de dispositivos que utilizan el controlador (un dispositivo por línea cuando hay varios dispositivos) y un controlador por línea. Si el controlador no tiene un dispositivo correspondiente, el dispositivo se muestra como none.
inserte la descripción de la imagen aquí
El significado de cada columna es el siguiente:

nombre de la columna significado
Conductor El nombre del conductor, el valor driver->namede
fluido UID es el valor correspondiente enum uclass_iden
uclase UCLASS nombre, el valor uclass_driver->namede
Dispositivos Nombre del dispositivo, el valor udevice->namede

dm static

  dm staticSe utiliza para mostrar los dispositivos vinculados a los datos de la plataforma, es decir, no del árbol de dispositivos. Por lo general, no están disponibles, pero algunas placas pueden usar dispositivos estáticos por razones de espacio.

nombre de la columna significado
Conductor driver->nameEl nombre del controlador definido en
DIRECCIÓN dirección de memoria de la unidad

dm tree

  dm treeSe utiliza para mostrar el árbol completo de dispositivos.
inserte la descripción de la imagen aquí
El significado de cada columna es el siguiente:

nombre de la columna significado
Clase El nombre UCLASS del dispositivo, es decir, el valor uclass_driver->namede
Índice El número de índice del dispositivo en UCLASS. Tenga en cuenta que no es el número de secuencia.
probado Si el dispositivo está activo, muestra+
Conductor El nombre del controlador utilizado por este dispositivo, el valor driver->namede
Nombre Mostrar el nombre del dispositivo (es decir, el valor udevice->namede

dm uclass

  dm uclassSe utiliza para mostrar cada clase y una lista de dispositivos en esa clase.
inserte la descripción de la imagen aquí

container_of

  En DM, los dispositivos se administran a través de una lista vinculada y la administración de la lista vinculada scripts/kconfig/list.hutiliza container_ofla macro definida en . El de U-Boot container_ofestá tomado de Linux, el diseño de esta macro es bastante interesante, por lo que debemos centrarnos en analizarlo. A primera vista, esta macro no es complicada, solo un bloque de código ( {}) y dos declaraciones independientes ( ;).
inserte la descripción de la imagen aquí

const typeof( ((type *)0)->member ) *__mptr = (ptr);

  typeofEs una palabra clave para obtener el tipo de miembro. Por lo tanto, la primera mitad de la oración es const typeof( ((type *)0)->member )en realidad para obtener memberel tipo de , y toda la oración es memberpara definir la variable de puntero _mptrcon el tipo de y asignarle un valor ptr, ptrque en realidad es un puntero member a .

(type *)( (char *)__mptr - ((size_t) &((type *)0)->member) );

  1. (char *)__mptrConvierte de forma coercitiva el tipo de miembro a char *, lo que requiere que la dirección se agregue o reste en bytes
  2. offsetofSe utiliza para obtener el desplazamiento del miembro de la estructura. Este es un uso inteligente Sabemos que la dirección del miembro de la estructura menos la dirección base de la estructura es el desplazamiento. Y si la dirección base es 0, entonces obtener directamente la dirección del miembro es el desplazamiento.
  3. (char *)__mptr - ((size_t) &((type *)0)->member) Es para obtener la primera dirección de typela variable de estructura , pero el tipo es char*, y finalmente se usa (type *)para convertir type al puntero de tipo.

en conclusión

  container_ofEl propósito final es devolver la dirección base de la estructura memberdonde se encuentra . En pocas palabras, container_ofla función es obtener la dirección base de la estructura según los miembros de la estructura. Y es const typeof( ((type *)0)->member ) *__mptr = (ptr);solo un estado intermedio, sin esta oración, container_ofno se puede lograr la universalidad de (la alternativa es usar el tipo coerción, pero solo se puede usar en un tipo específico).
inserte la descripción de la imagen aquí

proceso de inicialización

  Como se mencionó anteriormente uclass, udevicese crean dinámicamente, en función de los dispositivos definidos por Device Tree y Platform Data en el controlador U-Boot. El proceso de inicialización atravesará los dispositivos creados por Platform Data y Device Tree, y creará y asociará udevice, uclass y controladores relacionados.

  1. De acuerdo con la información del dispositivo encontrado (dispositivo de datos de plataforma nameo árbol de dispositivos compatible) para atravesar driverpara que coincida con sus miembros char *name;o const struct udevice_id *of_match;, si no hay un controlador, simplemente abandone
  2. driverDespués de encontrar , continúe buscando eldriver miembro correspondiente según el miembro . Si existe , devuelva el encontrado , de lo contrario, continúe recorriendo los miembros para que coincida con sus miembros , y abandone si no hayenum uclass_id iduclassuclassuclassdriverenum uclass_id iduclass driverenum uclass_id id;
  3. uclass driverUna vez que se encuentra a , se crea a uclass, uclassy el miembro de struct uclass_driver *uc_drv;apunta al encontrado uclass driver, y se devuelve el recién creado uclass.
  4. Finalmente, cree uno udevicecon miembros const struct driver *driver;que apunten a lo encontrado drivery miembros struct uclass *uclass;que apunten a lo obtenido anteriormente uclass.

  Cabe señalar aquí que el dispositivo es una estructura de árbol que contiene relaciones jerárquicas.Sin embargo, la inicialización de DM solo es responsable de atravesar todos los dispositivos de primer nivel, y los dispositivos de siguiente nivel son atravesados ​​por el método correspondiente uclassal .post_bindLa uclassmayoría .post_bindllamará directa o indirectamente dm_scan_fdt_devpara atravesar los subdispositivos del dispositivo actual. Algunos nodos no están habilitados y los nodos que no están habilitados no tendrán los dispositivos correspondientes.
inserte la descripción de la imagen aquí
  La interfaz para la inicialización de DM está dm_init_and_scanen./common/board_f.cen el archivo llamado antes de la reubicación static int initf_dm(void)y./common/board_r.cen el archivostatic int initr_dm(void). La inicialización después de la reubicación no es muy diferente de antes de la reubicación.
inserte la descripción de la imagen aquí
  U-Boot proporciona la función de etapa de arranque (./common/bootstage.c) para registrar información como el tiempo de ejecución de cada etapa, y esta información registrada se puede informar al usuario y pasar al sistema operativo para el registro/análisis adicional. bootstageNo está habilitado de forma predeterminadaLo que realmente está relacionado con la inicialización de DM esdm_init_and_scanque nos centraremos en esta función a continuación.

dm_init_and_scan

  dm_init_and_scanDefinido drivers/core/root.cen pre_reloc_only, cuando el parámetro de entrada es verdadero, significa que solo se analizarán los nodos antes de la reubicación (solo u-boot,dm-pre-relocse DM_FLAG_PRE_RELOCanalizarán los nodos con el atributo en el árbol de dispositivos o los dispositivos con la bandera); pre_reloc_onlysi es falso, todos los nodos se analizarán parse.
inserte la descripción de la imagen aquí
  Se puede ver a partir de los parámetros de entrada dm_init_and_scande que los parámetros de entrada son verdaderos antes de la reubicación, por lo que se analizarán menos nodos. Además, OF_LIVEes un árbol dinámico, que no está habilitado por defecto, porque of-platdatasolo SPL/TPLdisponible en el escenario, por lo que todos los códigos of-platdatarelacionados . DM_EVENTNo lo habilito por defecto aquí, solo ignóralo.

dm_init

  dm_initDefinido drivers/core/root.cen y utilizado principalmente para inicializar ./drivers/core/root.cel dispositivo raíz ( ) definido en U_BOOT_DRIVER(root_driver). El dispositivo raíz no se define mediante el árbol de dispositivos, sino que se define directamente en el código (es decir, el método de datos de plataforma).
inserte la descripción de la imagen aquí

Todos los dispositivos son hijos del dispositivo raíz. La única función del dispositivo raíz es administrar otros dispositivos

  dm_initEl parámetro de entrada of_liveindica si Live Device Tree está habilitado y el código fuente muestra que este parámetro de entrada no se ha utilizado. Live Device Tree es un concepto que corresponde a Flattened Device Tree. Se utiliza principalmente para acelerar el tiempo de escaneo de inicio, pero solo se puede usar después de la reubicación.

  of-platdataLa función no está habilitada, por lo que el código en else se ejecuta directamente, gd->uclass_rootseñalando gd->uclass_root_sy luego inicializando los miembros gd->uclass_rooten : gd->uclass_root.next = gd->uclass_rooty gd->uclass_root.prev = gd->uclass_root.
inserte la descripción de la imagen aquí

device_bind_by_name

  device_bind_by_nameSe drivers/core/device.cdefine y se utiliza principalmente para enlazar dispositivos que no están definidos mediante el árbol de dispositivos. Esta interfaz se utiliza para crear un dispositivo y vincularlo a un controlador. Para la inicialización de DM, el dispositivo ROOT se creará aquí ./drivers/core/root.cy U_BOOT_DRIVER(root_driver)se vinculará al dispositivo definido en .

lists_driver_lookup_name

  lists_driver_lookup_nameDefinido drivers/core/device.cen , atravesará todas struct driverlas secciones correspondientes y coincidirá con el nombre del controlador especificado (parámetro de entrada). Para la inicialización del dispositivo raíz aquí, el parámetro de entrada namees el valor del nombre ./drivers/core/root.cdefinido U_BOOT_DRIVER(root_driver)en root_drivery eventualmente devolverá la dirección base 0x804b438.
inserte la descripción de la imagen aquí

device_bind_common

  device_bind_commonDefinida drivers/core/device.c/en , la función es vincular el controlador de dispositivo, el dispositivo y UCLASS ( root_driverel dispositivo inicializado por DM y U_BOOT_DRIVER(root_driver)) UCLASS_ROOT, de acuerdo con el proceso de inicialización anterior, solo cuando hay un controlador de dispositivo, se creará el dispositivo correspondiente.

  1. Llame para encontrar el UCLASS correspondiente uclass_getsegún la identificación del dispositivo. Esto incluye las operaciones relacionadas con el procesamiento de UCLASS, que se describirán en detalle más adelante.
  2. dev = calloc(1, sizeof(struct udevice));Solicite una memoria udevicede nodo , es decir, cree un dispositivo (durante la inicialización de DM, se creará aquí ROOT DEVICE) y luego inicialice los nodos de la lista vinculada en él.
    inserte la descripción de la imagen aquí
    1. INIT_LIST_HEADSe usa para apuntar cada nodo de la lista vinculada a sí mismo
    2. dev_set_plateso esdev->plat_ = plat;
    3. Algunos dispositivos, como el bus SPI, el bus I2C y los puertos serie, se numeran mediante alias. Por lo tanto, debe extraerse del nodo del árbol de dispositivos y colocarse dev->seq_en .
      inserte la descripción de la imagen aquí
  3. Inicialice parte de la memoria utilizada por el dispositivo actual, como el archivo plat_. Tenga en cuenta que el espacio de memoria solo se asigna aquí y el contenido lo completa el controlador específico.
    	/* Check if we need to allocate plat */
    	if (drv->plat_auto) {
          
           /* 当驱动中 将 plat_auto 设置为实际的 platform data 的大小 */
    		bool alloc = !plat; /* plat 是入参,如果入参指定了,则就不会在分配驱动中 drv->plat_auto 的内存空间 */
    
    		/*
    		 * For of-platdata, we try use the existing data, but if
    		 * plat_auto is larger, we must allocate a new space
    		 */
    		if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
          
          
    			if (of_plat_size)
    				dev_or_flags(dev, DM_FLAG_OF_PLATDATA);
    			if (of_plat_size < drv->plat_auto)
    				alloc = true;
    		}
    		if (alloc) {
          
          
    			dev_or_flags(dev, DM_FLAG_ALLOC_PDATA);
    			ptr = calloc(1, drv->plat_auto);	/* 分配内存 */
    			if (!ptr) {
          
          
    				ret = -ENOMEM;
    				goto fail_alloc1;
    			}
    
    			/*
    			 * For of-platdata, copy the old plat into the new
    			 * space
    			 */
    			if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat)
    				memcpy(ptr, plat, of_plat_size);
    			dev_set_plat(dev, ptr);	/* dev->plat_ = plat; */
    		}
    	}
    
  4. El dispositivo actual puede optar por guardar algunos datos de la plataforma en el UCLASS correspondiente uclass_plat_a . Si el correspondiente uclass->per_device_plat_autono es 0, solicite la memoria y llame a dev_set_uclass_plat(dev, ptr);la asignación dev->uclass_plat_ = uclass_plat;para apuntar a la memoria solicitada. Tenga en cuenta que el espacio de memoria solo se asigna aquí y el contenido lo completa el controlador específico.
    	size = uc->uc_drv->per_device_plat_auto;
    	if (size) {
          
          
    		dev_or_flags(dev, DM_FLAG_ALLOC_UCLASS_PDATA);
    		ptr = calloc(1, size);
    		if (!ptr) {
          
          
    			ret = -ENOMEM;
    			goto fail_alloc2;
    		}
    		dev_set_uclass_plat(dev, ptr);
    	}
    
  5. Si el dispositivo actual tiene un dispositivo de nodo principal, inicialice el dispositivo de nodo principal per_child_plat_autoy luego llame list_add_tailpara agregar un nuevo dispositivo a su dispositivo de nodo principal. Para nuestro dispositivo ROOT, su padre es NULL, por lo que no es necesario agregarlo. Cuando parent no es NULL, el dispositivo se conectará a Parent a través child_headde estos dos miembros, y habrá diagramas detallados más adelante.sibling_node
  6. uclass_bind_deviceLlamar Agregue el dispositivo creado anteriormente a UCLASS y, al mismo tiempo, si el dispositivo tiene un dispositivo principal, debe llamar child_post_bindal método del dispositivo principal (para la inicialización de DM aquí, el dispositivo raíz no tiene dispositivo principal). Directamente en la imagen:
    inserte la descripción de la imagen aquí
  7. Llame al bindmétodo para completar el enlace entre el dispositivo y el controlador correspondiente, luego llame al dispositivo principal correspondiente al dispositivo actual child_post_bind(en realidad es lo mismo que uclass_bind_deviceen ), y finalmente llame al métodouclass correspondiente al actual dispositivo (uclass_driver correspondiente) Por lo general, en El método atravesará los subdispositivos del dispositivo actual por turnos (es decir, los subnodos del nodo actual en el árbol de dispositivos correspondiente) y vinculará todos los subdispositivos al dispositivo principal actual. El método se llama directa o indirectamente y luego enlaza .post_bind
    inserte la descripción de la imagen aquí
    uclasspost_binddm_scan_fdt_dev ➜ dm_scan_fdt_nodelists_bind_fdt

uclass_get

  El dispositivo debe pertenecer a UCLASS, uclass_getque se define drivers/core/uclass.cen gd->uclass_root, para recorrer la lista vinculada de uclass a la que apunta el id de uclass en udevice y devolver la dirección de uclass encontrada; si no se encuentra, crear una nueva uclass y devolver la dirección de uclass recién creada .

  1. Llame para uclass_findrecorrer gd->uclass_rootla lista enlazada de uclass a la que apunta para encontrar la uclass con la identificación especificada. Para la inicialización de DM, dado que gd->dm_rootes NULL, en realidad no se ejecuta lis_for_each_entry; de lo contrario, la expansión es la siguiente:
    inserte la descripción de la imagen aquí
  2. Cuando no se puede encontrar la uclass con la identificación especificada, llame para uclass_addcrear una nueva uclass
    1. Llame a lists_uclass_lookupfind uclass_driver, devuelva la uclass_driverdirección , de lo contrario devuelva un error
      inserte la descripción de la imagen aquí
    2. Cree una nueva uclass, luego realice una serie de inicializaciones y finalmente devuelva la uclass recién creada.
      inserte la descripción de la imagen aquí
      1. uc = calloc(1, sizeof(*uc));Solicitar una memoria de nodo UCLASS
      2. Juicio uclass_driver->priv_autoSolicitar uclaas->priv_ espacio de memoria (la interfaz uclass_set_prives una declaración de asignación simple uc->priv_ = priv;).
      3. INIT_LIST_HEAD()Se utiliza dev_headpara sibling_nodeseñalar punteros en y hacia sí mismos.
      4. list_addResponsable de serializar la memoria de nodo UCLASS solicitada a gd->uclass_rootla lista enlazada
      5. Determine y uclassllame a la interfazuclass_driver correspondiente a la actual:inituc_drv->init

dev_set_ofnode

  OF_CONTROLIndica si el árbol de dispositivos está habilitado, que está habilitado de forma predeterminada, por lo que continuará llamando al drivers/core/device.cdefinido dev_set_ofnodey node_apuntando nodo raíz.
inserte la descripción de la imagen aquí

device_probe

  Para ahorrar recursos, los dispositivos en U-Boot se prueban tarde. Muchos dispositivos nunca se usan durante la ejecución de U-Boot, probarlos lleva tiempo, requiere memoria, puede aumentar la demora del bucle principal, etc., por lo que U-Boot no probará dispositivos de manera proactiva a menos que sea necesario. De acuerdo con el código, el dispositivo raíz se inicializa y device_probese llama .

  1. Los dispositivos deben ser probados por código uclass o código de dispositivo genérico (por ejemplo device_find_global_by_ofnode()).
  2. Para activar un dispositivo, U-Boot primero lee los datos de información del dispositivo ( of_to_plat), si hay un dispositivo principal, el dispositivo principal debe activarse primero

  device_probeDefinido drivers/core/device.cen , se utiliza para activar un dispositivo para que pueda ser utilizado en cualquier momento.Para ahorrar recursos, el dispositivo en U-Boot se detectará con un retraso. Si el dispositivo ya está activado, vuelve directamente. Para la inicialización de DM, aquí es donde se prueba y activa el dispositivo raíz.

  1. device_of_to_platLlamada Convierte la información en dts a los datos de la plataforma del dispositivo, para proporcionar la información necesaria para operaciones como la detección del dispositivo. Esto puede hacer que se prueben otros dispositivos de los que depende el dispositivo actual, por ejemplo, una línea GPIO hará que se pruebe un dispositivo GPIO.
    1. Si el dispositivo actual tiene un dispositivo principal, ejecute recursivamente el dispositivo principal.device_of_to_plat
      inserte la descripción de la imagen aquí
    2. Llamado para device_alloc_privasignar la memoria utilizada por varios datos en poder de este dispositivo. Esto es solo para asignar espacio en la memoria, y el contenido real lo completan los controladores específicos.
      static int device_alloc_priv(struct udevice *dev)
      {
              
              
      	const struct driver *drv;
      	void *ptr;
      	int size;
      
      	drv = dev->driver;
      	assert(drv);
      
      	/* Allocate private data if requested and not reentered */
      	if (drv->priv_auto && !dev_get_priv(dev)) {
              
              
      		ptr = alloc_priv(drv->priv_auto, drv->flags);
      		if (!ptr)
      			return -ENOMEM;
      		dev_set_priv(dev, ptr);
      	}
      
      	/* Allocate private data if requested and not reentered */
      	size = dev->uclass->uc_drv->per_device_auto;
      	if (size && !dev_get_uclass_priv(dev)) {
              
              
      		ptr = alloc_priv(size, dev->uclass->uc_drv->flags);
      		if (!ptr)
      			return -ENOMEM;
      		dev_set_uclass_priv(dev, ptr);
      	}
      
      	/* Allocate parent data for this child */
      	if (dev->parent) {
              
              
      		size = dev->parent->driver->per_child_auto;
      		if (!size)
      			size = dev->parent->uclass->uc_drv->per_child_auto;
      		if (size && !dev_get_parent_priv(dev)) {
              
              
      			ptr = alloc_priv(size, drv->flags);
      			if (!ptr)
      				return -ENOMEM;
      			dev_set_parent_priv(dev, ptr);
      		}
      	}
      
      	return 0;
      }
      
      1. if (drv->priv_auto && !dev_get_priv(dev))Se basa en drv->priv_autola asignación dev->priv_de espacio de memoria. ¡Estos son los datos privados del dispositivo!
      2. if (size && !dev_get_uclass_priv(dev))Se basa en dev->uclass->uc_drv->per_device_autoel dev->uclass_priv_espacio asignado, que se utiliza para guardar los datos privados de su UCLASS propiedad del dispositivo.
      3. if (size && !dev_get_parent_priv(dev))Se utiliza para guardar los datos de su dispositivo principal propiedad del dispositivo actual de acuerdo con dev->parent->driver->per_child_autoel espacio asignado . Tenga en cuenta que solo se puede seleccionar uno de los datos privados del dispositivo principal y los datos privados de la uclass del dispositivo principal, y solo los datos privados del dispositivo principal se guardan de manera predeterminada.dev->parent_priv_
    3. Llame al controlador correspondiente al propio dispositivo of_to_platy convierta la información del dispositivo descrita en el árbol del dispositivo en datos de la plataforma (almacenados en la memoria udevice ->plat_a la que apunta ), y el controlador subsiguiente obtendrá los recursos relevantes de los datos de la plataforma al usar el hardware.
      inserte la descripción de la imagen aquí
  2. Si el dispositivo tiene un dispositivo principal, pruebe primero el dispositivo principal para asegurarse de que se prueben todos los dispositivos principales. device_probeimplementación recursiva directa
    inserte la descripción de la imagen aquí
  3. Aquí if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)hay que configurar el PINCTRL correspondiente de este dispositivo, pero debe tenerse en cuenta que debido a que el dispositivo PINCTRL puede no haber sido Probe, este es solo el estado predeterminado.
  4. Llame para device_get_dma_constraintsllenar las restricciones de DMA del dispositivo. Obtenga las restricciones de DMA del dispositivo del firmware. El controlador luego usa esta información para traducir la dirección física al espacio de direcciones del bus del dispositivo. Actualmente, solo se admite el árbol de dispositivos.
  5. Llame a la interfaz que debe ejecutarse en uclass (uclass_driver correspondiente) correspondiente al dispositivo actual antes de uclass_pre_probe_deviceejecutar el dispositivo de detección.
    1. pre_probe()El método en uclass (uclass_driver correspondiente) correspondiente al dispositivo actual
      inserte la descripción de la imagen aquí
    2. child_pre_probe()El método en uclass (uclass_driver correspondiente) correspondiente al dispositivo principal del dispositivo actual
      inserte la descripción de la imagen aquí
  6. Se llama el dispositivo principal del dispositivo actual child_pre_probe.
  7. La llamada dev_has_ofnodesolo procesa el reloj predeterminado para dispositivos con un ofnode válido (la frecuencia de reloj definida en el nodo del dispositivo)
  8. Ejecute la función de sondeo del controlador del dispositivo para activar realmente el dispositivo.
    inserte la descripción de la imagen aquí
  9. Llame a la interfaz que debe ejecutarse en uclass (uclass_driver correspondiente) después de uclass_post_probe_deviceejecutar el dispositivo de detección. Esto incluye post_probe()métodos y child_post_probe()métodos de uclass padre (el uclass_driver correspondiente).
    1. child_post_probe()El método en uclass (uclass_driver correspondiente) correspondiente al dispositivo principal del dispositivo actual
      inserte la descripción de la imagen aquí
    2. post_probe()El método en uclass (uclass_driver correspondiente) correspondiente al dispositivo actual
      inserte la descripción de la imagen aquí
  10. Si actualmente es un dispositivo PINCTRL, entonces para los nodos con pinctrl-names = "default";atributos , PINCTRL se configurará automáticamente aquí

  Tenga en cuenta que aquí solo estoy explicando la llamada en el nodo raíz.Otros device_probedispositivos no lo llamarán activamente cuando se inicialice, pero lo llamarán cuando realmente se necesite. Vea dm_scanel análisis .

dm_scan

  dm_scanSe define drivers/core/root.cen es responsable de inicializar todos los dispositivos excepto el dispositivo raíz. Como se mencionó anteriormente, U-Boot admite dos formas básicas de configuración del controlador, datos de plataforma (a menudo denominado plat en el código) y árbol de dispositivos plano (a menudo denominado fdt en el código). Por lo tanto, los dispositivos definidos por estos dos controladores deben tratarse aquí.

dm_scan_plat

  dm_scan_platDefinido drivers/core/root.cen , busca y vincula U_BOOT_DRVINFO(__name)dispositivos definidos directamente mediante . U_BOOT_DRVINFO(__name)Definido ./include/dm/platdata.hen , U_BOOT_DRIVER(__name)similar a , U_BOOT_DRVINFO(__name)excepto que se usa struct driver_infopara definir variables __name, y también define una sección con el mismo nombre, y __namela coloca en esta sección.
inserte la descripción de la imagen aquí
  dm_scan_platDe hecho, es recorrer driver_infola tabla ( hasta__u_boot_list_2_driver_info_1 ) y luego usar los miembros para encontrar los controladores definidos por . El establecimiento del controlador, el establecimiento de UCLASS y el enlace de uclass_driver son los mismos que el dispositivo raíz anterior, y las acciones internas finalmente se llaman para completar esta serie de acciones (los parámetros son diferentes cuando se llama). Tenga en cuenta que todos los nodos están relacionados ! ! !__u_boot_list_2_driver_info_3driver_infonameU_BOOT_DRIVER(__name)
inserte la descripción de la imagen aquí
  udevicedevice_bind_by_namedevice_bind_by_name
inserte la descripción de la imagen aquí
gd->dm_root

dm_extended_scan

  dm_extended_scanDefinido drivers/core/root.cen , maneja los dispositivos definidos en el árbol de dispositivos. De acuerdo con el código fuente, dm_extended_scanincluye principalmente dos partes: primero, dm_scan_fdtprocesar los nodos del dispositivo en el árbol de dispositivos a través de ; segundo, debido a que algunos nodos ( /chosen, /clocks, /firmware) no son dispositivos en sí mismos, pueden contener algunos dispositivos. En este momento, dm_scan_fdt_ofnode_pathuse para procesar los dispositivos en estos nodos uno por uno.
inserte la descripción de la imagen aquí
  ofnode_rootEs el nodo raíz ( of_offset=0), y ofnode_pathesto es para encontrar el nodo del árbol de dispositivos especificado de acuerdo con la ruta completa.Estas dos partes finalmente se proporcionan dm_scan_fdt_nodepara procesar el dispositivo del nodo, por lo que solo debemos centrarnos dm_scan_fdt_nodeen .

dm_scan_fdt_node

  dm_scan_fdt_nodeDefinido drivers/core/root.cen , implementa la exploración del árbol de dispositivos y la vinculación de controladores para nodos. Creará un nuevo udevice para el nodo del árbol de dispositivos enlazados y utilizará el parámetro de entrada principal como su dispositivo principal (el parámetro de entrada se fija para que sea gd->dm_rootel dispositivo raíz).

   dm_scan_fdt_nodeComenzará desde el nodo raíz y atravesará todos los nodos secundarios a su vez. Dado que el dispositivo raíz se ha inicializado por separado antes, el dispositivo que se encuentra aquí es el primer dispositivo debajo del nodo raíz y luego se usa para vincular los nodos lists_bind_fdtuno por uno enlace aquí es para conectar el nodo con el correspondiente udevice, controlador, uclass , uclass_driver vinculado).

	for (node = ofnode_first_subnode(parent_node);	/* 以根节点开始,获取第一个子节点 */
	     ofnode_valid(node);
	     node = ofnode_next_subnode(node)) {
    
    		/* 当前节点的子节点 */
		const char *node_name = ofnode_get_name(node); /* 节点名字 */

		if (!ofnode_is_enabled(node)) {
    
    
			pr_debug("   - ignoring disabled device\n");
			continue;
		}
		err = lists_bind_fdt(parent, node, NULL, NULL, pre_reloc_only); /* 绑定 udevice、driver、uclass */
		if (err && !ret) {
    
    
			ret = err;
			debug("%s: ret=%d\n", node_name, ret);
		}
	}
lists_bind_fdt

  lists_bind_fdtnodeSe creará un udevice internamente, y luego el udevice se vinculará al nodo actual (parámetro de entrada ). Por supuesto, al crear udevice, se vinculará su controlador correspondiente, uclass, uclass_driver (si no hay uclass, se creará uno nuevo).

  1. Obtenga compatibleel contenido (dirección base + longitud), si no hay un nodo de dispositivo compatible, este nodo se ignorará.
    compat_list = ofnode_get_property(node, "compatible", &compat_length);
    if (!compat_list) {
          
          
    	if (compat_length == -FDT_ERR_NOTFOUND) {
          
          
    		log_debug("Device '%s' has no compatible string\n",
    			  name);
    		return 0;
    	}
    
    	dm_warn("Device tree error at node '%s'\n", name);
    	return compat_length;
    }
    
  2. Traverse compatible, y luego ir a driverla tabla ( to__u_boot_list_2_driver_1 ) para comparar uno por uno (el nodo coincide con el controlador ). El código es muy simple, es un bucle de dos capas.__u_boot_list_2_driver_3compatibleof_match
    for (i = 0; i < compat_length; i += strlen(compat) + 1) {
          
          
    	compat = compat_list + i;
    	log_debug("   - attempt to match compatible string '%s'\n",
    		  compat);
    
    	for (entry = driver; entry != driver + n_ents; entry++) {
          
          
    		if (drv) {
          
          
    			if (drv != entry)
    				continue;
    			if (!entry->of_match)
    				break;
    		}
    		/* 这里就会返回 匹配的 struct udevice_id */
    		ret = driver_check_compatible(entry->of_match, &id,
    					      compat);
    		if (!ret)
    			break;
    	}
    
      Siempre que haya una coincidencia, llame a device_bind_with_driver_data➜completar eldevice_bind_common establecimiento del controlador, el establecimiento de UCLASS, el enlace de uclass_driver ( los parámetros son diferentes al llamar). Entre ellos está el partido, que se asigna al interior .   Tenga en cuenta que todos los nodos están relacionados ! ! ! De esta forma, todos los nodos estarán conectados por padre. Además, el parámetro de entrada plat es NULL, es decir, los datos de plat del dispositivo del árbol de dispositivos se extraen del árbol de re-dispositivo subsiguiente.udevicedevice_bind_commonid->datastruct udevice_iddevice_bind_commonstruct udevicedriver_data
    inserte la descripción de la imagen aquí
    gd->dm_rootdevice_bind_common
    inserte la descripción de la imagen aquí

dm_scan_other

  dm_scan_otherDefinido drivers/core/root.cen , se usa para buscar y vincular dispositivos especiales que no son visibles para el DM. Esta función es una __WEAKfunción sin sustancia y debe implementarse si es necesario.
inserte la descripción de la imagen aquí
  Hay una implementación predeterminada de esta interfaz en ./lib/efi/efi_app.cy ./boot/bootstd-uclass.c, la primera es para buscar dispositivos UEFI disponibles en U-Boot (crear un dispositivo de bloque en U-Boot), y la última se usa para vincular dispositivos con método de boor. No uso efi para comenzar aquí, por lo que se ejecutará ./boot/bootstd-uclass.cde forma dm_scan_other.

  Podríamos definir manualmente todos los dispositivos requeridos en el árbol de dispositivos, pero esto no es obligatorio. ./boot/bootstd-uclass.cEl dm_scan_otherin bootdevcreará automáticamente el dispositivo bootstd si no se encuentra en el árbol de dispositivos. Si no se encuentran dispositivos bootmeth en absoluto, crea uno para cada controlador bootmeth disponible.

Para obtener detalles sobre esta parte, consulte Bota estándar U-Boot

dm_probe_devices

  dm_probe_devicesDefinido drivers/core/root.cen , realice el recorrido gd->dm_rootde todos los dispositivos debajo y luego active el dispositivo. Como se mencionó anteriormente, todos los dispositivos U-Boot se inicializan con un retraso, y todos los dispositivos con DM_FLAG_PROBE_AFTER_BINDla bandera ; de lo contrario, el dispositivo se probará solo cuando se use más tarde.

static int dm_probe_devices(struct udevice *dev, bool pre_reloc_only)
{
    
    
	u32 mask = DM_FLAG_PROBE_AFTER_BIND;
	u32 flags = dev_get_flags(dev);
	struct udevice *child;
	int ret;

	if (pre_reloc_only)
		mask |= DM_FLAG_PRE_RELOC;

	if ((flags & mask) == mask) {
    
    
		ret = device_probe(dev);
		if (ret)
			return ret;
	}

	list_for_each_entry(child, &dev->child_head, sibling_node)
		dm_probe_devices(child, pre_reloc_only);

	return 0;
}
  1. Si el dispositivo tiene DM_FLAG_PROBE_AFTER_BINDel indicador , device_probela sonda de llamada activa el dispositivo actual. Mencioné anteriormente que el dispositivo ROOT se inicializa por separado y la sonda.

  2. Llame list_for_each_entry(child, &dev->child_head, sibling_node)a la variable todos los nodos secundarios del dispositivo actual. Como se mencionó anteriormente, está conectado udeviceen serie child_headcon dos miembros, por lo que esta interfaz se atraviesa .sibling_nodelist_for_each_entrychild_headsibling_node
    inserte la descripción de la imagen aquí

    1. list_entry()La función de es obtener el puntero de toda la estructura a través del puntero conocido al subelemento miembro.
    2. prefetch()Dígale a la CPU los elementos que se utilizarán a continuación y búsquelos previamente para aumentar la velocidad
    3. &pos->member !=(head)es la condición final fordel ciclo

    La siguiente es la forma básica después de expandirse completamente
    inserte la descripción de la imagen aquí

  A lo que debemos prestar atención es a la llamada recursiva aquí dm_probe_devicesPara cada subdispositivo, la llamada recursiva aquí atravesará el subdispositivo del subdispositivo, y así sucesivamente. La condición final es volver a la capa anterior hasta que no quede ningún subequipo. El siguiente dm_probe_deviceses el recorrido de cada dispositivo.
inserte la descripción de la imagen aquí

referencia

  1. https://blog.csdn.net/ZHONGCAI0901/article/details/117781158
  2. https://zhuanlan.zhihu.com/p/460754843
  3. https://www.cnblogs.com/YYFaGe/p/16672483.html
  4. https://blog.csdn.net/weixin_41028621/article/details/90643550
  5. https://blog.csdn.net/ooonebook/article/details/53234020
  6. https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html

Supongo que te gusta

Origin blog.csdn.net/ZCShouCSDN/article/details/128600865
Recomendado
Clasificación