Commit 91a247d7 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 8bdc69b7 718e38b4
* Freescale Quad Serial Peripheral Interface(QuadSPI) * Freescale Quad Serial Peripheral Interface(QuadSPI)
Required properties: Required properties:
- compatible : Should be "fsl,vf610-qspi" or "fsl,imx6sx-qspi" - compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi",
"fsl,imx7d-qspi", "fsl,imx6ul-qspi"
- reg : the first contains the register location and length, - reg : the first contains the register location and length,
the second contains the memory mapping address and length the second contains the memory mapping address and length
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
......
* NXP SPI Flash Interface (SPIFI)
NXP SPIFI is a specialized SPI interface for serial Flash devices.
It supports one Flash device with 1-, 2- and 4-bits width in SPI
mode 0 or 3. The controller operates in either command or memory
mode. In memory mode the Flash is accessible from the CPU as
normal memory.
Required properties:
- compatible : Should be "nxp,lpc1773-spifi"
- reg : the first contains the register location and length,
the second contains the memory mapping address and length
- reg-names: Should contain the reg names "spifi" and "flash"
- interrupts : Should contain the interrupt for the device
- clocks : The clocks needed by the SPIFI controller
- clock-names : Should contain the clock names "spifi" and "reg"
Optional properties:
- resets : phandle + reset specifier
The SPI Flash must be a child of the SPIFI node and must have a
compatible property as specified in bindings/mtd/jedec,spi-nor.txt
Optionally it can also contain the following properties.
- spi-cpol : Controller only supports mode 0 and 3 so either
both spi-cpol and spi-cpha should be present or
none of them
- spi-cpha : See above
- spi-rx-bus-width : Used to select how many pins that are used
for input on the controller
See bindings/spi/spi-bus.txt for more information.
Example:
spifi: spifi@40003000 {
compatible = "nxp,lpc1773-spifi";
reg = <0x40003000 0x1000>, <0x14000000 0x4000000>;
reg-names = "spifi", "flash";
interrupts = <30>;
clocks = <&ccu1 CLK_SPIFI>, <&ccu1 CLK_CPU_SPIFI>;
clock-names = "spifi", "reg";
resets = <&rgu 53>;
flash@0 {
compatible = "jedec,spi-nor";
spi-cpol;
spi-cpha;
spi-rx-bus-width = <4>;
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "data";
reg = <0 0x200000>;
};
};
};
...@@ -223,8 +223,6 @@ static int m25p_probe(struct spi_device *spi) ...@@ -223,8 +223,6 @@ static int m25p_probe(struct spi_device *spi)
*/ */
if (data && data->type) if (data && data->type)
flash_name = data->type; flash_name = data->type;
else if (!strcmp(spi->modalias, "spi-nor"))
flash_name = NULL; /* auto-detect */
else else
flash_name = spi->modalias; flash_name = spi->modalias;
...@@ -289,19 +287,25 @@ static const struct spi_device_id m25p_ids[] = { ...@@ -289,19 +287,25 @@ static const struct spi_device_id m25p_ids[] = {
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-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); 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 = { static struct spi_driver m25p80_driver = {
.driver = { .driver = {
.name = "m25p80", .name = "m25p80",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = m25p_of_table,
}, },
.id_table = m25p_ids, .id_table = m25p_ids,
.probe = m25p_probe, .probe = m25p_probe,
......
...@@ -102,6 +102,7 @@ static const struct of_device_id dataflash_dt_ids[] = { ...@@ -102,6 +102,7 @@ static const struct of_device_id dataflash_dt_ids[] = {
{ .compatible = "atmel,dataflash", }, { .compatible = "atmel,dataflash", },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
#endif #endif
/* ......................................................................... */ /* ......................................................................... */
......
...@@ -385,20 +385,28 @@ static int __init nettel_init(void) ...@@ -385,20 +385,28 @@ static int __init nettel_init(void)
} }
rc = mtd_device_register(intel_mtd, nettel_intel_partitions, rc = mtd_device_register(intel_mtd, nettel_intel_partitions,
num_intel_partitions); num_intel_partitions);
if (rc)
goto out_map_destroy;
#endif #endif
if (amd_mtd) { if (amd_mtd) {
rc = mtd_device_register(amd_mtd, nettel_amd_partitions, rc = mtd_device_register(amd_mtd, nettel_amd_partitions,
num_amd_partitions); num_amd_partitions);
if (rc)
goto out_mtd_unreg;
} }
#ifdef CONFIG_MTD_CFI_INTELEXT #ifdef CONFIG_MTD_CFI_INTELEXT
register_reboot_notifier(&nettel_notifier_block); register_reboot_notifier(&nettel_notifier_block);
#endif #endif
return(rc); return rc;
out_mtd_unreg:
#ifdef CONFIG_MTD_CFI_INTELEXT #ifdef CONFIG_MTD_CFI_INTELEXT
mtd_device_unregister(intel_mtd);
out_map_destroy:
map_destroy(intel_mtd);
out_unmap1: out_unmap1:
iounmap(nettel_intel_map.virt); iounmap(nettel_intel_map.virt);
#endif #endif
...@@ -407,8 +415,7 @@ static int __init nettel_init(void) ...@@ -407,8 +415,7 @@ static int __init nettel_init(void)
iounmap(nettel_mmcrp); iounmap(nettel_mmcrp);
iounmap(nettel_amd_map.virt); iounmap(nettel_amd_map.virt);
return(rc); return rc;
} }
/****************************************************************************/ /****************************************************************************/
......
...@@ -130,6 +130,8 @@ static const char * const *of_get_probes(struct device_node *dp) ...@@ -130,6 +130,8 @@ static const char * const *of_get_probes(struct device_node *dp)
count++; count++;
res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
if (!res)
return NULL;
count = 0; count = 0;
while (cplen > 0) { while (cplen > 0) {
res[count] = cp; res[count] = cp;
...@@ -311,6 +313,10 @@ static int of_flash_probe(struct platform_device *dev) ...@@ -311,6 +313,10 @@ static int of_flash_probe(struct platform_device *dev)
ppdata.of_node = dp; ppdata.of_node = dp;
part_probe_types = of_get_probes(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, mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
NULL, 0); NULL, 0);
of_free_probes(part_probe_types); of_free_probes(part_probe_types);
......
...@@ -97,14 +97,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, ...@@ -97,14 +97,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
if (req->cmd_flags & REQ_DISCARD) if (req->cmd_flags & REQ_DISCARD)
return tr->discard(dev, block, nsect); return tr->discard(dev, block, nsect);
switch(rq_data_dir(req)) { if (rq_data_dir(req) == READ) {
case READ:
for (; nsect > 0; nsect--, block++, buf += tr->blksize) for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf)) if (tr->readsect(dev, block, buf))
return -EIO; return -EIO;
rq_flush_dcache_pages(req); rq_flush_dcache_pages(req);
return 0; return 0;
case WRITE: } else {
if (!tr->writesect) if (!tr->writesect)
return -EIO; return -EIO;
...@@ -113,9 +112,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, ...@@ -113,9 +112,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
if (tr->writesect(dev, block, buf)) if (tr->writesect(dev, block, buf))
return -EIO; return -EIO;
return 0; return 0;
default:
printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
return -EIO;
} }
} }
......
...@@ -42,23 +42,20 @@ config MTD_SM_COMMON ...@@ -42,23 +42,20 @@ config MTD_SM_COMMON
default n default n
config MTD_NAND_DENALI config MTD_NAND_DENALI
tristate "Support Denali NAND controller" tristate
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.
config MTD_NAND_DENALI_PCI config MTD_NAND_DENALI_PCI
tristate "Support Denali NAND controller on Intel Moorestown" tristate "Support Denali NAND controller on Intel Moorestown"
depends on PCI && MTD_NAND_DENALI select MTD_NAND_DENALI
depends on HAS_DMA && PCI
help help
Enable the driver for NAND flash on Intel Moorestown, using the Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core. Denali NAND controller core.
config MTD_NAND_DENALI_DT config MTD_NAND_DENALI_DT
tristate "Support Denali NAND controller as a DT device" 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 help
Enable the driver for NAND flash on platforms using a Denali NAND Enable the driver for NAND flash on platforms using a Denali NAND
controller as a DT device. controller as a DT device.
......
...@@ -50,7 +50,7 @@ static inline u32 brcmnand_readl(void __iomem *addr) ...@@ -50,7 +50,7 @@ static inline u32 brcmnand_readl(void __iomem *addr)
* Other architectures (e.g., ARM) either do not support big endian, or * Other architectures (e.g., ARM) either do not support big endian, or
* else leave I/O in little endian mode. * 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); return __raw_readl(addr);
else else
return readl_relaxed(addr); return readl_relaxed(addr);
...@@ -59,7 +59,7 @@ static inline u32 brcmnand_readl(void __iomem *addr) ...@@ -59,7 +59,7 @@ static inline u32 brcmnand_readl(void __iomem *addr)
static inline void brcmnand_writel(u32 val, void __iomem *addr) static inline void brcmnand_writel(u32 val, void __iomem *addr)
{ {
/* See brcmnand_readl() comments */ /* 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); __raw_writel(val, addr);
else else
writel_relaxed(val, addr); writel_relaxed(val, addr);
......
...@@ -520,6 +520,32 @@ static struct nand_ecclayout hwecc4_2048 = { ...@@ -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) #if defined(CONFIG_OF)
static const struct of_device_id davinci_nand_of_match[] = { static const struct of_device_id davinci_nand_of_match[] = {
{.compatible = "ti,davinci-nand", }, {.compatible = "ti,davinci-nand", },
...@@ -796,18 +822,12 @@ static int nand_davinci_probe(struct platform_device *pdev) ...@@ -796,18 +822,12 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done; 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; ret = -EIO;
goto err; goto err;
......
...@@ -30,19 +30,19 @@ MODULE_DEVICE_TABLE(pci, denali_pci_ids); ...@@ -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) 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; resource_size_t csr_base, mem_base;
unsigned long csr_len, mem_len; unsigned long csr_len, mem_len;
struct denali_nand_info *denali; struct denali_nand_info *denali;
denali = kzalloc(sizeof(*denali), GFP_KERNEL); denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
if (!denali) if (!denali)
return -ENOMEM; return -ENOMEM;
ret = pci_enable_device(dev); ret = pcim_enable_device(dev);
if (ret) { if (ret) {
pr_err("Spectra: pci_enable_device failed.\n"); dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n");
goto failed_alloc_memery; return ret;
} }
if (id->driver_data == INTEL_CE4100) { 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) ...@@ -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); ret = pci_request_regions(dev, DENALI_NAND_NAME);
if (ret) { if (ret) {
pr_err("Spectra: Unable to request memory regions\n"); dev_err(&dev->dev, "Spectra: Unable to request memory regions\n");
goto failed_enable_dev; return ret;
} }
denali->flash_reg = ioremap_nocache(csr_base, csr_len); denali->flash_reg = ioremap_nocache(csr_base, csr_len);
if (!denali->flash_reg) { if (!denali->flash_reg) {
pr_err("Spectra: Unable to remap memory region\n"); dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
ret = -ENOMEM; return -ENOMEM;
goto failed_req_regions;
} }
denali->flash_mem = ioremap_nocache(mem_base, mem_len); denali->flash_mem = ioremap_nocache(mem_base, mem_len);
if (!denali->flash_mem) { if (!denali->flash_mem) {
pr_err("Spectra: ioremap_nocache failed!"); dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
ret = -ENOMEM; ret = -ENOMEM;
goto failed_remap_reg; goto failed_remap_reg;
} }
...@@ -99,13 +98,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -99,13 +98,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
iounmap(denali->flash_mem); iounmap(denali->flash_mem);
failed_remap_reg: failed_remap_reg:
iounmap(denali->flash_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; return ret;
} }
...@@ -117,9 +109,6 @@ static void denali_pci_remove(struct pci_dev *dev) ...@@ -117,9 +109,6 @@ static void denali_pci_remove(struct pci_dev *dev)
denali_remove(denali); denali_remove(denali);
iounmap(denali->flash_reg); iounmap(denali->flash_reg);
iounmap(denali->flash_mem); iounmap(denali->flash_mem);
pci_release_regions(dev);
pci_disable_device(dev);
kfree(denali);
} }
static struct pci_driver denali_pci_driver = { static struct pci_driver denali_pci_driver = {
...@@ -129,14 +118,4 @@ static struct pci_driver denali_pci_driver = { ...@@ -129,14 +118,4 @@ static struct pci_driver denali_pci_driver = {
.remove = denali_pci_remove, .remove = denali_pci_remove,
}; };
static int denali_init_pci(void) module_pci_driver(denali_pci_driver);
{
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);
...@@ -29,6 +29,10 @@ struct nand_flash_dev nand_flash_ids[] = { ...@@ -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 * listed by full ID. We list them first so that we can easily identify
* the most specific match. * 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", {"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
......
...@@ -649,7 +649,8 @@ static void free_device(struct nandsim *ns) ...@@ -649,7 +649,8 @@ static void free_device(struct nandsim *ns)
kmem_cache_free(ns->nand_pages_slab, kmem_cache_free(ns->nand_pages_slab,
ns->pages[i].byte); 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); vfree(ns->pages);
} }
} }
...@@ -729,8 +730,7 @@ static int init_nandsim(struct mtd_info *mtd) ...@@ -729,8 +730,7 @@ static int init_nandsim(struct mtd_info *mtd)
/* Fill the partition_info structure */ /* Fill the partition_info structure */
if (parts_num > ARRAY_SIZE(ns->partitions)) { if (parts_num > ARRAY_SIZE(ns->partitions)) {
NS_ERR("too many partitions.\n"); NS_ERR("too many partitions.\n");
ret = -EINVAL; return -EINVAL;
goto error;
} }
remains = ns->geom.totsz; remains = ns->geom.totsz;
next_offset = 0; next_offset = 0;
...@@ -739,14 +739,12 @@ static int init_nandsim(struct mtd_info *mtd) ...@@ -739,14 +739,12 @@ static int init_nandsim(struct mtd_info *mtd)
if (!part_sz || part_sz > remains) { if (!part_sz || part_sz > remains) {
NS_ERR("bad partition size.\n"); NS_ERR("bad partition size.\n");
ret = -EINVAL; return -EINVAL;
goto error;
} }
ns->partitions[i].name = get_partition_name(i); ns->partitions[i].name = get_partition_name(i);
if (!ns->partitions[i].name) { if (!ns->partitions[i].name) {
NS_ERR("unable to allocate memory.\n"); NS_ERR("unable to allocate memory.\n");
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
ns->partitions[i].offset = next_offset; ns->partitions[i].offset = next_offset;
ns->partitions[i].size = part_sz; ns->partitions[i].size = part_sz;
...@@ -757,14 +755,12 @@ static int init_nandsim(struct mtd_info *mtd) ...@@ -757,14 +755,12 @@ static int init_nandsim(struct mtd_info *mtd)
if (remains) { if (remains) {
if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
NS_ERR("too many partitions.\n"); NS_ERR("too many partitions.\n");
ret = -EINVAL; return -EINVAL;
goto error;
} }
ns->partitions[i].name = get_partition_name(i); ns->partitions[i].name = get_partition_name(i);
if (!ns->partitions[i].name) { if (!ns->partitions[i].name) {
NS_ERR("unable to allocate memory.\n"); NS_ERR("unable to allocate memory.\n");
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
ns->partitions[i].offset = next_offset; ns->partitions[i].offset = next_offset;
ns->partitions[i].size = remains; ns->partitions[i].size = remains;
...@@ -792,24 +788,18 @@ static int init_nandsim(struct mtd_info *mtd) ...@@ -792,24 +788,18 @@ static int init_nandsim(struct mtd_info *mtd)
printk("options: %#x\n", ns->options); printk("options: %#x\n", ns->options);
if ((ret = alloc_device(ns)) != 0) if ((ret = alloc_device(ns)) != 0)
goto error; return ret;
/* Allocate / initialize the internal buffer */ /* Allocate / initialize the internal buffer */
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
if (!ns->buf.byte) { if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob); ns->geom.pgszoob);
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
return 0; return 0;
error:
free_device(ns);
return ret;
} }
/* /*
......
...@@ -574,5 +574,5 @@ module_platform_driver(elm_driver); ...@@ -574,5 +574,5 @@ module_platform_driver(elm_driver);
MODULE_DESCRIPTION("ELM driver for BCH error correction"); MODULE_DESCRIPTION("ELM driver for BCH error correction");
MODULE_AUTHOR("Texas Instruments"); MODULE_AUTHOR("Texas Instruments");
MODULE_ALIAS("platform: elm"); MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -45,10 +45,13 @@ ...@@ -45,10 +45,13 @@
/* /*
* Define a buffer size for the initial command that detects the flash device: * 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, * STATUS, READID and PARAM.
* needing 256 bytes. * 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 */ /* registers and bit definitions */
#define NDCR (0x00) /* Control register */ #define NDCR (0x00) /* Control register */
...@@ -126,6 +129,13 @@ ...@@ -126,6 +129,13 @@
#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ #define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
#define EXT_CMD_TYPE_MONO 0 /* Monolithic 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 */ /* macros for registers read/write */
#define nand_writel(info, off, val) \ #define nand_writel(info, off, val) \
writel_relaxed((val), (info)->mmio_base + (off)) writel_relaxed((val), (info)->mmio_base + (off))
...@@ -173,8 +183,6 @@ struct pxa3xx_nand_host { ...@@ -173,8 +183,6 @@ struct pxa3xx_nand_host {
/* calculated from pxa3xx_nand_flash data */ /* calculated from pxa3xx_nand_flash data */
unsigned int col_addr_cycles; unsigned int col_addr_cycles;
unsigned int row_addr_cycles; unsigned int row_addr_cycles;
size_t read_id_bytes;
}; };
struct pxa3xx_nand_info { struct pxa3xx_nand_info {
...@@ -439,8 +447,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) ...@@ -439,8 +447,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
ndcr |= NDCR_ND_RUN; ndcr |= NDCR_ND_RUN;
/* clear status bits and run */ /* clear status bits and run */
nand_writel(info, NDCR, 0);
nand_writel(info, NDSR, NDSR_MASK); nand_writel(info, NDSR, NDSR_MASK);
nand_writel(info, NDCR, 0);
nand_writel(info, NDCR, ndcr); nand_writel(info, NDCR, ndcr);
} }
...@@ -675,8 +683,14 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) ...@@ -675,8 +683,14 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
is_ready = 1; 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) { if (status & NDSR_WRCMDREQ) {
nand_writel(info, NDSR, NDSR_WRCMDREQ);
status &= ~NDSR_WRCMDREQ; status &= ~NDSR_WRCMDREQ;
info->state = STATE_CMD_HANDLE; info->state = STATE_CMD_HANDLE;
...@@ -697,8 +711,6 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) ...@@ -697,8 +711,6 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
nand_writel(info, NDCB0, info->ndcb3); nand_writel(info, NDCB0, info->ndcb3);
} }
/* clear NDSR to let the controller exit the IRQ */
nand_writel(info, NDSR, status);
if (is_completed) if (is_completed)
complete(&info->cmd_complete); complete(&info->cmd_complete);
if (is_ready) if (is_ready)
...@@ -899,18 +911,18 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, ...@@ -899,18 +911,18 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
break; break;
case NAND_CMD_PARAM: case NAND_CMD_PARAM:
info->buf_count = 256; info->buf_count = INIT_BUFFER_SIZE;
info->ndcb0 |= NDCB0_CMD_TYPE(0) info->ndcb0 |= NDCB0_CMD_TYPE(0)
| NDCB0_ADDR_CYC(1) | NDCB0_ADDR_CYC(1)
| NDCB0_LEN_OVRD | NDCB0_LEN_OVRD
| command; | command;
info->ndcb1 = (column & 0xFF); info->ndcb1 = (column & 0xFF);
info->ndcb3 = 256; info->ndcb3 = INIT_BUFFER_SIZE;
info->data_size = 256; info->data_size = INIT_BUFFER_SIZE;
break; break;
case NAND_CMD_READID: case NAND_CMD_READID:
info->buf_count = host->read_id_bytes; info->buf_count = READ_ID_BYTES;
info->ndcb0 |= NDCB0_CMD_TYPE(3) info->ndcb0 |= NDCB0_CMD_TYPE(3)
| NDCB0_ADDR_CYC(1) | NDCB0_ADDR_CYC(1)
| command; | command;
...@@ -1247,9 +1259,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, ...@@ -1247,9 +1259,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
return -EINVAL; return -EINVAL;
} }
/* calculate flash information */
host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
/* calculate addressing information */ /* calculate addressing information */
host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; 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, ...@@ -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->flash_width == 16) ? NDCR_DWIDTH_M : 0;
ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 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 */ ndcr |= NDCR_SPARE_EN; /* enable spare by default */
info->reg_ndcr = ndcr; info->reg_ndcr = ndcr;
...@@ -1276,23 +1285,10 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, ...@@ -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) 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); 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 */ /* Set an initial chunk size */
info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
info->reg_ndcr = ndcr & ~NDCR_INT_MASK; info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
info->ndtr1cs0 = nand_readl(info, NDTR1CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
...@@ -1473,6 +1469,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) ...@@ -1473,6 +1469,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
goto KEEP_CONFIG; goto KEEP_CONFIG;
/* Set a default chunk size */
info->chunk_size = 512;
ret = pxa3xx_nand_sensing(info); ret = pxa3xx_nand_sensing(info);
if (ret) { if (ret) {
dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
......
...@@ -466,7 +466,7 @@ static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, ...@@ -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, static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc) uint8_t *read_ecc, uint8_t *calc_ecc)
{ {
uint16_t ecc_reg; uint32_t ecc_reg;
uint8_t ecc_status, err_byte; uint8_t ecc_status, err_byte;
int i, error = 0; int i, error = 0;
......
...@@ -99,6 +99,15 @@ ...@@ -99,6 +99,15 @@
NFC_CMD_INT_ENABLE | \ NFC_CMD_INT_ENABLE | \
NFC_DMA_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 bit use in NFC_CMD */
#define NFC_CMD_LOW_BYTE GENMASK(7, 0) #define NFC_CMD_LOW_BYTE GENMASK(7, 0)
#define NFC_CMD_HIGH_BYTE GENMASK(15, 8) #define NFC_CMD_HIGH_BYTE GENMASK(15, 8)
...@@ -208,6 +217,7 @@ struct sunxi_nand_hw_ecc { ...@@ -208,6 +217,7 @@ struct sunxi_nand_hw_ecc {
* @nand: base NAND chip structure * @nand: base NAND chip structure
* @mtd: base MTD structure * @mtd: base MTD structure
* @clk_rate: clk_rate required for this NAND chip * @clk_rate: clk_rate required for this NAND chip
* @timing_cfg TIMING_CFG register value for this NAND chip
* @selected: current active CS * @selected: current active CS
* @nsels: number of CS lines required by the NAND chip * @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions * @sels: array of CS lines descriptions
...@@ -217,6 +227,8 @@ struct sunxi_nand_chip { ...@@ -217,6 +227,8 @@ struct sunxi_nand_chip {
struct nand_chip nand; struct nand_chip nand;
struct mtd_info mtd; struct mtd_info mtd;
unsigned long clk_rate; unsigned long clk_rate;
u32 timing_cfg;
u32 timing_ctl;
int selected; int selected;
int nsels; int nsels;
struct sunxi_nand_chip_sel sels[0]; struct sunxi_nand_chip_sel sels[0];
...@@ -403,6 +415,8 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) ...@@ -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); writel(ctl, nfc->regs + NFC_REG_CTL);
sunxi_nand->selected = chip; sunxi_nand->selected = chip;
...@@ -807,10 +821,33 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, ...@@ -807,10 +821,33 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
return 0; 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, static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings) const struct nand_sdr_timings *timings)
{ {
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
u32 min_clk_period = 0; u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD;
/* T1 <=> tCLS */ /* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period) if (timings->tCLS_min > min_clk_period)
...@@ -872,6 +909,48 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, ...@@ -872,6 +909,48 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
if (timings->tWC_min > (min_clk_period * 2)) if (timings->tWC_min > (min_clk_period * 2))
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 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 */ /* Convert min_clk_period from picoseconds to nanoseconds */
min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); 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, ...@@ -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; chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
/* TODO: configure T16-T19 */
return 0; return 0;
} }
...@@ -1376,13 +1453,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) ...@@ -1376,13 +1453,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, nfc); 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); ret = sunxi_nand_chips_init(dev, nfc);
if (ret) { if (ret) {
dev_err(dev, "failed to init nand chips\n"); dev_err(dev, "failed to init nand chips\n");
......
...@@ -26,6 +26,18 @@ config SPI_FSL_QUADSPI ...@@ -26,6 +26,18 @@ config SPI_FSL_QUADSPI
depends on ARCH_MXC depends on ARCH_MXC
help help
This enables support for the Quad SPI controller in master mode. 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 endif # MTD_SPI_NOR
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
...@@ -26,6 +26,20 @@ ...@@ -26,6 +26,20 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.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 */ /* The registers */
#define QUADSPI_MCR 0x00 #define QUADSPI_MCR 0x00
...@@ -191,9 +205,13 @@ ...@@ -191,9 +205,13 @@
#define SEQID_EN4B 10 #define SEQID_EN4B 10
#define SEQID_BRWR 11 #define SEQID_BRWR 11
#define QUADSPI_MIN_IOMAP SZ_4M
enum fsl_qspi_devtype { enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID, FSL_QUADSPI_VYBRID,
FSL_QUADSPI_IMX6SX, FSL_QUADSPI_IMX6SX,
FSL_QUADSPI_IMX7D,
FSL_QUADSPI_IMX6UL,
}; };
struct fsl_qspi_devtype_data { struct fsl_qspi_devtype_data {
...@@ -201,20 +219,42 @@ struct fsl_qspi_devtype_data { ...@@ -201,20 +219,42 @@ struct fsl_qspi_devtype_data {
int rxfifo; int rxfifo;
int txfifo; int txfifo;
int ahb_buf_size; int ahb_buf_size;
int driver_data;
}; };
static struct fsl_qspi_devtype_data vybrid_data = { static struct fsl_qspi_devtype_data vybrid_data = {
.devtype = FSL_QUADSPI_VYBRID, .devtype = FSL_QUADSPI_VYBRID,
.rxfifo = 128, .rxfifo = 128,
.txfifo = 64, .txfifo = 64,
.ahb_buf_size = 1024 .ahb_buf_size = 1024,
.driver_data = QUADSPI_QUIRK_SWAP_ENDIAN,
}; };
static struct fsl_qspi_devtype_data imx6sx_data = { static struct fsl_qspi_devtype_data imx6sx_data = {
.devtype = FSL_QUADSPI_IMX6SX, .devtype = FSL_QUADSPI_IMX6SX,
.rxfifo = 128, .rxfifo = 128,
.txfifo = 512, .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 #define FSL_QSPI_MAX_CHIP 4
...@@ -222,8 +262,10 @@ struct fsl_qspi { ...@@ -222,8 +262,10 @@ struct fsl_qspi {
struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; struct mtd_info mtd[FSL_QSPI_MAX_CHIP];
struct spi_nor nor[FSL_QSPI_MAX_CHIP]; struct spi_nor nor[FSL_QSPI_MAX_CHIP];
void __iomem *iobase; void __iomem *iobase;
void __iomem *ahb_base; /* Used when read from AHB bus */ void __iomem *ahb_addr;
u32 memmap_phy; u32 memmap_phy;
u32 memmap_offs;
u32 memmap_len;
struct clk *clk, *clk_en; struct clk *clk, *clk_en;
struct device *dev; struct device *dev;
struct completion c; struct completion c;
...@@ -233,16 +275,28 @@ struct fsl_qspi { ...@@ -233,16 +275,28 @@ struct fsl_qspi {
u32 clk_rate; u32 clk_rate;
unsigned int chip_base_addr; /* We may support two chips. */ unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip; 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) ...@@ -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) 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) 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) ...@@ -343,14 +397,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Erase a sector */ /* Erase a sector */
lut_base = SEQID_SE * 4; lut_base = SEQID_SE * 4;
if (q->nor_size <= SZ_16M) { cmd = q->nor[0].erase_opcode;
cmd = SPINOR_OP_SE; addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
addrlen = ADDR24BIT;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_SE;
addrlen = ADDR32BIT;
}
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
...@@ -419,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) ...@@ -419,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
case SPINOR_OP_BRWR: case SPINOR_OP_BRWR:
return SEQID_BRWR; return SEQID_BRWR;
default: default:
if (cmd == q->nor[0].erase_opcode)
return SEQID_SE;
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break; break;
} }
...@@ -537,7 +587,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, ...@@ -537,7 +587,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
/* clear the TX FIFO. */ /* clear the TX FIFO. */
tmp = readl(q->iobase + QUADSPI_MCR); 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 */ /* fill the TX data to the FIFO */
for (j = 0, i = ((count + 3) / 4); j < i; j++) { 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, ...@@ -546,6 +596,11 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
txbuf++; 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 */ /* Trigger it */
ret = fsl_qspi_runcmd(q, opcode, to, count); ret = fsl_qspi_runcmd(q, opcode, to, count);
...@@ -606,6 +661,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) ...@@ -606,6 +661,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
q->iobase + QUADSPI_BFGENCR); 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(). */ /* We use this function to do some basic init for spi_nor_scan(). */
static int fsl_qspi_nor_setup(struct fsl_qspi *q) static int fsl_qspi_nor_setup(struct fsl_qspi *q)
{ {
...@@ -613,11 +700,23 @@ 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; u32 reg;
int ret; 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); ret = clk_set_rate(q->clk, 66000000);
if (ret) if (ret)
return 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. */ /* Init the LUT table. */
fsl_qspi_init_lut(q); fsl_qspi_init_lut(q);
...@@ -635,6 +734,9 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *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, writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
base + QUADSPI_MCR); base + QUADSPI_MCR);
/* clear all interrupt status */
writel(0xffffffff, q->iobase + QUADSPI_FR);
/* enable the interrupt */ /* enable the interrupt */
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
...@@ -646,13 +748,20 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) ...@@ -646,13 +748,20 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
unsigned long rate = q->clk_rate; unsigned long rate = q->clk_rate;
int ret; int ret;
if (is_imx6sx_qspi(q)) if (needs_4x_clock(q))
rate *= 4; 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); ret = clk_set_rate(q->clk, rate);
if (ret) if (ret)
return ret; return ret;
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;
/* Init the LUT table again. */ /* Init the LUT table again. */
fsl_qspi_init_lut(q); fsl_qspi_init_lut(q);
...@@ -665,6 +774,8 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *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[] = { static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_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 */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
...@@ -730,11 +841,42 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from, ...@@ -730,11 +841,42 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
struct fsl_qspi *q = nor->priv; struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode; u8 cmd = nor->read_opcode;
dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", /* if necessary,ioremap buffer before AHB read, */
cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); 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.*/ /* 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; *retlen += len;
return 0; return 0;
...@@ -761,26 +903,26 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) ...@@ -761,26 +903,26 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
struct fsl_qspi *q = nor->priv; struct fsl_qspi *q = nor->priv;
int ret; int ret;
ret = clk_enable(q->clk_en); mutex_lock(&q->lock);
if (ret)
return ret;
ret = clk_enable(q->clk); ret = fsl_qspi_clk_prep_enable(q);
if (ret) { if (ret)
clk_disable(q->clk_en); goto err_mutex;
return ret;
}
fsl_qspi_set_base_addr(q, nor); fsl_qspi_set_base_addr(q, nor);
return 0; return 0;
err_mutex:
mutex_unlock(&q->lock);
return ret;
} }
static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{ {
struct fsl_qspi *q = nor->priv; struct fsl_qspi *q = nor->priv;
clk_disable(q->clk); fsl_qspi_clk_disable_unprep(q);
clk_disable(q->clk_en); mutex_unlock(&q->lock);
} }
static int fsl_qspi_probe(struct platform_device *pdev) static int fsl_qspi_probe(struct platform_device *pdev)
...@@ -804,6 +946,10 @@ 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) if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP)
return -ENODEV; return -ENODEV;
q->dev = dev;
q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
platform_set_drvdata(pdev, q);
/* find the resources */ /* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
q->iobase = devm_ioremap_resource(dev, res); q->iobase = devm_ioremap_resource(dev, res);
...@@ -812,9 +958,11 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -812,9 +958,11 @@ static int fsl_qspi_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory"); "QuadSPI-memory");
q->ahb_base = devm_ioremap_resource(dev, res); if (!devm_request_mem_region(dev, res->start, resource_size(res),
if (IS_ERR(q->ahb_base)) res->name)) {
return PTR_ERR(q->ahb_base); dev_err(dev, "can't request region for resource %pR\n", res);
return -EBUSY;
}
q->memmap_phy = res->start; q->memmap_phy = res->start;
...@@ -827,15 +975,9 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -827,15 +975,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (IS_ERR(q->clk)) if (IS_ERR(q->clk))
return PTR_ERR(q->clk); return PTR_ERR(q->clk);
ret = clk_prepare_enable(q->clk_en); ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
dev_err(dev, "cannot enable the qspi_en clock: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(q->clk);
if (ret) { 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; goto clk_failed;
} }
...@@ -853,10 +995,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -853,10 +995,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
goto irq_failed; 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); ret = fsl_qspi_nor_setup(q);
if (ret) if (ret)
goto irq_failed; goto irq_failed;
...@@ -864,6 +1002,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -864,6 +1002,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
q->has_second_chip = true; q->has_second_chip = true;
mutex_init(&q->lock);
/* iterate the subnodes. */ /* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) { for_each_available_child_of_node(dev->of_node, np) {
char modalias[40]; char modalias[40];
...@@ -892,24 +1032,24 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -892,24 +1032,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
ret = of_modalias_node(np, modalias, sizeof(modalias)); ret = of_modalias_node(np, modalias, sizeof(modalias));
if (ret < 0) if (ret < 0)
goto irq_failed; goto mutex_failed;
ret = of_property_read_u32(np, "spi-max-frequency", ret = of_property_read_u32(np, "spi-max-frequency",
&q->clk_rate); &q->clk_rate);
if (ret < 0) if (ret < 0)
goto irq_failed; goto mutex_failed;
/* set the chip address for READID */ /* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor); fsl_qspi_set_base_addr(q, nor);
ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
if (ret) if (ret)
goto irq_failed; goto mutex_failed;
ppdata.of_node = np; ppdata.of_node = np;
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (ret) if (ret)
goto irq_failed; goto mutex_failed;
/* Set the correct NOR size now. */ /* Set the correct NOR size now. */
if (q->nor_size == 0) { if (q->nor_size == 0) {
...@@ -939,8 +1079,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -939,8 +1079,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret) if (ret)
goto last_init_failed; goto last_init_failed;
clk_disable(q->clk); fsl_qspi_clk_disable_unprep(q);
clk_disable(q->clk_en);
return 0; return 0;
last_init_failed: last_init_failed:
...@@ -950,10 +1089,12 @@ static int fsl_qspi_probe(struct platform_device *pdev) ...@@ -950,10 +1089,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
i *= 2; i *= 2;
mtd_device_unregister(&q->mtd[i]); mtd_device_unregister(&q->mtd[i]);
} }
mutex_failed:
mutex_destroy(&q->lock);
irq_failed: irq_failed:
clk_disable_unprepare(q->clk); fsl_qspi_clk_disable_unprep(q);
clk_failed: clk_failed:
clk_disable_unprepare(q->clk_en); dev_err(dev, "Freescale QuadSPI probe failed\n");
return ret; return ret;
} }
...@@ -973,8 +1114,11 @@ static int fsl_qspi_remove(struct platform_device *pdev) ...@@ -973,8 +1114,11 @@ static int fsl_qspi_remove(struct platform_device *pdev)
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER); writel(0x0, q->iobase + QUADSPI_RSER);
clk_unprepare(q->clk); mutex_destroy(&q->lock);
clk_unprepare(q->clk_en);
if (q->ahb_addr)
iounmap(q->ahb_addr);
return 0; return 0;
} }
...@@ -985,12 +1129,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -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) static int fsl_qspi_resume(struct platform_device *pdev)
{ {
int ret;
struct fsl_qspi *q = platform_get_drvdata(pdev); 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_nor_setup(q);
fsl_qspi_set_map_addr(q); fsl_qspi_set_map_addr(q);
fsl_qspi_nor_setup_last(q); fsl_qspi_nor_setup_last(q);
fsl_qspi_clk_disable_unprep(q);
return 0; return 0;
} }
......
/*
* 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");
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#define SPI_NOR_MAX_ID_LEN 6 #define SPI_NOR_MAX_ID_LEN 6
struct flash_info { struct flash_info {
char *name;
/* /*
* This array stores the ID bytes. * This array stores the ID bytes.
* The first three bytes are the JEDIC ID. * The first three bytes are the JEDIC ID.
...@@ -59,7 +61,7 @@ struct flash_info { ...@@ -59,7 +61,7 @@ struct flash_info {
#define JEDEC_MFR(info) ((info)->id[0]) #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 * 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) ...@@ -169,7 +171,7 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
} }
/* Enable/disable 4-byte addressing mode. */ /* 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 enable)
{ {
int status; int status;
...@@ -469,7 +471,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -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 */ /* Used when the "_ext_id" is two bytes at most */
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.id = { \ .id = { \
((_jedec_id) >> 16) & 0xff, \ ((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \ ((_jedec_id) >> 8) & 0xff, \
...@@ -481,11 +482,9 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -481,11 +482,9 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.sector_size = (_sector_size), \ .sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = 256, \ .page_size = 256, \
.flags = (_flags), \ .flags = (_flags),
})
#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.id = { \ .id = { \
((_jedec_id) >> 16) & 0xff, \ ((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \ ((_jedec_id) >> 8) & 0xff, \
...@@ -498,17 +497,14 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -498,17 +497,14 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.sector_size = (_sector_size), \ .sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = 256, \ .page_size = 256, \
.flags = (_flags), \ .flags = (_flags),
})
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.sector_size = (_sector_size), \ .sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = (_page_size), \ .page_size = (_page_size), \
.addr_width = (_addr_width), \ .addr_width = (_addr_width), \
.flags = (_flags), \ .flags = (_flags),
})
/* NOTE: double check command sets and memory organization when you add /* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which * 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) ...@@ -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 * For historical (and compatibility) reasons (before we got above config) some
* old entries may be missing 4K flag. * 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" */ /* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
...@@ -589,7 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = { ...@@ -589,7 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = {
/* Micron */ /* Micron */
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, { "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) }, { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
{ "n25q128a13", INFO(0x20ba18, 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) }, { "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[] = { ...@@ -626,6 +622,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, 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 */ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
...@@ -702,11 +699,11 @@ static const struct spi_device_id spi_nor_ids[] = { ...@@ -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; int tmp;
u8 id[SPI_NOR_MAX_ID_LEN]; 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); tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) { if (tmp < 0) {
...@@ -715,7 +712,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) ...@@ -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++) { 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 (info->id_len) {
if (!memcmp(info->id, id, info->id_len)) if (!memcmp(info->id, id, info->id_len))
return &spi_nor_ids[tmp]; return &spi_nor_ids[tmp];
...@@ -961,7 +958,7 @@ static int micron_quad_enable(struct spi_nor *nor) ...@@ -961,7 +958,7 @@ static int micron_quad_enable(struct spi_nor *nor)
return 0; 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; int status;
...@@ -1003,8 +1000,7 @@ static int spi_nor_check(struct spi_nor *nor) ...@@ -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) int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
{ {
const struct spi_device_id *id = NULL; const struct flash_info *info = NULL;
struct flash_info *info;
struct device *dev = nor->dev; struct device *dev = nor->dev;
struct mtd_info *mtd = nor->mtd; struct mtd_info *mtd = nor->mtd;
struct device_node *np = dev->of_node; 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) ...@@ -1015,27 +1011,25 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (ret) if (ret)
return ret; return ret;
/* Try to auto-detect if chip name wasn't specified */ if (name)
if (!name) info = spi_nor_match_id(name);
id = spi_nor_read_id(nor); /* Try to auto-detect if chip name wasn't specified or not found */
else if (!info)
id = spi_nor_match_id(name); info = spi_nor_read_id(nor);
if (IS_ERR_OR_NULL(id)) if (IS_ERR_OR_NULL(info))
return -ENOENT; return -ENOENT;
info = (void *)id->driver_data;
/* /*
* If caller has specified name of flash model that can normally be * If caller has specified name of flash model that can normally be
* detected using JEDEC, let's verify it. * detected using JEDEC, let's verify it.
*/ */
if (name && info->id_len) { if (name && info->id_len) {
const struct spi_device_id *jid; const struct flash_info *jinfo;
jid = spi_nor_read_id(nor); jinfo = spi_nor_read_id(nor);
if (IS_ERR(jid)) { if (IS_ERR(jinfo)) {
return PTR_ERR(jid); return PTR_ERR(jinfo);
} else if (jid != id) { } else if (jinfo != info) {
/* /*
* JEDEC knows better, so overwrite platform ID. We * JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let * 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) ...@@ -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. * information, even if it's not 100% accurate.
*/ */
dev_warn(dev, "found %s, expected %s\n", dev_warn(dev, "found %s, expected %s\n",
jid->name, id->name); jinfo->name, info->name);
id = jid; info = jinfo;
info = (void *)jid->driver_data;
} }
} }
...@@ -1196,7 +1189,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ...@@ -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); 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); (long long)mtd->size >> 10);
dev_dbg(dev, dev_dbg(dev,
...@@ -1219,9 +1212,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ...@@ -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); 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]) { while (id->name[0]) {
if (!strcmp(name, id->name)) if (!strcmp(name, id->name))
......
...@@ -125,7 +125,8 @@ static int write_whole_device(void) ...@@ -125,7 +125,8 @@ static int write_whole_device(void)
* Display the address, offset and data bytes at comparison failure. * Display the address, offset and data bytes at comparison failure.
* Return number of bitflips encountered. * 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; const unsigned char *su1, *su2;
int res; int res;
...@@ -135,8 +136,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou ...@@ -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++) { for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
res = *su1 ^ *su2; res = *su1 ^ *su2;
if (res) { if (res) {
pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n", pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n",
(unsigned long)addr, i, *su1, *su2, res); (unsigned long)addr, (unsigned long)offset + i,
*su1, *su2, res);
bitflips += hweight8(res); bitflips += hweight8(res);
} }
} }
...@@ -144,6 +146,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou ...@@ -144,6 +146,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou
return bitflips; 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 * Compare with 0xff and show the address, offset and data bytes at
* comparison failure. Return number of bitflips encountered. * comparison failure. Return number of bitflips encountered.
...@@ -228,9 +233,10 @@ static int verify_eraseblock(int ebnum) ...@@ -228,9 +233,10 @@ static int verify_eraseblock(int ebnum)
errcnt += 1; errcnt += 1;
return err ? err : -1; return err ? err : -1;
} }
bitflips = memcmpshow(addr, readbuf + use_offset, bitflips = memcmpshowoffset(addr, use_offset,
writebuf + (use_len_max * i) + use_offset, readbuf + use_offset,
use_len); writebuf + (use_len_max * i) + use_offset,
use_len);
/* verify pre-offset area for 0xff */ /* verify pre-offset area for 0xff */
bitflips += memffshow(addr, 0, readbuf, use_offset); bitflips += memffshow(addr, 0, readbuf, use_offset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment