flash:uboot下sf(nor flash)的流程

目录

1. sf probe命令的执行流程

1.1 查找dev

1.2 绑定dev

1.3 probe dev

2. read id和read data流程

2.1 read id流程

2.2 read data流程


1. sf probe命令的执行流程

uboot下sf命令:sf probe 1000000 0x2103

do_spi_flash() [cmd/sf.c] 

        |->do_spi_flash_probe() [cmd/sf.c]

                |->spi_flash_probe_bus_cs() [driver/mtd/spi/sf-uclass.c]

                        |->spi_get_bus_and_cs()[drivers/spi/spi-uclass.c]

重点看下这个函数:spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode,"jedec_spi_nor", str, &bus, &slave);

1.1 查找dev

uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus)[driver/core/uclass.c]

        |->uclass_find_device_by_seq(id, seq, false, &dev)[driver/core/uclass.c]

                |->uclass_foreach_dev(dev, uc)[include/dm/uclass.h]

                if ((find_req_seq ? dev->req_seq : dev->seq) ==seq_or_req_seq) {
                    *devp = dev;
                    log_debug("   - found\n");
                    return 0;
                }

[drivers/spi/spi-uclass.c]
UCLASS_DRIVER(spi) = {
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
    ...
};
|->spi_find_chip_select(bus, cs, &dev)[drivers/spi/spi-uclass.c]

在UCLASS_SPI对应的udevice列表中查找符合seq的udevice,其中,uclass_foreach_dev(dev, uc)是一个宏,会创建一个 for() 循环,该循环按从头到尾的顺序遍历uclass中的可用设备。

1.2 绑定dev

device_bind_driver(bus, drv_name, dev_name, &dev)[drivers/core/lists.c]
    |->device_bind_driver_to_node()
        |->device_bind_with_driver_data()[drivers/core/device.c]
            |->device_bind_common()
                |->uclass_get(drv->id, &uc)
                |->calloc(1, sizeof(struct udevice))
                |->dev->driver = drv
                |->dev->req_seq = -1
                |->if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) && (uc->uc_drv->flags &                         
                   DM_UC_FLAG_SEQ_ALIAS))
                    |->if(CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
                        |->if (uc->uc_drv->name && ofnode_valid(node))
				           dev_read_alias_seq(dev, &dev->req_seq);

一些设备,例如 SPI 总线、I2C 总线和串行端口使用别名编号,"DM_UC_FLAG_SEQ_ALIAS"就是配置的别名,有别名的设备,会分配一个reg_seq。

1.3 probe dev

device_probe(dev)[drivers/core/device.c]
    |->drv = dev->driver;
    |->drv->probe(dev);
        即spi_flash_std_probe()[drivers/mtd/spi/sf_probe.c]
            |->spi_flash_probe_slave(flash)

[drivers/mtd/spi/sf_probe.c]
static const struct udevice_id spi_flash_std_ids[] = {
	{ .compatible = "jedec,spi-nor" },
	{ }
};

U_BOOT_DRIVER(jedec_spi_nor) = {
	.name		= "jedec_spi_nor",
	.id		    = UCLASS_SPI_FLASH,
	.of_match	= spi_flash_std_ids,
	.probe		= spi_flash_std_probe,
	.remove		= spi_flash_std_remove,
	.priv_auto_alloc_size = sizeof(struct spi_flash),
	.ops		= &spi_flash_std_ops,
};

[arch/arm/dts/*.dts]
&qspi {
	status = "okay";
	page-size = <256>;
	block-size = <16>;
	#address-cells = <1>;
	#size-cells = <0>;

	spi-flash@0 {
		compatible = "jedec,spi-nor";
		spi-max-frequency = <1000000>; 
		reg = <1>; /* CS1 */
	};

由compatible中的字符串"jedec_spi_nor"匹配到dts文件中spi-flash子节点,从而调用到spi_flash_std_probe()函数,进而调用spi_flash_probe_slave(flash),可以probe总线上的一个SPI flash设备。

接下来看下这个spi_flash_probe_slave(flash)函数;

spi_flash_probe_slave()
    |->spi_claim_bus(spi)[drivers/spi/spi-uclass.c]
        |->dm_spi_claim_bus(slave->dev)
            spi_set_speed_mode(bus, speed, slave->mode)
                |->spi_get_ops(bus)
                |->ops->set_speed(bus, speed)
                    即cadence_spi_set_speed()[drivers/spi/cadence_qspi.c]
                |->ops->set_mode(bus, mode)
                    即cadence_spi_set_mode()[drivers/spi/cadence_qspi.c]

    |->spi_nor_scan()[drivers/mtd/spi/spi-nor-core.c]

spi_claim_bus()函数主要就是设置spi flash设备的速率和模式;

主要看下spi_nor_scan()函数;

|->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;
...

1.3.1 为所有命令重置 SPI 协议,并且连接SPI层的read、write、read_reg、write_reg的操作接口。

|->spi_nor_read_id(nor)
|->spi_nor_init_params(nor, info, &params)
    |->spi_nor_set_read_settings()
    |->spi_nor_set_pp_settings()
    |->spi_nor_parse_sfdp()

[drivers/mtd/spi/spi-nor-core.c]
info = spi_nor_ids;//白名单数组
	for (; info->name; info++) {
		if (info->id_len) {
			if (!memcmp(info->id, id, info->id_len))
				return info;
		}
	}

1.3.2 读出flash设备的jedec id,读出后会与白名单数组进行比较,并找到与之匹配的一项,之后初始化flash设备的参数,包括快速读取设置,页面程序设置以及解析串行flash可发现参数(sfdp),用来获取erase、read及write的操作码(opcode)和协议(protocol)。

对于串行flash可发现参数(sfdp),JEDEC JESD216 规范描述了串行闪存可发现参数。 这是几乎所有(Q)SPI存储器制造商都支持的标准。 这些硬编码表允许我们在运行时了解执行基本SPI闪存操作(例如快速读取、页面编程或扇区擦除命令)所需的主要参数。

|->if (!mtd->name)
		mtd->name = info->name;
|->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;

1.3.3 连接MTD层的erase、read、write接口。

|->spi_nor_setup(nor, info, &params, &hwcaps)
    |->spi_nor_select_read(nor, params, shared_mask)
    |->spi_nor_select_pp(nor, params, shared_mask)
    |->spi_nor_select_erase(nor, info)

1.3.4 配置SPI内存,包括为快速读取、页面编程和扇区擦除选择操作码,设置虚拟周期数(模式周期 + 等待状态),设置寄存器和内存访问的 SPI 协议,如果需要支持4线模式,还需设置 Quad Enable 位。

if (nor->addr_width) {
	} else if (info->addr_width) {
		nor->addr_width = info->addr_width;
	} else if (mtd->size > SZ_16M) {
		nor->addr_width = 4;
		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
		    info->flags & SPI_NOR_4B_OPCODES)
			|->spi_nor_set_4byte_opcodes(nor, info);

[drivers/mtd/spi/spi-nor-ids.c]
const struct flash_info spi_nor_ids[] = {
    { INFO("mt35xu512aba", 0x2c5b1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_OCTAL_READ |             
      SPI_NOR_4B_OPCODES) },
    ...
}

1.3.5 如果flash设备大于16MB,就需要启用4字节寻址,在函数spi_nor_set_4byte_opcodes(nor, info)中执行指令切换到4字节指令,需要注意的是,在白名单数组中,对于大于16MB的flash设备的配置中,需要在flags项中配置上宏定义SPI_NOR_4B_OPCODES,只有配置上该宏定义才会切换到4字节寻址。

|->spi_nor_init(nor)

|->printf("SF: Detected %s with page size ", nor->name);
	print_size(nor->page_size, ", erase size ");
	print_size(nor->erase_size, ", total ");
	print_size(nor->size, "");
	puts("\n");

//例如:SF: Detected gd25lq128d with page size 256 Bytes, erase size 4 KiB, total 16 MiB

1.3.6 发送所有必须的SPI flash命令以初始化flash设备。

2. read id和read data流程

首先看下qspi控制器相关的数据结构的初始化和与之匹配的dts节点,这样可以方便追踪read id和read data流程。

[drivers/spi/cadence.qspi.c]
static const struct spi_controller_mem_ops cadence_spi_mem_ops = {
	.exec_op = cadence_spi_mem_exec_op,
};

static const struct dm_spi_ops cadence_spi_ops = {
	.set_speed	= cadence_spi_set_speed,
	.set_mode	= cadence_spi_set_mode,
	.mem_ops	= &cadence_spi_mem_ops,
};

static const struct udevice_id cadence_spi_ids[] = {
	{ .compatible = "cdns,qspi-nor" },
	{ .compatible = "ti,am654-ospi" },
	{ }
};

U_BOOT_DRIVER(cadence_spi) = {
	.name = "cadence_spi",
	.id = UCLASS_SPI,
	.of_match = cadence_spi_ids,
	.ops = &cadence_spi_ops,
	.ofdata_to_platdata = cadence_spi_ofdata_to_platdata,
	.platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata),
	.priv_auto_alloc_size = sizeof(struct cadence_spi_priv),
	.probe = cadence_spi_probe,
	.remove = cadence_spi_remove,
	.flags = DM_FLAG_OS_PREPARE,
};

