[uboot]使能spi flash,支持sf命令

0 基本框架

sf及mtd在uboot中的基本框架如上图所示,

 sf及mtdparts命令就是以上述框架为基本的函数调用流程。

1 目标

 可以使用sf*命令烧写QSPI

2 具体配置

2.1 SF命名开启相关的宏

#
# Device access commands
#
CONFIG_CMD_SF=y  #sf命令开启宏
CONFIG_CMD_SF_TEST #sf命令测试使能
# CONFIG_CMD_SPI is not set

#
# SPI Flash Support
#
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH=y
CONFIG_SF_DEFAULT_BUS=2
CONFIG_SF_DEFAULT_CS=0
CONFIG_SF_DEFAULT_MODE=0x0
CONFIG_SF_DEFAULT_SPEED=100000
#CONFIG_SPI_FLASH_BAR=y
#CONFIG_SPI_FLASH_UNLOCK_ALL=y
CONFIG_SPI_FLASH_USE_4K_SECTORS=y
CONFIG_SPI_FLASH_MICRO=y #自定义宏,用于添加镁光的Nor Flash

#
# SOC (System On Chip) specific Drivers
#
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_SPI_MEM=y
CONFIG_DESIGNWARE_SPI=y

#
# dump dm info
#
CONFIG_CMD_DM=y #使能驱动框架调试命令


2.2 添加相关spi flash info

源码路径:u-boot\drivers\mtd\spi\spi-nor-ids.c

const struct flash_info spi_nor_ids[] = {
    CONFIG_SPI_FLASH_ATMEL
    
    ...

	#ifdef CONFIG_SPI_FLASH_MICRO
	/* MICRON (Micron Technology, Inc.) */
	{ INFO("MT25QU256A", 0x20BB19, 0, 64 * 1024, 512,
	SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
	SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
	},
	#endif
}

2.3 DTS配置

&spi0 {
       status = "okay";
};
&spi1 {
       status = "okay";
};
&spi2 {
       status = "okay";
};
&spi3 {
       status = "okay";
};

spi0: spi@e0009000 {
		compatible = "snps,dw-apb-ssi-4.02a";
		reg = <0 0xe0009000 0x0 0x1000>;
		/*clocks = <&nb_periph_clk 7>;*/
		status = "disabled";
};

spi1: spi@e002a000 {
		compatible = "snps,dw-apb-ssi-4.02a";
		reg = <0 0xe002a000 0x0 0x1000>;
		/*clocks = <&nb_periph_clk 7>;*/
		status = "disabled";
};

spi2: spi@e002b000 {
		compatible = "snps,dw-apb-ssi-4.02a";
		reg = <0 0xe002b000 0x0 0x1000>;
		#address-cells = <1>;
		#size-cells = <1>;

		/*clocks = <&nb_periph_clk 7>;*/
		status = "disabled";

		flash: mt25qu256@0 {
				compatible = "jedec,spi-nor";
				spi-max-frequency = <100000>;
				reg = <0 0>;
		};
};

spi3: spi@e002c000 {
		compatible = "snps,dw-apb-ssi-4.02a";
		reg = <0 0xe002c000 0x0 0x1000>;
		/*clocks = <&nb_periph_clk 7>;*/
		status = "disabled";
};


3 SF命令代码实现

3.1 do_spi_flash

文件路径:u-boot\cmd\sf.c

3.1.1 U_BOOT_CMD

U_BOOT_CMD(
	sf, 5, 1, do_spi_flash,
	"SPI flash sub-system",
	"probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n"
	" and chip select\n"
	"sf read addr offset|partition len - read `len' bytes starting at\n"
	" `offset' or from start of mtd\n"
	" `partition'to memory at `addr'\n"
	"sf write addr offset|partition len - write `len' bytes from memory\n"
	" at `addr' to flash at `offset'\n"
	" or to start of mtd `partition'\n"
	"sf erase offset|partition [+]len - erase `len' bytes from `offset'\n"
	" or from start of mtd `partition'\n"
	" `+len' round up `len' to block size\n"
	"sf update addr offset|partition len - erase and write `len' bytes from memory\n"
	" at `addr' to flash at `offset'\n"
	" or to start of mtd `partition'\n"
	"sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n"
	" at address 'sector'\n"
	SF_TEST_HELP
);

下面先来说明下U_BOOT_CMD

U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")
     各个参数的意义如下:
           name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
           maxargs:命令的最大参数个数
           repeatable:是否自动重复(按Enter键是否会重复执行)
           command:该命令对应的响应函数指针
           usage:简短的使用说明(字符串)
           help:较详细的使用说明(字符串)

U_BOOT_CMD宏在include/command.h中定义:

“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
        cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

其中Struct_Section在include/command.h中定义如下:

凡是带有attribute ((unused,section (“.u_boot_cmd”))属性声明的变量都将被存放在”.u_boot_cmd”段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

.u_boot_cmd段

在u-Boot连接脚本 u-boot.lds中定义了.u_boot_cmd段:

. = .;
__u_boot_cmd_start = .;          /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;           /*  将__u_boot_cmd_end指定为当前地址  */

这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将u-boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在 __u_boot_cmd_start 与 __u_boot_cmd_end 之间查找就可以了。

cmd_tbl_t自定义结构体

cmd_tbl_t在include/command.h中定义如下:

struct cmd_tbl_s {
	char        *name;        /* Command Name            */
	int          maxargs;    /* maximum number of arguments    */
	int          repeatable;    /* autorepeat allowed?        */
	/* Implementation function    */
	int        (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
	char        *usage;        /* Usage message    (short)    */
#ifdef    CONFIG_SYS_LONGHELP
	char        *help;        /* Help  message    (long)    */
#endif
#ifdef CONFIG_AUTO_COMPLETE
	/* do auto completion on the arguments */
	int        (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s    cmd_tbl_t;

一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。在内存中所有cmd_tbl_t类型内存分布如下:
在这里插入图片描述

3.1.2 do_spi_flash

do_spi_flash
    >>>ret = do_spi_flash_probe(argc, argv); #初始化spi总线及spi flash
    >>>ret = do_spi_flash_read_write(argc, argv); #spi flash读写
    >>>ret = do_spi_protect(argc, argv); #
    >>>ret = do_spi_flash_test(argc, argv);# spi flash测试程序

下面分别对这几个函数进行说明。

3.2 probe

3.2.1 probe基本流程

>>>do_spi_flash_probe
   >>>spi_find_bus_and_cs
      >>>uclass_find_device_by_seq //根据bus number遍历spi总线对应的udevice设备(SPI控制器)
      >>>spi_find_chip_select  //根据spi总线udevice设备找到对应cs的子udevice 设备,由于在dts中我们已近将spi flash放到spi总线设备下,即在dts中指定了spi flash所属的spi总线,所以这里第一次就可以找到对应cs的子udevice
   >>>spi_flash_probe_bus_cs //根据bus/cs找到对应的spi slave device
          >>>spi_get_bus_and_cs
             >>>ret = uclass_first_device_by_seq(UCLASS_SPI, busnum, &bus); //根据uclass找到udevice bus
             >>>ret = spi_find_chip_select(bus, cs, &dev); //根据spi总线udevice设备找到对应cs的子udevice 设备,由于在dts中我们已近将spi flash放到spi总线设备下,即在dts中指定了spi flash所属的spi总线,所以这里第一次就可以找到对应cs的子udevice
             >>>device_probe(dev); //判断设备!device_active(dev)是否active,若未active,调用SPI FLASH的驱动active it
                   >>>spi_flash_std_probe //参考3.2.3小节
                      >>>spi_flash_probe_slave
                         >>>spi_flash_scan
                            >>>spi_flash_read_id
                >>>slave = dev_get_parent_priv(dev); //parent private数据即为spi_slave句柄??
                >>>slave->dev = dev; //spi slave挂在spi sub-bus udevice上
    >>>dev_get_uclass_priv 

下面对可能用到的关键函数进行说明。要想完全搞明白整个SPI及SPI Flash驱动模型,还需要熟悉Uboot DM的基本知识,可以参考

https://blog.csdn.net/u010961173/article/details/119611819

3.2.2 spi_find_bus_and_cs

        函数在spi-uclass中实现,默认参与编译

  drivers/spi/Makefile:8:obj-y += spi-uclass.o

函数功能:根据bus、cs查询是否对应的spi bus udevice及对应cs的udevice.

 函数调用:

int spi_find_bus_and_cs(int busnum, int cs, struct udevice **busp,
			struct udevice **devp)
    >>>ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, false, &bus);//通过序号找到spi bus对应的udevice
    >>>ret = spi_find_chip_select(bus, cs, &dev);//根据spi bus udevice及cs查询是否有对应cs的子udevice,第一次肯定是没有的,因为还没有建立spi/spi_flash之间的联系
    >>>*busp = bus; // spi bus udevice
    >>>*devp = dev; //对应cs的spi bus的子udevice

(1)uclass_find_device_by_seq

        函数功能:按序号从uclass中找到对应的udevice,若序号匹配,则返回对应的udevice。

int uclass_find_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
    >>> ret = uclass_get(id, &uc); //获取uclass id对应的uclass对象
    >>> uclass_foreach_dev(dev, uc) { //遍历uclass下面所有的udevice,并找到对应seq的udvice对象
            if (dev->seq_ == seq)
                *devp = dev;
        }

        具体来讲,就是通过调用uclass_get函数获取uclass id对应的uclass对象,获取到uclass对象之后,再使用uclass_foreach_dev函数对其下的udevice设备进行遍历,最后得到指定busnum的spi bus udevice(总线设备)。

=================================================================================================

在这里,我们需要说明的是,uclass、udevice对象的创建及其驱动的绑定在DM的初始化阶段已经完成,下面简单的梳理一下流程:

dts中的udevice配置,通过compatible识别 ---|
                                           |
udevice驱动U_BOOT_DRIVER(xxx) -------------|-----DM初始化-----lists_bind_fdt解析dts,并根据compatible遍历udevice driver table---->创建uclass、udevice并完成与driver\uclass_driver的绑定
                                           |
uclass驱动UCLASS_DRIVER(xxx) --------------|

首先,在dts中定义spi总线对象及spi flash对象

然后,在源码中分别声明uclass_driver及udeivce的driver

        以spi驱动及spi flash驱动为例,需要声明的uclass_driver及udeivce的driver如下:

        spi(总线)驱动:

spi总线uclass驱动:
文件:spi-uclass.c
UCLASS_DRIVER(spi) = {
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.post_bind	= dm_scan_fdt_dev,
#endif
	.post_probe	= spi_post_probe,
	.child_pre_probe = spi_child_pre_probe,
	.per_device_auto_alloc_size = sizeof(struct dm_spi_bus),
	.per_child_auto_alloc_size = sizeof(struct spi_slave),
	.per_child_platdata_auto_alloc_size =
			sizeof(struct dm_spi_slave_platdata),
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.child_post_bind = spi_child_post_bind,
#endif
};

spi总线udevice驱动:
文件:designware_spi.c
U_BOOT_DRIVER(dw_spi) = {
	.name = "dw_spi",
	.id = UCLASS_SPI,
	.of_match = dw_spi_ids,
	.ops = &dw_spi_ops,
	.ofdata_to_platdata = dw_spi_ofdata_to_platdata,
	.platdata_auto_alloc_size = sizeof(struct dw_spi_platdata),
	.priv_auto_alloc_size = sizeof(struct dw_spi_priv),
	.probe = dw_spi_probe,
};

        spi flash驱动:

spi flash uclass驱动:
文件:sf_uclass.c
UCLASS_DRIVER(spi_flash) = {
	.id		= UCLASS_SPI_FLASH,
	.name		= "spi_flash",
	.post_bind	= spi_flash_post_bind,
	.per_device_auto_alloc_size = sizeof(struct spi_flash),
};

spi flash udevice驱动:
文件:sf_probe.c
U_BOOT_DRIVER(spi_flash_std) = {
	.name		= "spi_flash_std",
	.id		= UCLASS_SPI_FLASH,
	.of_match	= spi_flash_std_ids,
	.probe		= spi_flash_std_probe,
	.priv_auto_alloc_size = sizeof(struct spi_flash),
	.ops		= &spi_flash_std_ops,
};

最后,uboot在初始化阶段会解析dtb,然后寻找dtb udevice对应的到driver,动态创建driver对应的uclass、udeivce实例对象,同时对相关的驱动进行绑定。要说明的一点是,并且不是所有uclass都会生成,有对应uclass driver并且被udevice匹配到的uclass才会生成。

=================================================================================================

(2)spi_find_chip_select

         函数功能:根据spi bus device及片选,查找其下的子设备,若spi bus device 下面已经挂接了对应cs的udevice则将该udevice返回。

int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp)
    >>>	for (device_find_first_child(bus, &dev); dev;
	         device_find_next_child(&dev)) { //遍历SPI总线设备下的子设备,寻找是否有对应不同CS的子设备udevice
		    plat = dev_get_parent_platdata(dev);
		    if (plat->cs == cs) { //由于在dts中我们已近将spi flash放到spi总线设备下,即在dts中指定了spi flash所属的spi总线,所以这里第一次就可以找到对应cs的子udevice
			    *devp = dev;
			    return 0;
		    }
        }

        函数的实现的功能比较简单,但是我们要思考这么一个问题,spi bus udevice下面的子udevice(即对应cs的那个udevice对象)什么时间创建并被挂接到spi bus udevice的?spi总线和spi flash是怎样建立的联系?

        我们这里的回答是在dts中,如下所示:

spi2: spi@e002b000 {
		compatible = "snps,dw-apb-ssi-4.02a";
		reg = <0 0xe002b000 0x0 0x1000>;
		#address-cells = <1>;
		#size-cells = <1>;

		/*clocks = <&nb_periph_clk 7>;*/
		status = "disabled";

		flash: mt25qu256@0 {
				compatible = "jedec,spi-nor";
				spi-max-frequency = <100000>;
				reg = <0 0>;
		};
};

由于在dts中我们已近将spi flash放到spi总线设备下,即在dts中指定了spi flash所属的spi总线,所以这里第一次也能够找到

3.2.3 spi_flash_probe_bus_cs

 函数功能:建立SPI总线udevice及spi flash udevice之间的联系,根据spi bus 、cs获取spi slave设备,spi slave对应的udevice(作为spi bus udevice的子设备挂接到spi udevice上)。

函数调用

int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
			   unsigned int max_hz, unsigned int spi_mode,
			   struct udevice **devp)
    >>>	ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode,
				  "spi_flash_std", str, &bus, &slave);
        >>>ret = uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus); //根据spi bus num获取spi bus udevice
        >>>ret = spi_find_chip_select(bus, cs, &dev); //根据spi bus udevice及cs获取cs对应的udevice,由于在dts中我们已近将spi flash放到spi总线设备下,即在dts中指定了spi flash所属的spi总线,所以这里第一次也能够找到
        >>>若从设备没有active,进行激活操作
            if (!device_active(dev)) {
                ret = device_probe(dev); //下面具体说明
		        slave = dev_get_parent_priv(dev);//从这里可以看出,cs对应的udevice的priv存放的是spi slave对象指针,slave对象在初始化阶段根据UCLASS_DRIVER(spi).per_child_auto_alloc_size进行分配
		        slave->dev = dev;
	        }
        >>>plat = dev_get_parent_platdata(dev); //获取platdata数据,这里的platdata是在device_bind_common函数中在初始化阶段根据UCLASS_DRIVER(spi).per_child_platdata_auto_alloc_size进行分配的
        >>>ret = spi_set_speed_mode(bus, speed, mode); //调用bus udevice本身的回调,设置相关参数
        >>>*busp = bus; //返回spi bus udevice
        >>>*devp = dev_get_parent_priv(dev);//返回struct spi_slave对象
    >>>	*devp = slave->dev; //获取struct spi_slave所挂接的udecice(即CS对应的udevice)

         spi_flash_probe_bus_cs函数调用了spi_get_bus_and_cs,根据bus number及cs number返回cs对应的udevice设备(该设备作为SPI bus udevice的子设备)及spi_slave从设备。具体来讲,根据uclass id获取bus udevice,通过调用spi_find_chip_select根据cs获取子总线设备sub-bus udevice。拿到cs对应的spi bus udeivce子udevice设备后,调用device_probe函数probe spi_slave设备。

device_probe函数是一个递归函数,完成以下功能:

(1)判断udevice是否已经active,否则,active it

(2)检查并active这个udevice相关的上游udevice,并完成probe

(3)执行udevice及其上游的驱动probe函数

函数执行完成后,相关的udevice及其驱动就会被激活active。

下面我们来细看一下device_probe函数的实现。

int device_probe(struct udevice *dev)
{
	const struct driver *drv;
	int ret;

	if (!dev)
		return -EINVAL;
    /* 判断当前device是否已经active,若已active,直接返回 */
	if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
		return 0;

    /* udevice对应的driver已经ready */
	drv = dev->driver;
	assert(drv);
    
    /* 将udevice相关的of配置同步都udevice的数据结构中 */
	ret = device_of_to_plat(dev);
	if (ret)
		goto fail;

	/* 确保所有的父设备均已active,否则,递归active */
	if (dev->parent) {
		ret = device_probe(dev->parent);
		if (ret)
			goto fail;

		/*
		 * The device might have already been probed during
		 * the call to device_probe() on its parent device
		 * (e.g. PCI bridge devices). Test the flags again
		 * so that we don't mess up the device.
		 */
		if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
			return 0;
	}

    /* 激活设备 */
	dev_or_flags(dev, DM_FLAG_ACTIVATED);

	/*
	 * Process pinctrl for everything except the root device, and
	 * continue regardless of the result of pinctrl. Don't process pinctrl
	 * settings for pinctrl devices since the device may not yet be
	 * probed.
	 *
	 * This call can produce some non-intuitive results. For example, on an
	 * x86 device where dev is the main PCI bus, the pinctrl device may be
	 * child or grandchild of that bus, meaning that the child will be
	 * probed here. If the child happens to be the P2SB and the pinctrl
	 * device is a child of that, then both the pinctrl and P2SB will be
	 * probed by this call. This works because the DM_FLAG_ACTIVATED flag
	 * is set just above. However, the PCI bus' probe() method and
	 * associated uclass methods have not yet been called.
	 */
	if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)
		pinctrl_select_state(dev, "default");

	if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent &&
	    (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) &&
	    !(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) {
		ret = dev_power_domain_on(dev);
		if (ret)
			goto fail;
	}

    /* ??? */
	ret = device_get_dma_constraints(dev);
	if (ret)
		goto fail;
    /* uclass probe前的预处理 
        这里会执行
        (1)udevice对应的uclass驱动uclass_driver的pre_probe函数
        (2)父设备udevice对应uclass_drver的child_pre_probe函数
    */
	ret = uclass_pre_probe_device(dev);
	if (ret)
		goto fail;

	if (dev->parent && dev->parent->driver->child_pre_probe) {
		ret = dev->parent->driver->child_pre_probe(dev);
		if (ret)
			goto fail;
	}

	/* Only handle devices that have a valid ofnode */
	if (dev_has_ofnode(dev)) {
		/*
		 * Process 'assigned-{clocks/clock-parents/clock-rates}'
		 * properties
		 */
		ret = clk_set_defaults(dev, 0);
		if (ret)
			goto fail;
	}

    /* 执行驱动本身的probe函数,再定义驱动时指定,U_BOOT_DRIVER(jedec_spi_nor).probe */
	if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto fail;
	}

    /* uclass probe后的扫尾工作 
        这里会执行
        (1) 父设备udevice对应uclass_drver的child_post_probe函数
        (2) udevice对应的uclass驱动uclass_driver的post_probe函数

    */
	ret = uclass_post_probe_device(dev);
	if (ret)
		goto fail_uclass;

	if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)
		pinctrl_select_state(dev, "default");

	return 0;
fail_uclass:
	if (device_remove(dev, DM_REMOVE_NORMAL)) {
		dm_warn("%s: Device '%s' failed to remove on error path\n",
			__func__, dev->name);
	}
fail:
	dev_bic_flags(dev, DM_FLAG_ACTIVATED);

	device_free(dev);

	return ret;
}

3.2.4 dev_get_uclass_priv

这个函数比较简单,直接返回设备对应uclass的priv数据,这个数据指向struct spi_flash对象。

void *dev_get_uclass_priv(const struct udevice *dev)
{
	if (!dev) {
		dm_warn("%s: null device\n", __func__);
		return NULL;
	}

	return dm_priv_to_rw(dev->uclass_priv_);
}

static inline void *dm_priv_to_rw(void *priv)
{
	return priv;
}

        通过上面的配置可知,在DM初始化过程仅仅建立了uclass\uclass_driver\udevice\udevice driver之间的关系,但是还没有probe(active),probe的动作是通过device_probe来完成的,例如spi总线的probe、spi flash的probe。

3.4 spi 驱动及spi flash驱动

spi驱动和spi flash驱动均是由spi_flash_probe_bus_cs函数通过调用device_probe函数来完成的。下面列出具体的probe函数的实现。

3.4.1 spi 驱动

关于spi驱动的具体实现因每个厂家的IP实现不同而不同。

uclass spi 总线驱动
文件:spi-uclass.c
UCLASS_DRIVER(spi) = {
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.post_bind	= dm_scan_fdt_dev,
#endif
	.post_probe	= spi_post_probe,
	.child_pre_probe = spi_child_pre_probe,
	.per_device_auto_alloc_size = sizeof(struct dm_spi_bus), //每个spi bus相关配置参数占用的内存大小
	.per_child_auto_alloc_size = sizeof(struct spi_slave), //每个slave 对象实例大小
	.per_child_platdata_auto_alloc_size =
			sizeof(struct dm_spi_slave_platdata), //每个slave的与设备相关的配置数据
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.child_post_bind = spi_child_post_bind,
#endif
};

udevice spi 总线驱动
文件:designeware_spi.c
U_BOOT_DRIVER(dw_spi) = {
	.name = "dw_spi",
	.id = UCLASS_SPI,
	.of_match = dw_spi_ids,
	.ops = &dw_spi_ops,
	.ofdata_to_platdata = dw_spi_ofdata_to_platdata,
	.platdata_auto_alloc_size = sizeof(struct dw_spi_platdata),
	.priv_auto_alloc_size = sizeof(struct dw_spi_priv),
	.probe = dw_spi_probe,
};

static const struct dm_spi_ops dw_spi_ops = {
	.xfer		= dw_spi_xfer,
	.set_speed	= dw_spi_set_speed,
	.set_mode	= dw_spi_set_mode,
	/*
	 * cs_info is not needed, since we require all chip selects to be
	 * in the device tree explicitly
	 */
};

static const struct udevice_id dw_spi_ids[] = {
	{ .compatible = "snps,dw-apb-ssi" },
	{ }
};

3.4.2 spi flash驱动

uclass spi flash驱动
文件:sf-uclass.c
UCLASS_DRIVER(spi_flash) = {
	.id		= UCLASS_SPI_FLASH,
	.name		= "spi_flash",
	.post_bind	= spi_flash_post_bind,
	.per_device_auto_alloc_size = sizeof(struct spi_flash),
};

udevice spi flash驱动
文件:sf_probe.c中
U_BOOT_DRIVER(jedec_spi_nor) = {
	.name		= "jedec_spi_nor",
	.id		= UCLASS_SPI_FLASH, //spi slave 的id 与uclass的id一致,所以在DM初始化过程中就已经和udevice绑定到一块了
	.of_match	= spi_flash_std_ids,
	.probe		= spi_flash_std_probe,
	.remove		= spi_flash_std_remove,
	.priv_auto	= sizeof(struct spi_nor),
	.ops		= &spi_flash_std_ops,
};

static const struct dm_spi_flash_ops spi_flash_std_ops = {
	.read = spi_flash_std_read,
	.write = spi_flash_std_write,
	.erase = spi_flash_std_erase,
	.get_sw_write_prot = spi_flash_std_get_sw_write_prot,
};

static const struct udevice_id spi_flash_std_ids[] = {
	{ .compatible = "jedec,spi-nor" },//dts中配置时需要遵循的compatiable
	{ }
};

3.4.2.1 spi flash驱动spi_flash_std_probe

//入参udevice是cs对应的udevice(挂在spi bus udevice上,作为其子设备而存在)
int spi_flash_std_probe(struct udevice *dev)
{
	struct spi_slave *slave = dev_get_parent_priv(dev); //获取struct spi_slave对象
	struct spi_flash *flash;

	flash = dev_get_uclass_priv(dev); //获取struct spi_flash对象
	flash->dev = dev;
	flash->spi = slave;
	return spi_flash_probe_slave(flash);
}

(1)dev_get_parent_priv函数

函数的作用是从udevice中获取parent_priv,变量中存放的是spi_slave的对象句柄。

这个对象是在spi总线在初始化的时候分配的,具体分配的大小由UCLASS_DRIVER(spi).per_child_auto    = sizeof(struct spi_slave)指定

UCLASS_DRIVER(spi) = {
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
	.post_bind	= dm_scan_fdt_dev,
#endif
	.post_probe	= spi_post_probe,
	.child_pre_probe = spi_child_pre_probe,
	.per_device_auto	= sizeof(struct dm_spi_bus),
	.per_child_auto	= sizeof(struct spi_slave), //每个slave使用的内存消耗
	.per_child_plat_auto	= sizeof(struct dm_spi_slave_plat),
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	.child_post_bind = spi_child_post_bind,
#endif
};

int device_probe(struct udevice *dev)
    >>>ret = device_of_to_plat(dev); //这个也是一个递归函数
        >>>ret = device_alloc_priv(dev);  //分配设备要求的priv/plat数据
            >>>分配驱动中要求的内存
                >>>drv = dev->driver;
                >>>ptr = alloc_priv(drv->priv_auto, drv->flags); //分配驱动中要求的priv数据
                    例如,分配U_BOOT_DRIVER(jedec_spi_nor).priv_auto	= sizeof(struct spi_nor),也就是spi flash对象
                >>>dev_set_priv(dev, ptr);
            >>>分配uclass中要求的内存
                >>>size = dev->uclass->uc_drv->per_device_auto;
                >>>ptr = alloc_priv(size, dev->uclass->uc_drv->flags);
                    例如,分配UCLASS_DRIVER(spi).per_device_auto	= sizeof(struct dm_spi_bus),也就是dm_spi_bus
                >>>dev_set_uclass_priv(dev, ptr);
            >>>分配parent中要求的内存
                >>>分别检查parent udevice和parent uclass对每个子设备是否有内存分配的需求
                    >>>size = dev->parent->driver->per_child_auto; //parent驱动中要求的内存
                    >>>size = dev->parent->uclass->uc_drv->per_child_auto; //parent对应的uclass中要求的内存
                        例如,分配UCLASS_DRIVER(spi).per_child_auto	= sizeof(struct spi_slave),也就是spi_slave对象
                    >>>ptr = alloc_priv(size, drv->flags);
                    >>>dev_set_parent_priv(dev, ptr);
                
                

