mirror of https://gitee.com/openkylin/linux.git
MTD updates for 4.3-rc1:
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJV5h5kAAoJEFySrpd9RFgt0fYP/3mw4kn4F8c+LjD4BTgx95Zt QQ0AZ8lnv+++NvlXsYH6RwqO1prqDceSNlPfdfIuMNIyinYZ/qpacGqxDMtZ5/IT HiTQRNlHrIejb5mA7bJueWuxN2O+O83e0k48KTgUiW+H6APgpCgq6tOvNu70zByz u8FFqMMefM2RrUdI4ittZLFs+BdM5HPw4MrR3c+itBITOEH3xt0tYjH0q/ie68CV 8mK0YP0DFnLFczBd3sqs4B24nOypezXLEJWZ0rD5XjKXm7XZ+9h3rKsfagtrBa14 ZO6+G49Wsf83M1chVQ6D++vinV1MyUrhfC+LE5D6gKJGlsRI1Yguw3vyNKpjAi61 MzKQY/uRiDLQI/OJ28TM50DYXLVvyWPNjXVB0+O8Y3jYcNpyLKPUv2LQdKuZcLsq PqSEyOh/6fOsr98ozt7CUCMORKhtekxBtOUSjNisdbl1vSOPysws91TftHaXXvZl 4AFEk1wmrp6FWOaeN5ZoYihF39fHl6XAZEhJZPGdOKXbuHK9Bdrspr8c2PMArOUj uevP20Ol3vq+7Uc0A6xwPlk4mj97aP3xH18eRdTE6V0WLC4gjc2jMYlg/lpvYVvD o1iJgHcqN5dgHsfJIkyRH4ymL404pd3W/5CfMqnMjDvXtL7pcZ8Sr1G6XSMZGyYB XAT43tzKGA2GjGd4Pqin =vpBS -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
91a247d7d3
|
@ -1,7 +1,8 @@
|
|||
* Freescale Quad Serial Peripheral Interface(QuadSPI)
|
||||
|
||||
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,
|
||||
the second contains the memory mapping address and length
|
||||
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
* 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)
|
|||
*/
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
||||
/* ......................................................................... */
|
||||
|
|
|
@ -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 @@ static int __init nettel_init(void)
|
|||
iounmap(nettel_mmcrp);
|
||||
iounmap(nettel_amd_map.virt);
|
||||
|
||||
return(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
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);
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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->devtype == FSL_QUADSPI_VYBRID;
|
||||
return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN;
|
||||
}
|
||||
|
||||
static inline int is_imx6sx_qspi(struct fsl_qspi *q)
|
||||
static inline int needs_4x_clock(struct fsl_qspi *q)
|
||||
{
|
||||
return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX;
|
||||
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->driver_data & QUADSPI_QUIRK_TKT253890;
|
||||
}
|
||||
|
||||
static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
|
||||
{
|
||||
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);
|
||||
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) {
|
||||
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 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue