summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorHan Xu <han.xu@freescale.com>2015-08-04 10:25:22 -0500
committerBrian Norris <computersforpeace@gmail.com>2015-08-06 10:07:39 -0700
commit49bd706aac8fe7fa90988ecd3fd5c276575b194e (patch)
treedb73c27e64a053be286acdfb0682852adf916345 /drivers/mtd
parent43163022927b6e7d202a7e6f939c3f392465494d (diff)
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 <han.xu@freescale.com> Signed-off-by: Frank Li <Frank.Li@freescale.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c55
1 files 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;
}