(2)dev_get_uclass_priv

从udevice中获取uclass_priv,变量中存放的是spi_flash的对象句柄。

这个对象是在spi总线在初始化的时候分配的,具体分配的大小由U_BOOT_DRIVER(jedec_spi_nor).priv_auto    = sizeof(struct spi_nor)指定.

(3)spi_flash_probe_slave

struct spi_flash对象包含了其所在的udevice信息及表述spi_slave的信息。

文件:sf_probe.c
static int spi_flash_probe_slave(struct spi_flash *flash)
    >>>struct spi_slave *spi = flash->spi;
    >>>ret = spi_claim_bus(spi); //spi-uclass.c
        >>>dm_spi_claim_bus(slave->dev)
            >>>int ret = spi_set_speed_mode(bus, speed, slave->mode);
    >>>ret = spi_nor_scan(flash); //spi-nor-core.c
        >>>nor相关的初始化
        	nor->reg_proto = SNOR_PROTO_1_1_1;
    	    nor->read_proto = SNOR_PROTO_1_1_1;
	        nor->write_proto = SNOR_PROTO_1_1_1;
        	nor->read = spi_nor_read_data;
        	nor->write = spi_nor_write_data;
        	nor->read_reg = spi_nor_read_reg;
        	nor->write_reg = spi_nor_write_reg;
        >>>info = spi_nor_read_id(nor);
            将读出来的数据与spi_nor_ids数据表中的值进行比较,成功,则返回info
        >>>ret = spi_nor_init_params(nor, info, &params);
        >>>mtd参数初始化
        	mtd->priv = nor;
	        mtd->type = MTD_NORFLASH;
	        mtd->writesize = 1;
        	mtd->flags = MTD_CAP_NORFLASH;
        	mtd->size = params.size;
        	mtd->_erase = spi_nor_erase;
        	mtd->_read = spi_nor_read;
        	mtd->_write = spi_nor_write;
        >>> ret = spi_nor_setup(nor, info, &params, &hwcaps);
        >>> spi_nor_set_4byte_opcodes(nor, info);
        >>> ret = spi_nor_init(nor);

3.5 read/write


do_spi_flash_read_write() 

static int do_spi_flash_read_write(int argc, char *const argv[])
    >>>ret = spi_flash_update(flash, offset, len, buf);
    >>>ret = spi_flash_read(flash, offset, len, buf);
        >>>spi_flash_read_dm(flash->dev, offset, len, buf); //spi_flash.h
            >>>sf_get_ops(dev)->read(dev, offset, len, buf) //sf-uclass.c
                即 (struct dm_spi_flash_ops *)(dev)->driver->ops)
    >>>ret = spi_flash_write(flash, offset, len, buf);
        >>>spi_flash_write_dm(flash->dev, offset, len, buf); // //spi_flash.h
            >>>sf_get_ops(dev)->write(dev, offset, len, buf)  //sf-uclass.c
                即 (struct dm_spi_flash_ops *)(dev)->driver->ops)

3.6 erase

do_spi_flash_erase()

static int do_spi_flash_erase(int argc, char *const argv[])
    >>>ret = spi_flash_erase(flash, offset, size);
        >>>spi_flash_erase_dm(flash->dev, offset, len); //spi_flash.h
            >>>sf_get_ops(dev)->erase(dev, offset, len) //sf-uclass.c
               即 (struct dm_spi_flash_ops *)(dev)->driver->ops)

3.7 other

do_spi_protect()
do_spi_flash_test()

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值