linux挂载QSPI FLASH

常用的flash设备有:nor-flash、nand-flash、qspi-flash。对于qspi-flash设备,linux要想正常挂载的话,必须要注册qspi控制器驱动、qspi设备驱动两种驱动文件。qspi控制器驱动主要是初始化好控制器的寄存器,而qspi设备驱动则是对应的flash操作接口以及调用到块设备驱动,进行块设备解析。

对于flash设备驱动的架构,可以参阅博主的上篇文章。这里我们从字符设备的角度分析下qspi-flash的驱动架构。以zynq为例来分析:

一、qspi控制器驱动

qspi控制器驱动代码位于drivers\spi\spi-zynq-qspi.c中。在drivers\spi\目录下有很多spi驱动的代码,如果你并不知道qspi驱动应该看哪个文件,可以通过设备树中qspi节点中的compatiabe属性来反推,例如设备树中的qspi属性为xlnx,zynq-qspi-1.0,在内核源码里搜索xlnx,zynq-qspi-1.0,就可以找到对应的驱动文件。

static int zynq_qspi_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct spi_master *master;
	struct zynq_qspi *xqspi;
	struct resource *res;
	u32 num_cs;

	master = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
	if (!master)
		return -ENOMEM;

	xqspi = spi_master_get_devdata(master);
	master->dev.of_node = pdev->dev.of_node;
	platform_set_drvdata(pdev, master);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	xqspi->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(xqspi->regs)) {
		ret = PTR_ERR(xqspi->regs);
		goto remove_master;
	}

	if (of_property_read_u32(pdev->dev.of_node, "is-dual",
				 &xqspi->is_dual)) {
		dev_warn(&pdev->dev, "couldn't determine configuration info");
		dev_warn(&pdev->dev, "about dual memories. defaulting to single memory\n");
	}

	xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
	if (IS_ERR(xqspi->pclk)) {
		dev_err(&pdev->dev, "pclk clock not found.\n");
		ret = PTR_ERR(xqspi->pclk);
		goto remove_master;
	}

	xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
	if (IS_ERR(xqspi->refclk)) {
		dev_err(&pdev->dev, "ref_clk clock not found.\n");
		ret = PTR_ERR(xqspi->refclk);
		goto remove_master;
	}

	ret = clk_prepare_enable(xqspi->pclk);
	if (ret) {
		dev_err(&pdev->dev, "Unable to enable APB clock.\n");
		goto remove_master;
	}

	ret = clk_prepare_enable(xqspi->refclk);
	if (ret) {
		dev_err(&pdev->dev, "Unable to enable device clock.\n");
		goto clk_dis_pclk;
	}

	/* QSPI controller initializations */
	zynq_qspi_init_hw(xqspi);

	xqspi->irq = platform_get_irq(pdev, 0);
	if (xqspi->irq <= 0) {
		ret = -ENXIO;
		dev_err(&pdev->dev, "irq resource not found\n");
		goto remove_master;
	}
	ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq,
			       0, pdev->name, master);
	if (ret != 0) {
		ret = -ENXIO;
		dev_err(&pdev->dev, "request_irq failed\n");
		goto remove_master;
	}

	ret = of_property_read_u32(pdev->dev.of_node, "num-cs",
				   &num_cs);
	if (ret < 0)
		master->num_chipselect = ZYNQ_QSPI_DEFAULT_NUM_CS;
	else
		master->num_chipselect = num_cs;

	master->setup = zynq_qspi_setup;
	master->set_cs = zynq_qspi_chipselect;
	master->transfer_one = zynq_qspi_start_transfer;
	master->prepare_transfer_hardware = zynq_prepare_transfer_hardware;
	master->unprepare_transfer_hardware = zynq_unprepare_transfer_hardware;
	master->flags = SPI_MASTER_QUAD_MODE;

	master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
	master->bits_per_word_mask = SPI_BPW_MASK(8);
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
			    SPI_TX_DUAL | SPI_TX_QUAD;

	ret = spi_register_master(master);
	if (ret) {
		dev_err(&pdev->dev, "spi_register_master failed\n");
		goto clk_dis_all;
	}

	return ret;

clk_dis_all:
	clk_disable_unprepare(xqspi->refclk);
clk_dis_pclk:
	clk_disable_unprepare(xqspi->pclk);
remove_master:
	spi_master_put(master);
	return ret;
}

从上述代码中可以看到,控制器驱动代码做的工作就是获取设备树中的硬件资源,初始化硬件,以及实现spi-master中的方法。如果编译内核时将spi编译进去,在生成的操作系统中,我们可以通过

ls /sys/bus/

可以看到sys/bus目录下有spi总线。

但是这时候你在sys/bus/spi/driver目录下看不到任何驱动,因为你注册的zynq-qspi驱动是平台总线下的,我么可以利用  ls sys/bus/platform/drivers

现在控制器驱动已经加载上去了,spi总线也注册了,但是spi的设备驱动还没有加载,因此还需要将spi设备驱动编译进内核。spi设备常用的就是mtd设备,因此需要去看看块设备驱动中包含哪些东西?

这时候就需要看硬件上采用的flash型号了,市面上flash型号太多了,linux不可能把每个型号都列出来,只能列出一部分的型号,但好在flash的接口是标准的,要么是JEDEC标准,要么是CFI标准,因此,内核驱动源码中只要实现这两种接口的驱动就行。

我们以m25p80.c为例,来分析spi设备驱动的代码。

static int m25p_probe(struct spi_device *spi)
{
	struct flash_platform_data	*data;
	struct m25p *flash;
	struct spi_nor *nor;
	enum read_mode mode = SPI_NOR_NORMAL;
	char *flash_name;
	int ret;

	data = dev_get_platdata(&spi->dev);

	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
	if (!flash)
		return -ENOMEM;

	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;
	nor->spi = spi;

	if (spi->mode & SPI_RX_QUAD)
		mode = SPI_NOR_QUAD;
	else if (spi->mode & SPI_RX_DUAL)
		mode = SPI_NOR_DUAL;

	if (data && data->name)
		nor->mtd.name = data->name;

	/* For some (historical?) reason many platforms provide two different
	 * names in flash_platform_data: "name" and "type". Quite often name is
	 * set to "m25p80" and then "type" provides a real chip name.
	 * If that's the case, respect "type" and ignore a "name".
	 */
	if (data && data->type)
		flash_name = data->type;
	else if (!strcmp(spi->modalias, "spi-nor"))
		flash_name = NULL; /* auto-detect */
	else
		flash_name = spi->modalias;

	ret = spi_nor_scan(nor, flash_name, mode);
	if (ret)
		return ret;

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

显然,probe函数中主要实现了flash设备的读写操作,以及注册块设备。在probe函数中有个很重要的函数spi_nor_scan,追踪下这个函数:

spi_nor_scan->spi_nor_read_id->spi_nor_ids

在spi_nor_ids中基本列举了所有的flash型号。因此,就可以理解,为什么设备树中描述的实际的flash型号,但驱动却无法与设备匹配上的问题。

这是因为spi设备驱动程序中支持的id_table中只列举了少许flash设备,而驱动和设备匹配的规则是先比较of_match_table,如果of_match_table没有匹配的,则比较id_table中的设备,因此,在写flash设备树树时必须使用id_table中列举的设备名称,这样设备和驱动才能match上,驱动程序的probe函数才能被执行。

spi_nor_scan函数会去读取flash设备的ID,而并不是直接用设备树中的compatible属性作为硬件信息,如果实际的flash  ID与设备树中定义的不同时,则会打印

因此,在写设备树文件时,只需要写一个id_table中列举的与实际flash设备相近的设备就可以了,spi设备驱动会自动读取flash的ID来获取硬件信息。

查看设备与驱动有没有匹配上可以通过  ls sys/bus/spi/drivers/m25p80/来查看

之前将设备树中的compatible属性写为s25fl128s,驱动就一直无法执行probe函数,而将compatible属性改为s25fl256s1,就可以看到驱动和设备match上了,probe函数正常执行。

SPI通信是通过主机发起的,因此CPU侧外挂的控制器要作为主机控制器,来发起对SPI设备的访问。

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
裁剪QSPI闪存是指在Linux系统中对QSPI闪存进行部分容量的利用,以满足特定需求或优化存储空间的利用方式。以下是裁剪QSPI闪存的一般步骤: 1. 确定需求:首先需要确定裁剪QSPI闪存的目的和需求,例如需要减小系统的存储空间占用或只保留特定的文件系统等。 2. 选择裁剪工具:根据系统和闪存型号选择适合的裁剪工具。常用的工具包括`mtd-utils`、`fw_printenv`、`fw_setenv`等。 3. 备份闪存数据:在进行裁剪操作之前,建议先备份闪存中的重要数据,以防止意外损失。 4. 分析闪存布局:使用相关工具分析闪存的分区和布局情况。可以通过`cat /proc/mtd`命令或`mtdinfo /dev/mtdX`命令查看闪存信息。 5. 裁剪文件系统:根据需求,可以删除或者调整文件系统中的文件和目录,以减小闪存占用空间。注意要保留必要的系统文件和配置文件。 6. 调整内核参数:根据需要,可以调整内核参数以减小内核镜像的大小。可以通过编辑内核配置文件或者使用`make menuconfig`命令进行相关设置。 7. 重新构建固件:根据所做的更改,重新构建固件映像文件。具体步骤根据所使用的开发环境和工具链可能会有所不同。 8. 烧写固件:将重新构建的固件映像文件烧写到QSPI闪存中。可以使用相关工具,如`flashcp`命令或者烧写工具来完成。 注意:在进行裁剪操作之前,请确保充分了解闪存的布局和系统的需求,并谨慎操作以避免数据丢失或系统不可用的情况发生。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值