You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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 @@ out_unmap2:
|
||||
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 @@ failed_remap_mem:
|
||||
iounmap(denali->flash_mem);
|
||||
failed_remap_reg:
|
||||
iounmap(denali->flash_reg);
|
||||
failed_req_regions:
|
||||
pci_release_regions(dev);
|
||||
failed_enable_dev:
|
||||
pci_disable_device(dev);
|
||||
failed_alloc_memery:
|
||||
kfree(denali);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -117,9 +109,6 @@ static void denali_pci_remove(struct pci_dev *dev)
|
||||
denali_remove(denali);
|
||||
iounmap(denali->flash_reg);
|
||||
iounmap(denali->flash_mem);
|
||||
pci_release_regions(dev);
|
||||
pci_disable_device(dev);
|
||||
kfree(denali);
|
||||
}
|
||||
|
||||
static struct pci_driver denali_pci_driver = {
|
||||
@@ -129,14 +118,4 @@ static struct pci_driver denali_pci_driver = {
|
||||
.remove = denali_pci_remove,
|
||||
};
|
||||
|
||||
static int denali_init_pci(void)
|
||||
{
|
||||
return pci_register_driver(&denali_pci_driver);
|
||||
}
|
||||
module_init(denali_init_pci);
|
||||
|
||||
static void denali_exit_pci(void)
|
||||
{
|
||||
pci_unregister_driver(&denali_pci_driver);
|
||||
}
|
||||
module_exit(denali_exit_pci);
|
||||
module_pci_driver(denali_pci_driver);
|
||||
|
||||
@@ -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 @@ last_init_failed:
|
||||
i *= 2;
|
||||
mtd_device_unregister(&q->mtd[i]);
|
||||
}
|
||||
mutex_failed:
|
||||
mutex_destroy(&q->lock);
|
||||
irq_failed:
|
||||
clk_disable_unprepare(q->clk);
|
||||
fsl_qspi_clk_disable_unprep(q);
|
||||
clk_failed:
|
||||
clk_disable_unprepare(q->clk_en);
|
||||
dev_err(dev, "Freescale QuadSPI probe failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -973,8 +1114,11 @@ static int fsl_qspi_remove(struct platform_device *pdev)
|
||||
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
|
||||
writel(0x0, q->iobase + QUADSPI_RSER);
|
||||
|
||||
clk_unprepare(q->clk);
|
||||
clk_unprepare(q->clk_en);
|
||||
mutex_destroy(&q->lock);
|
||||
|
||||
if (q->ahb_addr)
|
||||
iounmap(q->ahb_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -985,12 +1129,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
|
||||
static int fsl_qspi_resume(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct fsl_qspi *q = platform_get_drvdata(pdev);
|
||||
|
||||
ret = fsl_qspi_clk_prep_enable(q);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsl_qspi_nor_setup(q);
|
||||
fsl_qspi_set_map_addr(q);
|
||||
fsl_qspi_nor_setup_last(q);
|
||||
|
||||
fsl_qspi_clk_disable_unprep(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user