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设备的访问。
本文地址:https://blog.csdn.net/m0_37765662/article/details/110541016