From 49bd706aac8fe7fa90988ecd3fd5c276575b194e Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 4 Aug 2015 10:25:22 -0500 Subject: [PATCH] mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read QSPI may failed to map enough memory (256MB) for AHB read in previous implementation, especially in 3G/1G memory layout kernel. Dynamically map memory to avoid such issue. This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory for AHB read, it should be enough for common scenarios, and the side effect (0.6% performance drop) is minor. Previous implementation root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K 32768+0 records in 32768+0 records out 33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1 1+0 records in 1+0 records out 33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s After applied the patch root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K 32768+0 records in 32768+0 records out 33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1 1+0 records in 1+0 records out 33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s Signed-off-by: Han Xu Signed-off-by: Frank Li Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 55 +++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 1946c6da76cd..2d460368ce5f 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -192,6 +192,8 @@ #define SEQID_EN4B 10 #define SEQID_BRWR 11 +#define QUADSPI_MIN_IOMAP SZ_4M + enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, @@ -223,8 +225,10 @@ struct fsl_qspi { struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; struct spi_nor nor[FSL_QSPI_MAX_CHIP]; void __iomem *iobase; - void __iomem *ahb_base; /* Used when read from AHB bus */ + void __iomem *ahb_addr; u32 memmap_phy; + u32 memmap_offs; + u32 memmap_len; struct clk *clk, *clk_en; struct device *dev; struct completion c; @@ -732,11 +736,42 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from, struct fsl_qspi *q = nor->priv; u8 cmd = nor->read_opcode; - dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", - cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); + /* if necessary,ioremap buffer before AHB read, */ + if (!q->ahb_addr) { + q->memmap_offs = q->chip_base_addr + from; + q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; + + q->ahb_addr = ioremap_nocache( + q->memmap_phy + q->memmap_offs, + q->memmap_len); + if (!q->ahb_addr) { + dev_err(q->dev, "ioremap failed\n"); + return -ENOMEM; + } + /* ioremap if the data requested is out of range */ + } else if (q->chip_base_addr + from < q->memmap_offs + || q->chip_base_addr + from + len > + q->memmap_offs + q->memmap_len) { + iounmap(q->ahb_addr); + + q->memmap_offs = q->chip_base_addr + from; + q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; + q->ahb_addr = ioremap_nocache( + q->memmap_phy + q->memmap_offs, + q->memmap_len); + if (!q->ahb_addr) { + dev_err(q->dev, "ioremap failed\n"); + return -ENOMEM; + } + } + + dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n", + cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, + len); /* Read out the data directly from the AHB buffer.*/ - memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); + memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, + len); *retlen += len; return 0; @@ -821,9 +856,11 @@ static int fsl_qspi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); - q->ahb_base = devm_ioremap_resource(dev, res); - if (IS_ERR(q->ahb_base)) - return PTR_ERR(q->ahb_base); + if (!devm_request_mem_region(dev, res->start, resource_size(res), + res->name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return -EBUSY; + } q->memmap_phy = res->start; @@ -989,6 +1026,10 @@ static int fsl_qspi_remove(struct platform_device *pdev) mutex_destroy(&q->lock); clk_unprepare(q->clk); clk_unprepare(q->clk_en); + + if (q->ahb_addr) + iounmap(q->ahb_addr); + return 0; } -- 2.20.1