Linux内核4.14版本——SPI NOR子系统(4)——m25p80.c分析

1. 简介

2. m25p_probe

2.1 设置struct spi_nor结构体

2.2 设置struct spi_nor_hwcaps结构体

2.3 spi_nor_scan注册struct spi_nor结构体

2.4 mtd device注册(mtd_device_register)

3. 几个函数

3.1 m25p80_read_reg

3.2 m25p80_write_reg

3.3 m25p80_read

3.4 m25p80_write


1. 简介

我们分析了spi-nor子系统框架的由来,以及spi-nor子系统的核心函数,下面我们分析一下控制器的实现。从前面一章文章,我们知道使用spi-nor子系统有两种情况。一个是通过SPI总线访问的NOR FLASH,一个是通过QSPI控制器访问的NOR FLASH,如下所示。本文主要介绍的是通过SPI总线访问的NOR FLASH。

      我们看一下m25p_probe函数。

static struct spi_driver m25p80_driver = {
    .driver = {
        .name    = "m25p80",
        .of_match_table = m25p_of_table,
    },
    .id_table    = m25p_ids,
    .probe    = m25p_probe,
    .remove    = m25p_remove,

    /* REVISIT: many of these chips have deep power-down modes, which
     * should clearly be entered on suspend() to minimize power use.
     * And also when they're otherwise idle...
     */
};

2. m25p_probe

2.1 设置struct spi_nor结构体

static int m25p_probe(struct spi_device *spi)
{
	............

	nor = &flash->spi_nor;

	/* install the hooks */
	nor->read = m25p80_read;
	nor->write = m25p80_write;
	nor->write_reg = m25p80_write_reg;
	nor->read_reg = m25p80_read_reg;

	nor->dev = &spi->dev;
	spi_nor_set_flash_node(nor, spi->dev.of_node);
	nor->priv = flash;

	spi_set_drvdata(spi, flash);
	flash->spi = spi;
	..........
}

      设置struct spi_nor结构体。

2.2 设置struct spi_nor_hwcaps结构体

static int m25p_probe(struct spi_device *spi)
{
	...........
	struct spi_nor_hwcaps hwcaps = {
		.mask = SNOR_HWCAPS_READ |
			SNOR_HWCAPS_READ_FAST |
			SNOR_HWCAPS_PP,
	};
	.............

	if (spi->mode & SPI_RX_QUAD) {
		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;

		if (spi->mode & SPI_TX_QUAD)
			hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
					SNOR_HWCAPS_PP_1_1_4 |
					SNOR_HWCAPS_PP_1_4_4);
	} else if (spi->mode & SPI_RX_DUAL) {
		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;

		if (spi->mode & SPI_TX_DUAL)
			hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
	}
	.........
}

2.3 spi_nor_scan注册struct spi_nor结构体

static int m25p_probe(struct spi_device *spi)
{
	...........
	ret = spi_nor_scan(nor, flash_name, &hwcaps);
	if (ret)
		return ret;
	..........
}

2.4 mtd device注册(mtd_device_register)

static int m25p_probe(struct spi_device *spi)
{
	...........
	ret = spi_nor_scan(nor, flash_name, &hwcaps);
	if (ret)
		return ret;

	return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
				   data ? data->nr_parts : 0);
}

3. 几个函数

       少数据量读写用read_reg/write_reg,大数据量用read/write。读写底层实现使用SPI控制器实现的。

3.1 m25p80_read_reg

static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
	struct m25p *flash = nor->priv;
	struct spi_device *spi = flash->spi;
	int ret;

	ret = spi_write_then_read(spi, &code, 1, val, len);
	if (ret < 0)
		dev_err(&spi->dev, "error %d reading %x\n", ret, code);

	return ret;
}

3.2 m25p80_write_reg

static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
	struct m25p *flash = nor->priv;
	struct spi_device *spi = flash->spi;

	flash->command[0] = opcode;
	if (buf)
		memcpy(&flash->command[1], buf, len);

	return spi_write(spi, flash->command, len + 1);
}

3.3 m25p80_read