qspi: spi@f1000000 {
			compatible =  "cdns,qspi-nor";
			status = "disabled";
			reg = <0xf1000000 0x4000>,
            <0xe0000000 0x10000000>;
 			num-cs = <4>;
 			cdns,fifo-depth = <256>;
 			cdns,fifo-width = <4>;
			cdns,trigger-address = <0xe0000000>;
			/*cdns,is-decoded-cs = <0>;*/
		};

2.1 read id流程

spi_nor_read_id(nor)[drivers/mtd/spi/spi-nor-core.c]
    |->nor->read_reg()
        即spi_nor_read_reg()
          |->struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
					  SPI_MEM_OP_NO_ADDR,
					  SPI_MEM_OP_NO_DUMMY,
					  SPI_MEM_OP_DATA_IN(len, NULL, 1));
          |->spi_nor_read_write_reg(nor, &op, val)
              |->spi_mem_exec_op(nor->spi, op)[drivers/spi/spi-mem.c]
                  |->ops->mem_ops->exec_op(slave, op)
                    即cadence_spi_mem_exec_op()[drivers/spi/cadence.qspi.c]
                        |->cadence_qspi_apb_command_read(base, op)[drivers/spi/cadence_qspi_apb.c]
                            |->cadence_qspi_apb_exec_flash_cmd(reg_base, reg)

2.2 read data流程

nor->read()
  即spi_nor_read_data()[drivers/mtd/spi/spi-nor-core.c]
    |->struct spi_mem_op op =
			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
				   SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
				   SPI_MEM_OP_DATA_IN(len, buf, 1));
    |->spi_mem_exec_op(nor->spi, &op)[drivers/spi/spi-mem.c]
        |->ops->mem_ops->exec_op(slave, op)
          即cadence_spi_mem_exec_op()[drivers/spi/cadence.qspi.c]
            |->cadence_qspi_apb_read_setup(plat, op)[drivers/spi/cadence_qspi_apb.c]
            |->cadence_qspi_apb_read_execute(plat, op)
                |->memcpy_fromio(buf, plat->ahbbase + from, len)

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值