summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-09-02 12:16:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-02 12:16:24 -0700
commit91a247d7d3694a161092931ea4e0b13c11b8e9a0 (patch)
treee7b7f1cf30ba702e533a95e7ae1939efee3c9e8f /drivers
parent8bdc69b764013a9b5ebeef7df8f314f1066c5d79 (diff)
parent718e38b4d96085b4dab08d3d32e5e033aa1ba6e9 (diff)
downloadop-kernel-dev-91a247d7d3694a161092931ea4e0b13c11b8e9a0.zip
op-kernel-dev-91a247d7d3694a161092931ea4e0b13c11b8e9a0.tar.gz
Merge tag 'for-linus-20150901' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris: "SPI NOR: - reduce virtual address space requirements for fsl-quadspi memory map - new fsl-quadspi IP support: imx6ul-qspi and imx7d-qspi - add new NOR flash device support - add new driver for NXP SPI Flash Interface (SPIFI) - stop abusing SPI API structs for non-SPI framework - fixup DT table matching for new "jedec,spi-nor" string NAND: - brcmnand: fix big endian MIPS macro usage - denali: refactor to use devres, dev_*() printing, etc. - OMAP ELM: change the module alias to actually be usable - pxa3xx_nand: fixup a few command sequencing issues -- both new and old - race conditions in the IRQ handler status clearing - problems when a bootloader left interrupts pending - config issues when overriding the bootloader configuration - new flash device support - sunxi_nand: - optimize timing configuration by calculation, rather than fixed fail-safe values - use EDO setting from ONFI - r852: fix compiler warnings - davinci: add 4KB page support Core: - oobtest: correct debug print information" * tag 'for-linus-20150901' of git://git.infradead.org/linux-mtd: (42 commits) mtd: mtd_oobtest: Fix the address offset with vary_offset case mtd: blkdevs: fix switch-bool compilation warning mtd: spi-nor: stop (ab)using struct spi_device_id mtd: nand: add Toshiba TC58NVG0S3E to nand_ids table mtd: dataflash: Export OF module alias information nand: pxa3xx: Increase READ_ID buffer and make the size static mtd: nand: pxa3xx-nand: fix random command timeouts mtd: nand: pxa3xx_nand: fix early spurious interrupt mtd: pxa3xx_nand: add a default chunk size mtd: omap_elm: Fix module alias mtd: physmap_of: fix null pointer deference when kzalloc returns null mtd: nettel: do not ignore mtd_device_register() failure in nettel_init() mtd: denali_pci: switch to dev_err() mtd: denali_pci: refactor driver using devres API mtd: denali_pci: use module_pci_driver() macro mtd: denali: hide core part from user in Kconfig mtd: spi-nor: add Spansion S25FL204K support mtd: spi-nor: Improve Kconfig help text for SPI_FSL_QUADSPI mtd: spi-nor: add driver for NXP SPI Flash Interface (SPIFI) doc: dt: add documentation for nxp,lpc1773-spifi ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/devices/m25p80.c18
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c1
-rw-r--r--drivers/mtd/maps/nettel.c13
-rw-r--r--drivers/mtd/maps/physmap_of.c6
-rw-r--r--drivers/mtd/mtd_blkdevs.c8
-rw-r--r--drivers/mtd/nand/Kconfig13
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.h4
-rw-r--r--drivers/mtd/nand/davinci_nand.c42
-rw-r--r--drivers/mtd/nand/denali_pci.c43
-rw-r--r--drivers/mtd/nand/nand_ids.c4
-rw-r--r--drivers/mtd/nand/nandsim.c28
-rw-r--r--drivers/mtd/nand/omap_elm.c2
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c61
-rw-r--r--drivers/mtd/nand/r852.c2
-rw-r--r--drivers/mtd/nand/sunxi_nand.c88
-rw-r--r--drivers/mtd/spi-nor/Kconfig14
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c265
-rw-r--r--drivers/mtd/spi-nor/nxp-spifi.c482
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c69
-rw-r--r--drivers/mtd/tests/oobtest.c18
21 files changed, 950 insertions, 232 deletions
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index d313f948b..9cd3631 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -223,8 +223,6 @@ static int m25p_probe(struct spi_device *spi)
*/
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;
@@ -289,19 +287,25 @@ static const struct spi_device_id m25p_ids[] = {
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
- /*
- * Generic support for SPI NOR that can be identified by the JEDEC READ
- * ID opcode (0x9F). Use this, if possible.
- */
- {"spi-nor"},
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
+static const struct of_device_id m25p_of_table[] = {
+ /*
+ * Generic compatibility for SPI NOR that can be identified by the
+ * JEDEC READ ID opcode (0x9F). Use this, if possible.
+ */
+ { .compatible = "jedec,spi-nor" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, m25p_of_table);
+
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.owner = THIS_MODULE,
+ .of_match_table = m25p_of_table,
},
.id_table = m25p_ids,
.probe = m25p_probe,
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 0099aba..df6f611 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -102,6 +102,7 @@ static const struct of_device_id dataflash_dt_ids[] = {
{ .compatible = "atmel,dataflash", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
#endif
/* ......................................................................... */
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
index eadcfff..a577ef8 100644
--- a/drivers/mtd/maps/nettel.c
+++ b/drivers/mtd/maps/nettel.c
@@ -385,20 +385,28 @@ static int __init nettel_init(void)
}
rc = mtd_device_register(intel_mtd, nettel_intel_partitions,
num_intel_partitions);
+ if (rc)
+ goto out_map_destroy;
#endif
if (amd_mtd) {
rc = mtd_device_register(amd_mtd, nettel_amd_partitions,
num_amd_partitions);
+ if (rc)
+ goto out_mtd_unreg;
}
#ifdef CONFIG_MTD_CFI_INTELEXT
register_reboot_notifier(&nettel_notifier_block);
#endif
- return(rc);
+ return rc;
+out_mtd_unreg:
#ifdef CONFIG_MTD_CFI_INTELEXT
+ mtd_device_unregister(intel_mtd);
+out_map_destroy:
+ map_destroy(intel_mtd);
out_unmap1:
iounmap(nettel_intel_map.virt);
#endif
@@ -407,8 +415,7 @@ out_unmap2:
iounmap(nettel_mmcrp);
iounmap(nettel_amd_map.virt);
- return(rc);
-
+ return rc;
}
/****************************************************************************/
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 774b32f..3e614e9 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -130,6 +130,8 @@ static const char * const *of_get_probes(struct device_node *dp)
count++;
res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return NULL;
count = 0;
while (cplen > 0) {
res[count] = cp;
@@ -311,6 +313,10 @@ static int of_flash_probe(struct platform_device *dev)
ppdata.of_node = dp;
part_probe_types = of_get_probes(dp);
+ if (!part_probe_types) {
+ err = -ENOMEM;
+ goto err_out;
+ }
mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
NULL, 0);
of_free_probes(part_probe_types);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 41acc50..8830475 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -97,14 +97,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
if (req->cmd_flags & REQ_DISCARD)
return tr->discard(dev, block, nsect);
- switch(rq_data_dir(req)) {
- case READ:
+ if (rq_data_dir(req) == READ) {
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf))
return -EIO;
rq_flush_dcache_pages(req);
return 0;
- case WRITE:
+ } else {
if (!tr->writesect)
return -EIO;
@@ -113,9 +112,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
if (tr->writesect(dev, block, buf))
return -EIO;
return 0;
- default:
- printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
- return -EIO;
}
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5b2806a..3324281 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -42,23 +42,20 @@ config MTD_SM_COMMON
default n
config MTD_NAND_DENALI
- tristate "Support Denali NAND controller"
- depends on HAS_DMA
- help
- Enable support for the Denali NAND controller. This should be
- combined with either the PCI or platform drivers to provide device
- registration.
+ tristate
config MTD_NAND_DENALI_PCI
tristate "Support Denali NAND controller on Intel Moorestown"
- depends on PCI && MTD_NAND_DENALI
+ select MTD_NAND_DENALI
+ depends on HAS_DMA && PCI
help
Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core.
config MTD_NAND_DENALI_DT
tristate "Support Denali NAND controller as a DT device"
- depends on HAVE_CLK && MTD_NAND_DENALI
+ select MTD_NAND_DENALI
+ depends on HAS_DMA && HAVE_CLK
help
Enable the driver for NAND flash on platforms using a Denali NAND
controller as a DT device.
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h
index a20c736..169f99e 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.h
+++ b/drivers/mtd/nand/brcmnand/brcmnand.h
@@ -50,7 +50,7 @@ static inline u32 brcmnand_readl(void __iomem *addr)
* Other architectures (e.g., ARM) either do not support big endian, or
* else leave I/O in little endian mode.
*/
- if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
return __raw_readl(addr);
else
return readl_relaxed(addr);
@@ -59,7 +59,7 @@ static inline u32 brcmnand_readl(void __iomem *addr)
static inline void brcmnand_writel(u32 val, void __iomem *addr)
{
/* See brcmnand_readl() comments */
- if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
__raw_writel(val, addr);
else
writel_relaxed(val, addr);
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index feb6d18..b9080130 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -520,6 +520,32 @@ static struct nand_ecclayout hwecc4_2048 = {
},
};
+/*
+ * An ECC layout for using 4-bit ECC with large-page (4096bytes) flash,
+ * storing ten ECC bytes plus the manufacturer's bad block marker byte,
+ * and not overlapping the default BBT markers.
+ */
+static struct nand_ecclayout hwecc4_4096 = {
+ .eccbytes = 80,
+ .eccpos = {
+ /* at the end of spare sector */
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ },
+ .oobfree = {
+ /* 2 bytes at offset 0 hold manufacturer badblock markers */
+ {.offset = 2, .length = 46, },
+ /* 5 bytes at offset 8 hold BBT markers */
+ /* 8 bytes at offset 16 hold JFFS2 clean markers */
+ },
+};
+
#if defined(CONFIG_OF)
static const struct of_device_id davinci_nand_of_match[] = {
{.compatible = "ti,davinci-nand", },
@@ -796,18 +822,12 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done;
}
+ if (chunks == 8) {
+ info->ecclayout = hwecc4_4096;
+ info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
+ goto syndrome_done;
+ }
- /* 4KiB page chips are not yet supported. The eccpos from
- * nand_ecclayout cannot hold 80 bytes and change to eccpos[]
- * breaks userspace ioctl interface with mtd-utils. Once we
- * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
- * for the 4KiB page chips.
- *
- * TODO: Note that nand_ecclayout has now been expanded and can
- * hold plenty of OOB entries.
- */
- dev_warn(&pdev->dev, "no 4-bit ECC support yet "
- "for 4KiB-page NAND\n");
ret = -EIO;
goto err;
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index 6e2f387..de31514 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -30,19 +30,19 @@ MODULE_DEVICE_TABLE(pci, denali_pci_ids);
static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- int ret = -ENODEV;
+ int ret;
resource_size_t csr_base, mem_base;
unsigned long csr_len, mem_len;
struct denali_nand_info *denali;
- denali = kzalloc(sizeof(*denali), GFP_KERNEL);
+ denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
if (!denali)
return -ENOMEM;
- ret = pci_enable_device(dev);
+ ret = pcim_enable_device(dev);
if (ret) {
- pr_err("Spectra: pci_enable_device failed.\n");
- goto failed_alloc_memery;
+ dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n");
+ return ret;
}
if (id->driver_data == INTEL_CE4100) {
@@ -69,20 +69,19 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
ret = pci_request_regions(dev, DENALI_NAND_NAME);
if (ret) {
- pr_err("Spectra: Unable to request memory regions\n");
- goto failed_enable_dev;
+ dev_err(&dev->dev, "Spectra: Unable to request memory regions\n");
+ return ret;
}
denali->flash_reg = ioremap_nocache(csr_base, csr_len);
if (!denali->flash_reg) {
- pr_err("Spectra: Unable to remap memory region\n");
- ret = -ENOMEM;
- goto failed_req_regions;
+ dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
+ return -ENOMEM;
}
denali->flash_mem = ioremap_nocache(mem_base, mem_len);
if (!denali->flash_mem) {
- pr_err("Spectra: ioremap_nocache failed!");
+ dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
ret = -ENOMEM;
goto failed_remap_reg;
}
@@ -99,13 +98,6 @@ failed_remap_mem:
iounmap(denali->flash_mem);
failed_remap_reg:
iounmap(denali->flash_reg);
-failed_req_regions:
- pci_release_regions(dev);
-failed_enable_dev:
- pci_disable_device(dev);
-failed_alloc_memery:
- kfree(denali);
-
return ret;
}
@@ -117,9 +109,6 @@ static void denali_pci_remove(struct pci_dev *dev)
denali_remove(denali);
iounmap(denali->flash_reg);
iounmap(denali->flash_mem);
- pci_release_regions(dev);
- pci_disable_device(dev);
- kfree(denali);
}
static struct pci_driver denali_pci_driver = {
@@ -129,14 +118,4 @@ static struct pci_driver denali_pci_driver = {
.remove = denali_pci_remove,
};
-static int denali_init_pci(void)
-{
- return pci_register_driver(&denali_pci_driver);
-}
-module_init(denali_init_pci);
-
-static void denali_exit_pci(void)
-{
- pci_unregister_driver(&denali_pci_driver);
-}
-module_exit(denali_exit_pci);
+module_pci_driver(denali_pci_driver);
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 7124400..a8804a3 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -29,6 +29,10 @@ struct nand_flash_dev nand_flash_ids[] = {
* listed by full ID. We list them first so that we can easily identify
* the most specific match.
*/
+ {"TC58NVG0S3E 1G 3.3V 8-bit",
+ { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
+ SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
+ 2 },
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 52c0c1a..95d0cc4 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -649,7 +649,8 @@ static void free_device(struct nandsim *ns)
kmem_cache_free(ns->nand_pages_slab,
ns->pages[i].byte);
}
- kmem_cache_destroy(ns->nand_pages_slab);
+ if (ns->nand_pages_slab)
+ kmem_cache_destroy(ns->nand_pages_slab);
vfree(ns->pages);
}
}
@@ -729,8 +730,7 @@ static int init_nandsim(struct mtd_info *mtd)
/* Fill the partition_info structure */
if (parts_num > ARRAY_SIZE(ns->partitions)) {
NS_ERR("too many partitions.\n");
- ret = -EINVAL;
- goto error;
+ return -EINVAL;
}
remains = ns->geom.totsz;
next_offset = 0;
@@ -739,14 +739,12 @@ static int init_nandsim(struct mtd_info *mtd)
if (!part_sz || part_sz > remains) {
NS_ERR("bad partition size.\n");
- ret = -EINVAL;
- goto error;
+ return -EINVAL;
}
ns->partitions[i].name = get_partition_name(i);
if (!ns->partitions[i].name) {
NS_ERR("unable to allocate memory.\n");
- ret = -ENOMEM;
- goto error;
+ return -ENOMEM;
}
ns->partitions[i].offset = next_offset;
ns->partitions[i].size = part_sz;
@@ -757,14 +755,12 @@ static int init_nandsim(struct mtd_info *mtd)
if (remains) {
if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
NS_ERR("too many partitions.\n");
- ret = -EINVAL;
- goto error;
+ return -EINVAL;
}
ns->partitions[i].name = get_partition_name(i);
if (!ns->partitions[i].name) {
NS_ERR("unable to allocate memory.\n");
- ret = -ENOMEM;
- goto error;
+ return -ENOMEM;
}
ns->partitions[i].offset = next_offset;
ns->partitions[i].size = remains;
@@ -792,24 +788,18 @@ static int init_nandsim(struct mtd_info *mtd)
printk("options: %#x\n", ns->options);
if ((ret = alloc_device(ns)) != 0)
- goto error;
+ return ret;
/* Allocate / initialize the internal buffer */
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob);
- ret = -ENOMEM;
- goto error;
+ return -ENOMEM;
}
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
return 0;
-
-error:
- free_device(ns);
-
- return ret;
}
/*
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c
index 376bfe1..235ec79 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/omap_elm.c
@@ -574,5 +574,5 @@ module_platform_driver(elm_driver);
MODULE_DESCRIPTION("ELM driver for BCH error correction");
MODULE_AUTHOR("Texas Instruments");
-MODULE_ALIAS("platform: elm");
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 1259cc5..740983a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -45,10 +45,13 @@
/*
* Define a buffer size for the initial command that detects the flash device:
- * STATUS, READID and PARAM. The largest of these is the PARAM command,
- * needing 256 bytes.
+ * STATUS, READID and PARAM.
+ * ONFI param page is 256 bytes, and there are three redundant copies
+ * to be read. JEDEC param page is 512 bytes, and there are also three
+ * redundant copies to be read.
+ * Hence this buffer should be at least 512 x 3. Let's pick 2048.
*/
-#define INIT_BUFFER_SIZE 256
+#define INIT_BUFFER_SIZE 2048
/* registers and bit definitions */
#define NDCR (0x00) /* Control register */
@@ -126,6 +129,13 @@
#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
+/*
+ * This should be large enough to read 'ONFI' and 'JEDEC'.
+ * Let's use 7 bytes, which is the maximum ID count supported
+ * by the controller (see NDCR_RD_ID_CNT_MASK).
+ */
+#define READ_ID_BYTES 7
+
/* macros for registers read/write */
#define nand_writel(info, off, val) \
writel_relaxed((val), (info)->mmio_base + (off))
@@ -173,8 +183,6 @@ struct pxa3xx_nand_host {
/* calculated from pxa3xx_nand_flash data */
unsigned int col_addr_cycles;
unsigned int row_addr_cycles;
- size_t read_id_bytes;
-
};
struct pxa3xx_nand_info {
@@ -439,8 +447,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
ndcr |= NDCR_ND_RUN;
/* clear status bits and run */
- nand_writel(info, NDCR, 0);
nand_writel(info, NDSR, NDSR_MASK);
+ nand_writel(info, NDCR, 0);
nand_writel(info, NDCR, ndcr);
}
@@ -675,8 +683,14 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
is_ready = 1;
}
+ /*
+ * Clear all status bit before issuing the next command, which
+ * can and will alter the status bits and will deserve a new
+ * interrupt on its own. This lets the controller exit the IRQ
+ */
+ nand_writel(info, NDSR, status);
+
if (status & NDSR_WRCMDREQ) {
- nand_writel(info, NDSR, NDSR_WRCMDREQ);
status &= ~NDSR_WRCMDREQ;
info->state = STATE_CMD_HANDLE;
@@ -697,8 +711,6 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
nand_writel(info, NDCB0, info->ndcb3);
}
- /* clear NDSR to let the controller exit the IRQ */
- nand_writel(info, NDSR, status);
if (is_completed)
complete(&info->cmd_complete);
if (is_ready)
@@ -899,18 +911,18 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
break;
case NAND_CMD_PARAM:
- info->buf_count = 256;
+ info->buf_count = INIT_BUFFER_SIZE;
info->ndcb0 |= NDCB0_CMD_TYPE(0)
| NDCB0_ADDR_CYC(1)
| NDCB0_LEN_OVRD
| command;
info->ndcb1 = (column & 0xFF);
- info->ndcb3 = 256;
- info->data_size = 256;
+ info->ndcb3 = INIT_BUFFER_SIZE;
+ info->data_size = INIT_BUFFER_SIZE;
break;
case NAND_CMD_READID:
- info->buf_count = host->read_id_bytes;
+ info->buf_count = READ_ID_BYTES;
info->ndcb0 |= NDCB0_CMD_TYPE(3)
| NDCB0_ADDR_CYC(1)
| command;
@@ -1247,9 +1259,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
return -EINVAL;
}
- /* calculate flash information */
- host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
-
/* calculate addressing information */
host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
@@ -1265,7 +1274,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
- ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
+ ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
ndcr |= NDCR_SPARE_EN; /* enable spare by default */
info->reg_ndcr = ndcr;
@@ -1276,23 +1285,10 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{
- /*
- * We set 0 by hard coding here, for we don't support keep_config
- * when there is more than one chip attached to the controller
- */
- struct pxa3xx_nand_host *host = info->host[0];
uint32_t ndcr = nand_readl(info, NDCR);
- if (ndcr & NDCR_PAGE_SZ) {
- /* Controller's FIFO size */
- info->chunk_size = 2048;
- host->read_id_bytes = 4;
- } else {
- info->chunk_size = 512;
- host->read_id_bytes = 2;
- }
-
/* Set an initial chunk size */
+ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
@@ -1473,6 +1469,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
goto KEEP_CONFIG;
+ /* Set a default chunk size */
+ info->chunk_size = 512;
+
ret = pxa3xx_nand_sensing(info);
if (ret) {
dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index 77e96d2..cc6bac5 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -466,7 +466,7 @@ static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
- uint16_t ecc_reg;
+ uint32_t ecc_reg;
uint8_t ecc_status, err_byte;
int i, error = 0;
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 6f93b29..f97a58d 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -99,6 +99,15 @@
NFC_CMD_INT_ENABLE | \
NFC_DMA_INT_ENABLE)
+/* define bit use in NFC_TIMING_CTL */
+#define NFC_TIMING_CTL_EDO BIT(8)
+
+/* define NFC_TIMING_CFG register layout */
+#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
+ (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \
+ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
+ (((tCAD) & 0x7) << 8))
+
/* define bit use in NFC_CMD */
#define NFC_CMD_LOW_BYTE GENMASK(7, 0)
#define NFC_CMD_HIGH_BYTE GENMASK(15, 8)
@@ -208,6 +217,7 @@ struct sunxi_nand_hw_ecc {
* @nand: base NAND chip structure
* @mtd: base MTD structure
* @clk_rate: clk_rate required for this NAND chip
+ * @timing_cfg TIMING_CFG register value for this NAND chip
* @selected: current active CS
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
@@ -217,6 +227,8 @@ struct sunxi_nand_chip {
struct nand_chip nand;
struct mtd_info mtd;
unsigned long clk_rate;
+ u32 timing_cfg;
+ u32 timing_ctl;
int selected;
int nsels;
struct sunxi_nand_chip_sel sels[0];
@@ -403,6 +415,8 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
}
}
+ writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
+ writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
writel(ctl, nfc->regs + NFC_REG_CTL);
sunxi_nand->selected = chip;
@@ -807,10 +821,33 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
return 0;
}
+static const s32 tWB_lut[] = {6, 12, 16, 20};
+static const s32 tRHW_lut[] = {4, 8, 12, 20};
+
+static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
+ u32 clk_period)
+{
+ u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
+ int i;
+
+ for (i = 0; i < lut_size; i++) {
+ if (clk_cycles <= lut[i])
+ return i;
+ }
+
+ /* Doesn't fit */
+ return -EINVAL;
+}
+
+#define sunxi_nand_lookup_timing(l, p, c) \
+ _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
+
static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings)
{
+ struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
u32 min_clk_period = 0;
+ s32 tWB, tADL, tWHR, tRHW, tCAD;
/* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period)
@@ -872,6 +909,48 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
if (timings->tWC_min > (min_clk_period * 2))
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+ /* T16 - T19 + tCAD */
+ tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
+ min_clk_period);
+ if (tWB < 0) {
+ dev_err(nfc->dev, "unsupported tWB\n");
+ return tWB;
+ }
+
+ tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
+ if (tADL > 3) {
+ dev_err(nfc->dev, "unsupported tADL\n");
+ return -EINVAL;
+ }
+
+ tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
+ if (tWHR > 3) {
+ dev_err(nfc->dev, "unsupported tWHR\n");
+ return -EINVAL;
+ }
+
+ tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
+ min_clk_period);
+ if (tRHW < 0) {
+ dev_err(nfc->dev, "unsupported tRHW\n");
+ return tRHW;
+ }
+
+ /*
+ * TODO: according to ONFI specs this value only applies for DDR NAND,
+ * but Allwinner seems to set this to 0x7. Mimic them for now.
+ */
+ tCAD = 0x7;
+
+ /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
+
+ /*
+ * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
+ * output cycle timings shall be used if the host drives tRC less than
+ * 30 ns.
+ */
+ chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0;
/* Convert min_clk_period from picoseconds to nanoseconds */
min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
@@ -884,8 +963,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
*/
chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
- /* TODO: configure T16-T19 */
-
return 0;
}
@@ -1376,13 +1453,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, nfc);
- /*
- * TODO: replace these magic values with proper flags as soon as we
- * know what they are encoding.
- */
- writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
- writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
-
ret = sunxi_nand_chips_init(dev, nfc);
if (ret) {
dev_err(dev, "failed to init nand chips\n");
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 64a4f0e..89bf4c1 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -26,6 +26,18 @@ config SPI_FSL_QUADSPI
depends on ARCH_MXC
help
This enables support for the Quad SPI controller in master mode.
- We only connect the NOR to this controller now.
+ This controller does not support generic SPI. It only supports
+ SPI NOR.
+
+config SPI_NXP_SPIFI
+ tristate "NXP SPI Flash Interface (SPIFI)"
+ depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
+ depends on HAS_IOMEM
+ help
+ Enable support for the NXP LPC SPI Flash Interface controller.
+
+ SPIFI is a specialized controller for connecting serial SPI
+ Flash. Enable this option if you have a device with a SPIFI
+ controller and want to access the Flash as a mtd device.
endif # MTD_SPI_NOR
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 6a7ce14..e53333e 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
+obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 52a872f..d32b7e0 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -26,6 +26,20 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+
+/* Controller needs driver to swap endian */
+#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
+/* Controller needs 4x internal clock */
+#define QUADSPI_QUIRK_4X_INT_CLK (1 << 1)
+/*
+ * TKT253890, Controller needs driver to fill txfifo till 16 byte to
+ * trigger data transfer even though extern data will not transferred.
+ */
+#define QUADSPI_QUIRK_TKT253890 (1 << 2)
+/* Controller cannot wake up from wait mode, TKT245618 */
+#define QUADSPI_QUIRK_TKT245618 (1 << 3)
/* The registers */
#define QUADSPI_MCR 0x00
@@ -191,9 +205,13 @@
#define SEQID_EN4B 10
#define SEQID_BRWR 11
+#define QUADSPI_MIN_IOMAP SZ_4M
+
enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID,
FSL_QUADSPI_IMX6SX,
+ FSL_QUADSPI_IMX7D,
+ FSL_QUADSPI_IMX6UL,
};
struct fsl_qspi_devtype_data {
@@ -201,20 +219,42 @@ struct fsl_qspi_devtype_data {
int rxfifo;
int txfifo;
int ahb_buf_size;
+ int driver_data;
};
static struct fsl_qspi_devtype_data vybrid_data = {
.devtype = FSL_QUADSPI_VYBRID,
.rxfifo = 128,
.txfifo = 64,
- .ahb_buf_size = 1024
+ .ahb_buf_size = 1024,
+ .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN,
};
static struct fsl_qspi_devtype_data imx6sx_data = {
.devtype = FSL_QUADSPI_IMX6SX,
.rxfifo = 128,
.txfifo = 512,
- .ahb_buf_size = 1024
+ .ahb_buf_size = 1024,
+ .driver_data = QUADSPI_QUIRK_4X_INT_CLK
+ | QUADSPI_QUIRK_TKT245618,
+};
+
+static struct fsl_qspi_devtype_data imx7d_data = {
+ .devtype = FSL_QUADSPI_IMX7D,
+ .rxfifo = 512,
+ .txfifo = 512,
+ .ahb_buf_size = 1024,
+ .driver_data = QUADSPI_QUIRK_TKT253890
+ | QUADSPI_QUIRK_4X_INT_CLK,
+};
+
+static struct fsl_qspi_devtype_data imx6ul_data = {
+ .devtype = FSL_QUADSPI_IMX6UL,
+ .rxfifo = 128,
+ .txfifo = 512,
+ .ahb_buf_size = 1024,
+ .driver_data = QUADSPI_QUIRK_TKT253890
+ | QUADSPI_QUIRK_4X_INT_CLK,
};
#define FSL_QSPI_MAX_CHIP 4
@@ -222,8 +262,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;
@@ -233,16 +275,28 @@ struct fsl_qspi {
u32 clk_rate;
unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip;
+ struct mutex lock;
+ struct pm_qos_request pm_qos_req;
};
-static inline int is_vybrid_qspi(struct fsl_qspi *q)
+static inline int needs_swap_endian(struct fsl_qspi *q)
+{
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN;
+}
+
+static inline int needs_4x_clock(struct fsl_qspi *q)
+{
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK;
+}
+
+static inline int needs_fill_txfifo(struct fsl_qspi *q)
{
- return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT253890;
}
-static inline int is_imx6sx_qspi(struct fsl_qspi *q)
+static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
{
- return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX;
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
}
/*
@@ -251,7 +305,7 @@ static inline int is_imx6sx_qspi(struct fsl_qspi *q)
*/
static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
{
- return is_vybrid_qspi(q) ? __swab32(a) : a;
+ return needs_swap_endian(q) ? __swab32(a) : a;
}
static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q)
@@ -343,14 +397,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Erase a sector */
lut_base = SEQID_SE * 4;
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_SE;
- addrlen = ADDR24BIT;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_SE;
- addrlen = ADDR32BIT;
- }
+ cmd = q->nor[0].erase_opcode;
+ addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
@@ -419,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
case SPINOR_OP_BRWR:
return SEQID_BRWR;
default:
+ if (cmd == q->nor[0].erase_opcode)
+ return SEQID_SE;
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break;
}
@@ -537,7 +587,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
/* clear the TX FIFO. */
tmp = readl(q->iobase + QUADSPI_MCR);
- writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
+ writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
/* fill the TX data to the FIFO */
for (j = 0, i = ((count + 3) / 4); j < i; j++) {
@@ -546,6 +596,11 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
txbuf++;
}
+ /* fill the TXFIFO upto 16 bytes for i.MX7d */
+ if (needs_fill_txfifo(q))
+ for (; i < 4; i++)
+ writel(tmp, q->iobase + QUADSPI_TBDR);
+
/* Trigger it */
ret = fsl_qspi_runcmd(q, opcode, to, count);
@@ -606,6 +661,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
q->iobase + QUADSPI_BFGENCR);
}
+/* This function was used to prepare and enable QSPI clock */
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
+{
+ int ret;
+
+ ret = clk_prepare_enable(q->clk_en);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(q->clk);
+ if (ret) {
+ clk_disable_unprepare(q->clk_en);
+ return ret;
+ }
+
+ if (needs_wakeup_wait_mode(q))
+ pm_qos_add_request(&q->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0);
+
+ return 0;
+}
+
+/* This function was used to disable and unprepare QSPI clock */
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q)
+{
+ if (needs_wakeup_wait_mode(q))
+ pm_qos_remove_request(&q->pm_qos_req);
+
+ clk_disable_unprepare(q->clk);
+ clk_disable_unprepare(q->clk_en);
+
+}
+
/* We use this function to do some basic init for spi_nor_scan(). */
static int fsl_qspi_nor_setup(struct fsl_qspi *q)
{
@@ -613,11 +700,23 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
u32 reg;
int ret;
- /* the default frequency, we will change it in the future.*/
+ /* disable and unprepare clock to avoid glitch pass to controller */
+ fsl_qspi_clk_disable_unprep(q);
+
+ /* the default frequency, we will change it in the future. */
ret = clk_set_rate(q->clk, 66000000);
if (ret)
return ret;
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
+ /* Reset the module */
+ writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
+ base + QUADSPI_MCR);
+ udelay(1);
+
/* Init the LUT table. */
fsl_qspi_init_lut(q);
@@ -635,6 +734,9 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
base + QUADSPI_MCR);
+ /* clear all interrupt status */
+ writel(0xffffffff, q->iobase + QUADSPI_FR);
+
/* enable the interrupt */
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
@@ -646,13 +748,20 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
unsigned long rate = q->clk_rate;
int ret;
- if (is_imx6sx_qspi(q))
+ if (needs_4x_clock(q))
rate *= 4;
+ /* disable and unprepare clock to avoid glitch pass to controller */
+ fsl_qspi_clk_disable_unprep(q);
+
ret = clk_set_rate(q->clk, rate);
if (ret)
return ret;
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
/* Init the LUT table again. */
fsl_qspi_init_lut(q);
@@ -665,6 +774,8 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
+ { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
+ { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
@@ -730,11 +841,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;
@@ -761,26 +903,26 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
struct fsl_qspi *q = nor->priv;
int ret;
- ret = clk_enable(q->clk_en);
- if (ret)
- return ret;
+ mutex_lock(&q->lock);
- ret = clk_enable(q->clk);
- if (ret) {
- clk_disable(q->clk_en);
- return ret;
- }
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ goto err_mutex;
fsl_qspi_set_base_addr(q, nor);
return 0;
+
+err_mutex:
+ mutex_unlock(&q->lock);
+ return ret;
}
static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct fsl_qspi *q = nor->priv;
- clk_disable(q->clk);
- clk_disable(q->clk_en);
+ fsl_qspi_clk_disable_unprep(q);
+ mutex_unlock(&q->lock);
}
static int fsl_qspi_probe(struct platform_device *pdev)
@@ -804,6 +946,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP)
return -ENODEV;
+ q->dev = dev;
+ q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
+ platform_set_drvdata(pdev, q);
+
/* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
q->iobase = devm_ioremap_resource(dev, res);
@@ -812,9 +958,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;
@@ -827,15 +975,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (IS_ERR(q->clk))
return PTR_ERR(q->clk);
- ret = clk_prepare_enable(q->clk_en);
- if (ret) {
- dev_err(dev, "cannot enable the qspi_en clock: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(q->clk);
+ ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
- dev_err(dev, "cannot enable the qspi clock: %d\n", ret);
+ dev_err(dev, "can not enable the clock\n");
goto clk_failed;
}
@@ -853,10 +995,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
goto irq_failed;
}
- q->dev = dev;
- q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
- platform_set_drvdata(pdev, q);
-
ret = fsl_qspi_nor_setup(q);
if (ret)
goto irq_failed;
@@ -864,6 +1002,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
q->has_second_chip = true;
+ mutex_init(&q->lock);
+
/* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) {
char modalias[40];
@@ -892,24 +1032,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
ret = of_modalias_node(np, modalias, sizeof(modalias));
if (ret < 0)
- goto irq_failed;
+ goto mutex_failed;
ret = of_property_read_u32(np, "spi-max-frequency",
&q->clk_rate);
if (ret < 0)
- goto irq_failed;
+ goto mutex_failed;
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
if (ret)
- goto irq_failed;
+ goto mutex_failed;
ppdata.of_node = np;
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (ret)
- goto irq_failed;
+ goto mutex_failed;
/* Set the correct NOR size now. */
if (q->nor_size == 0) {
@@ -939,8 +1079,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
goto last_init_failed;
- clk_disable(q->clk);
- clk_disable(q->clk_en);
+ fsl_qspi_clk_disable_unprep(q);
return 0;
last_init_failed:
@@ -950,10 +1089,12 @@ last_init_failed:
i *= 2;
mtd_device_unregister(&q->mtd[i]);
}
+mutex_failed:
+ mutex_destroy(&q->lock);
irq_failed:
- clk_disable_unprepare(q->clk);
+ fsl_qspi_clk_disable_unprep(q);
clk_failed:
- clk_disable_unprepare(q->clk_en);
+ dev_err(dev, "Freescale QuadSPI probe failed\n");
return ret;
}
@@ -973,8 +1114,11 @@ static int fsl_qspi_remove(struct platform_device *pdev)
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER);
- clk_unprepare(q->clk);
- clk_unprepare(q->clk_en);
+ mutex_destroy(&q->lock);
+
+ if (q->ahb_addr)
+ iounmap(q->ahb_addr);
+
return 0;
}
@@ -985,12 +1129,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
static int fsl_qspi_resume(struct platform_device *pdev)
{
+ int ret;
struct fsl_qspi *q = platform_get_drvdata(pdev);
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
fsl_qspi_nor_setup(q);
fsl_qspi_set_map_addr(q);
fsl_qspi_nor_setup_last(q);
+ fsl_qspi_clk_disable_unprep(q);
+
return 0;
}
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
new file mode 100644
index 0000000..9ad1dd0
--- /dev/null
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -0,0 +1,482 @@
+/*
+ * SPI-NOR driver for NXP SPI Flash Interface (SPIFI)
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * Based on Freescale QuadSPI driver:
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+/* NXP SPIFI registers, bits and macros */
+#define SPIFI_CTRL 0x000
+#define SPIFI_CTRL_TIMEOUT(timeout) (timeout)
+#define SPIFI_CTRL_CSHIGH(cshigh) ((cshigh) << 16)
+#define SPIFI_CTRL_MODE3 BIT(23)
+#define SPIFI_CTRL_DUAL BIT(28)
+#define SPIFI_CTRL_FBCLK BIT(30)
+#define SPIFI_CMD 0x004
+#define SPIFI_CMD_DATALEN(dlen) ((dlen) & 0x3fff)
+#define SPIFI_CMD_DOUT BIT(15)
+#define SPIFI_CMD_INTLEN(ilen) ((ilen) << 16)
+#define SPIFI_CMD_FIELDFORM(field) ((field) << 19)
+#define SPIFI_CMD_FIELDFORM_ALL_SERIAL SPIFI_CMD_FIELDFORM(0x0)
+#define SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA SPIFI_CMD_FIELDFORM(0x1)
+#define SPIFI_CMD_FRAMEFORM(frame) ((frame) << 21)
+#define SPIFI_CMD_FRAMEFORM_OPCODE_ONLY SPIFI_CMD_FRAMEFORM(0x1)
+#define SPIFI_CMD_OPCODE(op) ((op) << 24)
+#define SPIFI_ADDR 0x008
+#define SPIFI_IDATA 0x00c
+#define SPIFI_CLIMIT 0x010
+#define SPIFI_DATA 0x014
+#define SPIFI_MCMD 0x018
+#define SPIFI_STAT 0x01c
+#define SPIFI_STAT_MCINIT BIT(0)
+#define SPIFI_STAT_CMD BIT(1)
+#define SPIFI_STAT_RESET BIT(4)
+
+#define SPI_NOR_MAX_ID_LEN 6
+
+struct nxp_spifi {
+ struct device *dev;
+ struct clk *clk_spifi;
+ struct clk *clk_reg;
+ void __iomem *io_base;
+ void __iomem *flash_base;
+ struct mtd_info mtd;
+ struct spi_nor nor;
+ bool memory_mode;
+ u32 mcmd;
+};
+
+static int nxp_spifi_wait_for_cmd(struct nxp_spifi *spifi)
+{
+ u8 stat;
+ int ret;
+
+ ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
+ !(stat & SPIFI_STAT_CMD), 10, 30);
+ if (ret)
+ dev_warn(spifi->dev, "command timed out\n");
+
+ return ret;
+}
+
+static int nxp_spifi_reset(struct nxp_spifi *spifi)
+{
+ u8 stat;
+ int ret;
+
+ writel(SPIFI_STAT_RESET, spifi->io_base + SPIFI_STAT);
+ ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
+ !(stat & SPIFI_STAT_RESET), 10, 30);
+ if (ret)
+ dev_warn(spifi->dev, "state reset timed out\n");
+
+ return ret;
+}
+
+static int nxp_spifi_set_memory_mode_off(struct nxp_spifi *spifi)
+{
+ int ret;
+
+ if (!spifi->memory_mode)
+ return 0;
+
+ ret = nxp_spifi_reset(spifi);
+ if (ret)
+ dev_err(spifi->dev, "unable to enter command mode\n");
+ else
+ spifi->memory_mode = false;
+
+ return ret;
+}
+
+static int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi)
+{
+ u8 stat;
+ int ret;
+
+ if (spifi->memory_mode)
+ return 0;
+
+ writel(spifi->mcmd, spifi->io_base + SPIFI_MCMD);
+ ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
+ stat & SPIFI_STAT_MCINIT, 10, 30);
+ if (ret)
+ dev_err(spifi->dev, "unable to enter memory mode\n");
+ else
+ spifi->memory_mode = true;
+
+ return ret;
+}
+
+static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ struct nxp_spifi *spifi = nor->priv;
+ u32 cmd;
+ int ret;
+
+ ret = nxp_spifi_set_memory_mode_off(spifi);
+ if (ret)
+ return ret;
+
+ cmd = SPIFI_CMD_DATALEN(len) |
+ SPIFI_CMD_OPCODE(opcode) |
+ SPIFI_CMD_FIELDFORM_ALL_SERIAL |
+ SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
+ writel(cmd, spifi->io_base + SPIFI_CMD);
+
+ while (len--)
+ *buf++ = readb(spifi->io_base + SPIFI_DATA);
+
+ return nxp_spifi_wait_for_cmd(spifi);
+}
+
+static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+ int len, int write_enable)
+{
+ struct nxp_spifi *spifi = nor->priv;
+ u32 cmd;
+ int ret;
+
+ ret = nxp_spifi_set_memory_mode_off(spifi);
+ if (ret)
+ return ret;
+
+ cmd = SPIFI_CMD_DOUT |
+ SPIFI_CMD_DATALEN(len) |
+ SPIFI_CMD_OPCODE(opcode) |
+ SPIFI_CMD_FIELDFORM_ALL_SERIAL |
+ SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
+ writel(cmd, spifi->io_base + SPIFI_CMD);
+
+ while (len--)
+ writeb(*buf++, spifi->io_base + SPIFI_DATA);
+
+ return nxp_spifi_wait_for_cmd(spifi);
+}
+
+static int nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct nxp_spifi *spifi = nor->priv;
+ int ret;
+
+ ret = nxp_spifi_set_memory_mode_on(spifi);
+ if (ret)
+ return ret;
+
+ memcpy_fromio(buf, spifi->flash_base + from, len);
+ *retlen += len;
+
+ return 0;
+}
+
+static void nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct nxp_spifi *spifi = nor->priv;
+ u32 cmd;
+ int ret;
+
+ ret = nxp_spifi_set_memory_mode_off(spifi);
+ if (ret)
+ return;
+
+ writel(to, spifi->io_base + SPIFI_ADDR);
+ *retlen += len;
+
+ cmd = SPIFI_CMD_DOUT |
+ SPIFI_CMD_DATALEN(len) |
+ SPIFI_CMD_FIELDFORM_ALL_SERIAL |
+ SPIFI_CMD_OPCODE(nor->program_opcode) |
+ SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
+ writel(cmd, spifi->io_base + SPIFI_CMD);
+
+ while (len--)
+ writeb(*buf++, spifi->io_base + SPIFI_DATA);
+
+ nxp_spifi_wait_for_cmd(spifi);
+}
+
+static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
+{
+ struct nxp_spifi *spifi = nor->priv;
+ u32 cmd;
+ int ret;
+
+ ret = nxp_spifi_set_memory_mode_off(spifi);
+ if (ret)
+ return ret;
+
+ writel(offs, spifi->io_base + SPIFI_ADDR);
+
+ cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL |
+ SPIFI_CMD_OPCODE(nor->erase_opcode) |
+ SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
+ writel(cmd, spifi->io_base + SPIFI_CMD);
+
+ return nxp_spifi_wait_for_cmd(spifi);
+}
+
+static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
+{
+ switch (spifi->nor.flash_read) {
+ case SPI_NOR_NORMAL:
+ case SPI_NOR_FAST:
+ spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
+ break;
+ case SPI_NOR_DUAL:
+ case SPI_NOR_QUAD:
+ spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
+ break;
+ default:
+ dev_err(spifi->dev, "unsupported SPI read mode\n");
+ return -EINVAL;
+ }
+
+ /* Memory mode supports address length between 1 and 4 */
+ if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4)
+ return -EINVAL;
+
+ spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) |
+ SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) |
+ SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
+
+ return 0;
+}
+
+static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
+{
+ u8 id[SPI_NOR_MAX_ID_LEN];
+ nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+}
+
+static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
+ struct device_node *np)
+{
+ struct mtd_part_parser_data ppdata;
+ enum read_mode flash_read;
+ u32 ctrl, property;
+ u16 mode = 0;
+ int ret;
+
+ if (!of_property_read_u32(np, "spi-rx-bus-width", &property)) {
+ switch (property) {
+ case 1:
+ break;
+ case 2:
+ mode |= SPI_RX_DUAL;
+ break;
+ case 4:
+ mode |= SPI_RX_QUAD;
+ break;
+ default:
+ dev_err(spifi->dev, "unsupported rx-bus-width\n");
+ return -EINVAL;
+ }
+ }
+
+ if (of_find_property(np, "spi-cpha", NULL))
+ mode |= SPI_CPHA;
+
+ if (of_find_property(np, "spi-cpol", NULL))
+ mode |= SPI_CPOL;
+
+ /* Setup control register defaults */
+ ctrl = SPIFI_CTRL_TIMEOUT(1000) |
+ SPIFI_CTRL_CSHIGH(15) |
+ SPIFI_CTRL_FBCLK;
+
+ if (mode & SPI_RX_DUAL) {
+ ctrl |= SPIFI_CTRL_DUAL;
+ flash_read = SPI_NOR_DUAL;
+ } else if (mode & SPI_RX_QUAD) {
+ ctrl &= ~SPIFI_CTRL_DUAL;
+ flash_read = SPI_NOR_QUAD;
+ } else {
+ ctrl |= SPIFI_CTRL_DUAL;
+ flash_read = SPI_NOR_NORMAL;
+ }
+
+ switch (mode & (SPI_CPHA | SPI_CPOL)) {
+ case SPI_MODE_0:
+ ctrl &= ~SPIFI_CTRL_MODE3;
+ break;
+ case SPI_MODE_3:
+ ctrl |= SPIFI_CTRL_MODE3;
+ break;
+ default:
+ dev_err(spifi->dev, "only mode 0 and 3 supported\n");
+ return -EINVAL;
+ }
+
+ writel(ctrl, spifi->io_base + SPIFI_CTRL);
+
+ spifi->mtd.priv = &spifi->nor;
+ spifi->nor.mtd = &spifi->mtd;
+ spifi->nor.dev = spifi->dev;
+ spifi->nor.priv = spifi;
+ spifi->nor.read = nxp_spifi_read;
+ spifi->nor.write = nxp_spifi_write;
+ spifi->nor.erase = nxp_spifi_erase;
+ spifi->nor.read_reg = nxp_spifi_read_reg;
+ spifi->nor.write_reg = nxp_spifi_write_reg;
+
+ /*
+ * The first read on a hard reset isn't reliable so do a
+ * dummy read of the id before calling spi_nor_scan().
+ * The reason for this problem is unknown.
+ *
+ * The official NXP spifilib uses more or less the same
+ * workaround that is applied here by reading the device
+ * id multiple times.
+ */
+ nxp_spifi_dummy_id_read(&spifi->nor);
+
+ ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
+ if (ret) {
+ dev_err(spifi->dev, "device scan failed\n");
+ return ret;
+ }
+
+ ret = nxp_spifi_setup_memory_cmd(spifi);
+ if (ret) {
+ dev_err(spifi->dev, "memory command setup failed\n");
+ return ret;
+ }
+
+ ppdata.of_node = np;
+ ret = mtd_device_parse_register(&spifi->mtd, NULL, &ppdata, NULL, 0);
+ if (ret) {
+ dev_err(spifi->dev, "mtd device parse failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nxp_spifi_probe(struct platform_device *pdev)
+{
+ struct device_node *flash_np;
+ struct nxp_spifi *spifi;
+ struct resource *res;
+ int ret;
+
+ spifi = devm_kzalloc(&pdev->dev, sizeof(*spifi), GFP_KERNEL);
+ if (!spifi)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spifi");
+ spifi->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(spifi->io_base))
+ return PTR_ERR(spifi->io_base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash");
+ spifi->flash_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(spifi->flash_base))
+ return PTR_ERR(spifi->flash_base);
+
+ spifi->clk_spifi = devm_clk_get(&pdev->dev, "spifi");
+ if (IS_ERR(spifi->clk_spifi)) {
+ dev_err(&pdev->dev, "spifi clock not found\n");
+ return PTR_ERR(spifi->clk_spifi);
+ }
+
+ spifi->clk_reg = devm_clk_get(&pdev->dev, "reg");
+ if (IS_ERR(spifi->clk_reg)) {
+ dev_err(&pdev->dev, "reg clock not found\n");
+ return PTR_ERR(spifi->clk_reg);
+ }
+
+ ret = clk_prepare_enable(spifi->clk_reg);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable reg clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(spifi->clk_spifi);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable spifi clock\n");
+ goto dis_clk_reg;
+ }
+
+ spifi->dev = &pdev->dev;
+ platform_set_drvdata(pdev, spifi);
+
+ /* Initialize and reset device */
+ nxp_spifi_reset(spifi);
+ writel(0, spifi->io_base + SPIFI_IDATA);
+ writel(0, spifi->io_base + SPIFI_MCMD);
+ nxp_spifi_reset(spifi);
+
+ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
+ if (!flash_np) {
+ dev_err(&pdev->dev, "no SPI flash device to configure\n");
+ ret = -ENODEV;
+ goto dis_clks;
+ }
+
+ ret = nxp_spifi_setup_flash(spifi, flash_np);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to setup flash chip\n");
+ goto dis_clks;
+ }
+
+ return 0;
+
+dis_clks:
+ clk_disable_unprepare(spifi->clk_spifi);
+dis_clk_reg:
+ clk_disable_unprepare(spifi->clk_reg);
+ return ret;
+}
+
+static int nxp_spifi_remove(struct platform_device *pdev)
+{
+ struct nxp_spifi *spifi = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(&spifi->mtd);
+ clk_disable_unprepare(spifi->clk_spifi);
+ clk_disable_unprepare(spifi->clk_reg);
+
+ return 0;
+}
+
+static const struct of_device_id nxp_spifi_match[] = {
+ {.compatible = "nxp,lpc1773-spifi"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nxp_spifi_match);
+
+static struct platform_driver nxp_spifi_driver = {
+ .probe = nxp_spifi_probe,
+ .remove = nxp_spifi_remove,
+ .driver = {
+ .name = "nxp-spifi",
+ .of_match_table = nxp_spifi_match,
+ },
+};
+module_platform_driver(nxp_spifi_driver);
+
+MODULE_DESCRIPTION("NXP SPI Flash Interface driver");
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d78831b..c27d427 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -29,6 +29,8 @@
#define SPI_NOR_MAX_ID_LEN 6
struct flash_info {
+ char *name;
+
/*
* This array stores the ID bytes.
* The first three bytes are the JEDIC ID.
@@ -59,7 +61,7 @@ struct flash_info {
#define JEDEC_MFR(info) ((info)->id[0])
-static const struct spi_device_id *spi_nor_match_id(const char *name);
+static const struct flash_info *spi_nor_match_id(const char *name);
/*
* Read the status register, returning its value in the location
@@ -169,7 +171,7 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
}
/* Enable/disable 4-byte addressing mode. */
-static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
+static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
int enable)
{
int status;
@@ -469,7 +471,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Used when the "_ext_id" is two bytes at most */
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
- ((kernel_ulong_t)&(struct flash_info) { \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
@@ -481,11 +482,9 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
- .flags = (_flags), \
- })
+ .flags = (_flags),
#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
- ((kernel_ulong_t)&(struct flash_info) { \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
@@ -498,17 +497,14 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
- .flags = (_flags), \
- })
+ .flags = (_flags),
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
- ((kernel_ulong_t)&(struct flash_info) { \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = (_page_size), \
.addr_width = (_addr_width), \
- .flags = (_flags), \
- })
+ .flags = (_flags),
/* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which
@@ -521,7 +517,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
* For historical (and compatibility) reasons (before we got above config) some
* old entries may be missing 4K flag.
*/
-static const struct spi_device_id spi_nor_ids[] = {
+static const struct flash_info spi_nor_ids[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
@@ -589,7 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = {
/* Micron */
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
- { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ) },
+ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
@@ -626,6 +622,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
+ { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
@@ -702,11 +699,11 @@ static const struct spi_device_id spi_nor_ids[] = {
{ },
};
-static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
{
int tmp;
u8 id[SPI_NOR_MAX_ID_LEN];
- struct flash_info *info;
+ const struct flash_info *info;
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
@@ -715,7 +712,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
}
for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
- info = (void *)spi_nor_ids[tmp].driver_data;
+ info = &spi_nor_ids[tmp];
if (info->id_len) {
if (!memcmp(info->id, id, info->id_len))
return &spi_nor_ids[tmp];
@@ -961,7 +958,7 @@ static int micron_quad_enable(struct spi_nor *nor)
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
+static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{
int status;
@@ -1003,8 +1000,7 @@ static int spi_nor_check(struct spi_nor *nor)
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
{
- const struct spi_device_id *id = NULL;
- struct flash_info *info;
+ const struct flash_info *info = NULL;
struct device *dev = nor->dev;
struct mtd_info *mtd = nor->mtd;
struct device_node *np = dev->of_node;
@@ -1015,27 +1011,25 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (ret)
return ret;
- /* Try to auto-detect if chip name wasn't specified */
- if (!name)
- id = spi_nor_read_id(nor);
- else
- id = spi_nor_match_id(name);
- if (IS_ERR_OR_NULL(id))
+ if (name)
+ info = spi_nor_match_id(name);
+ /* Try to auto-detect if chip name wasn't specified or not found */
+ if (!info)
+ info = spi_nor_read_id(nor);
+ if (IS_ERR_OR_NULL(info))
return -ENOENT;
- info = (void *)id->driver_data;
-
/*
* If caller has specified name of flash model that can normally be
* detected using JEDEC, let's verify it.
*/
if (name && info->id_len) {
- const struct spi_device_id *jid;
+ const struct flash_info *jinfo;
- jid = spi_nor_read_id(nor);
- if (IS_ERR(jid)) {
- return PTR_ERR(jid);
- } else if (jid != id) {
+ jinfo = spi_nor_read_id(nor);
+ if (IS_ERR(jinfo)) {
+ return PTR_ERR(jinfo);
+ } else if (jinfo != info) {
/*
* JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let
@@ -1044,9 +1038,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
* information, even if it's not 100% accurate.
*/
dev_warn(dev, "found %s, expected %s\n",
- jid->name, id->name);
- id = jid;
- info = (void *)jid->driver_data;
+ jinfo->name, info->name);
+ info = jinfo;
}
}
@@ -1196,7 +1189,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->read_dummy = spi_nor_read_dummy_cycles(nor);
- dev_info(dev, "%s (%lld Kbytes)\n", id->name,
+ dev_info(dev, "%s (%lld Kbytes)\n", info->name,
(long long)mtd->size >> 10);
dev_dbg(dev,
@@ -1219,9 +1212,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
}
EXPORT_SYMBOL_GPL(spi_nor_scan);
-static const struct spi_device_id *spi_nor_match_id(const char *name)
+static const struct flash_info *spi_nor_match_id(const char *name)
{
- const struct spi_device_id *id = spi_nor_ids;
+ const struct flash_info *id = spi_nor_ids;
while (id->name[0]) {
if (!strcmp(name, id->name))
diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
index 8e8525f..3176212 100644
--- a/drivers/mtd/tests/oobtest.c
+++ b/drivers/mtd/tests/oobtest.c
@@ -125,7 +125,8 @@ static int write_whole_device(void)
* Display the address, offset and data bytes at comparison failure.
* Return number of bitflips encountered.
*/
-static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
+static size_t memcmpshowoffset(loff_t addr, loff_t offset, const void *cs,
+ const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res;
@@ -135,8 +136,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
res = *su1 ^ *su2;
if (res) {
- pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n",
- (unsigned long)addr, i, *su1, *su2, res);
+ pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n",
+ (unsigned long)addr, (unsigned long)offset + i,
+ *su1, *su2, res);
bitflips += hweight8(res);
}
}
@@ -144,6 +146,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou
return bitflips;
}
+#define memcmpshow(addr, cs, ct, count) memcmpshowoffset((addr), 0, (cs), (ct),\
+ (count))
+
/*
* Compare with 0xff and show the address, offset and data bytes at
* comparison failure. Return number of bitflips encountered.
@@ -228,9 +233,10 @@ static int verify_eraseblock(int ebnum)
errcnt += 1;
return err ? err : -1;
}
- bitflips = memcmpshow(addr, readbuf + use_offset,
- writebuf + (use_len_max * i) + use_offset,
- use_len);
+ bitflips = memcmpshowoffset(addr, use_offset,
+ readbuf + use_offset,
+ writebuf + (use_len_max * i) + use_offset,
+ use_len);
/* verify pre-offset area for 0xff */
bitflips += memffshow(addr, 0, readbuf, use_offset);
OpenPOWER on IntegriCloud