static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
			   u_char *buf)
{
	struct m25p *flash = nor->priv;
	struct spi_device *spi = flash->spi;
	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
	struct spi_transfer t[3];
	struct spi_message m;
	unsigned int dummy = nor->read_dummy;
	ssize_t ret;
	int cmd_sz;

	/* get transfer protocols. */
	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
	data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);

	/* convert the dummy cycles to the number of bytes */
	dummy = (dummy * addr_nbits) / 8;

	if (spi_flash_read_supported(spi)) {
		struct spi_flash_read_message msg;

		memset(&msg, 0, sizeof(msg));

		msg.buf = buf;
		msg.from = from;
		msg.len = len;
		msg.read_opcode = nor->read_opcode;
		msg.addr_width = nor->addr_width;
		msg.dummy_bytes = dummy;
		msg.opcode_nbits = inst_nbits;
		msg.addr_nbits = addr_nbits;
		msg.data_nbits = data_nbits;

		ret = spi_flash_read(spi, &msg);
		if (ret < 0)
			return ret;
		return msg.retlen;
	}

	spi_message_init(&m);
	memset(t, 0, (sizeof t));

	flash->command[0] = nor->read_opcode;
	m25p_addr2cmd(nor, from, flash->command);

	t[0].tx_buf = flash->command;
	t[0].tx_nbits = inst_nbits;
	t[0].len = m25p_cmdsz(nor) + dummy;
	spi_message_add_tail(&t[0], &m);

	/*
	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
	 * specific pattern, which might make the memory enter its Continuous
	 * Read mode by mistake.
	 * Based on the different mode cycle bit patterns listed and described
	 * in the JESD216B specification, the 0xff value works for all memories
	 * and all manufacturers.
	 */
	cmd_sz = t[0].len;
	memset(flash->command + cmd_sz - dummy, 0xff, dummy);

	/* split the op code and address bytes into two transfers if needed. */
	data_idx = 1;
	if (addr_nbits != inst_nbits) {
		t[0].len = 1;

		t[1].tx_buf = &flash->command[1];
		t[1].tx_nbits = addr_nbits;
		t[1].len = cmd_sz - 1;
		spi_message_add_tail(&t[1], &m);

		data_idx = 2;
	}

	t[data_idx].rx_buf = buf;
	t[data_idx].rx_nbits = data_nbits;
	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
			       spi_max_message_size(spi) - cmd_sz);
	spi_message_add_tail(&t[data_idx], &m);

	ret = spi_sync(spi, &m);
	if (ret)
		return ret;

	ret = m.actual_length - cmd_sz;
	if (ret < 0)
		return -EIO;
	return ret;
}

3.4 m25p80_write

static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
			    const u_char *buf)
{
	struct m25p *flash = nor->priv;
	struct spi_device *spi = flash->spi;
	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
	struct spi_transfer t[3] = {};
	struct spi_message m;
	int cmd_sz = m25p_cmdsz(nor);
	ssize_t ret;

	/* get transfer protocols. */
	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);

	spi_message_init(&m);

	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
		cmd_sz = 1;

	flash->command[0] = nor->program_opcode;
	m25p_addr2cmd(nor, to, flash->command);

	t[0].tx_buf = flash->command;
	t[0].tx_nbits = inst_nbits;
	t[0].len = cmd_sz;
	spi_message_add_tail(&t[0], &m);

	/* split the op code and address bytes into two transfers if needed. */
	data_idx = 1;
	if (addr_nbits != inst_nbits) {
		t[0].len = 1;

		t[1].tx_buf = &flash->command[1];
		t[1].tx_nbits = addr_nbits;
		t[1].len = cmd_sz - 1;
		spi_message_add_tail(&t[1], &m);

		data_idx = 2;
	}

	t[data_idx].tx_buf = buf;
	t[data_idx].tx_nbits = data_nbits;
	t[data_idx].len = len;
	spi_message_add_tail(&t[data_idx], &m);

	ret = spi_sync(spi, &m);
	if (ret)
		return ret;

	ret = m.actual_length - cmd_sz;
	if (ret < 0)
		return -EIO;
	return ret;
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
M25P80 是一款常见的 SPI NOR Flash 存储器芯片,它被广泛应用于嵌入式系统中。在 Linux 系统中,可以使用 MTD(Memory Technology Devices)子系统来管理和访问 SPI NOR Flash 存储器。下面是一个简单的示例,演示如何在 Linux 中使用 MTD 子系统访问 M25P80 芯片。 1. 确认 M25P80 芯片已经连接到 Linux 系统的 SPI 总线上,并且 SPI 总线驱动程序已经加载成功。 2. 在 Linux 系统中安装 mtd-utils 工具包,该工具包提供了一些用于操作 MTD 设备的实用工具。 3. 在 Linux 中加载 spi-nor 驱动程序,该驱动程序支持对 SPI NOR Flash 存储器的访问。可以使用 modprobe 命令加载该驱动程序: ``` modprobe spi-nor ``` 4. 在 Linux 中加载 mtdchar 驱动程序,该驱动程序支持将 MTD 设备映射为字符设备。可以使用 modprobe 命令加载该驱动程序: ``` modprobe mtdchar ``` 5. 在 Linux 中创建一个 MTD 设备,并将其与 M25P80 芯片进行关联。可以使用 flash_eraseall 命令来擦除整个设备,并使用 flashcp 命令将一个镜像文件写入该设备。例如: ``` flash_eraseall /dev/mtd0 flashcp image.bin /dev/mtd0 ``` 6. 在 Linux 中挂载 MTD 设备,可以将其挂载为 JFFS2 文件系统。可以使用以下命令来挂载设备: ``` mount -t jffs2 /dev/mtdblock0 /mnt/flash ``` 7. 现在,可以在 /mnt/flash 目录中访问 M25P80 芯片中存储的数据了。可以使用 cp 命令将文件复制到 M25P80 芯片中,也可以使用 cat 命令查看文件内容。例如: ``` cp file.txt /mnt/flash cat /mnt/flash/file.txt ``` 上述步骤只是一个简单的示例,实际上在使用 MTD 子系统访问 SPI NOR Flash 存储器时,还需要进行其他配置和设置。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值