summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-09-09 14:48:21 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-09-09 14:48:21 -0700
commita59e57da49f7c3f3de8cf4b7568a0c6c82f5b242 (patch)
tree521ccae942f9d226409cadb5503f74f78858e0af /drivers/mtd
parent0ce5c79f384b9f730cf03a1e464673ae906e7c89 (diff)
parentd1f936d73683a540227cca3aaecdb68b6c3d53c5 (diff)
downloadlinux-a59e57da49f7c3f3de8cf4b7568a0c6c82f5b242.tar.bz2
Merge tag 'for-linus-20170904' of git://git.infradead.org/linux-mtd
Pull MTD updates from Boris Brezillon: "General updates: - Constify pci_device_id in various drivers - Constify device_type - Remove pad control code from the Gemini driver - Use %pOF to print OF node full_name - Various fixes in the physmap_of driver - Remove unused vars in mtdswap - Check devm_kzalloc() return value in the spear_smi driver - Check clk_prepare_enable() return code in the st_spi_fsm driver - Create per MTD device debugfs enties NAND updates, from Boris Brezillon: - Fix memory leaks in the core - Remove unused NAND locking support - Rename nand.h into rawnand.h (preparing support for spi NANDs) - Use NAND_MAX_ID_LEN where appropriate - Fix support for 20nm Hynix chips - Fix support for Samsung and Hynix SLC NANDs - Various cleanup, improvements and fixes in the qcom driver - Fixes for bugs detected by various static code analysis tools - Fix mxc ooblayout definition - Add a new part_parsers to tmio and sharpsl platform data in order to define a custom list of partition parsers - Request the reset line in exclusive mode in the sunxi driver - Fix a build error in the orion-nand driver when compiled for ARMv4 - Allow 64-bit mvebu platforms to select the PXA3XX driver SPI NOR updates, from Cyrille Pitchen and Marek Vasut: - add support to the JEDEC JESD216B specification (SFDP tables). - add support to the Intel Denverton SPI flash controller. - fix error recovery for Spansion/Cypress SPI NOR memories. - fix 4-byte address management for the Aspeed SPI controller. - add support to some Microchip SST26 memory parts - remove unneeded pinctrl header Write a message for tag:" * tag 'for-linus-20170904' of git://git.infradead.org/linux-mtd: (74 commits) mtd: nand: complain loudly when chip->bits_per_cell is not correctly initialized mtd: nand: make Samsung SLC NAND usable again mtd: nand: tmio: Register partitions using the parsers mfd: tmio: Add partition parsers platform data mtd: nand: sharpsl: Register partitions using the parsers mtd: nand: sharpsl: Add partition parsers platform data mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller mtd: nand: qcom: support for IPQ4019 QPIC NAND controller dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation dt-bindings: qcom_nandc: fix the ipq806x device tree example mtd: nand: qcom: support for different DEV_CMD register offsets mtd: nand: qcom: QPIC data descriptors handling mtd: nand: qcom: enable BAM or ADM mode mtd: nand: qcom: erased codeword detection configuration mtd: nand: qcom: support for read location registers mtd: nand: qcom: support for passing flags in DMA helper functions mtd: nand: qcom: add BAM DMA descriptor handling mtd: nand: qcom: allocate BAM transaction mtd: nand: qcom: DMA mapping support for register read buffer ...
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/docg3.c49
-rw-r--r--drivers/mtd/devices/docg3.h2
-rw-r--r--drivers/mtd/devices/spear_smi.c2
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c20
-rw-r--r--drivers/mtd/inftlcore.c2
-rw-r--r--drivers/mtd/maps/amd76xrom.c4
-rw-r--r--drivers/mtd/maps/ck804xrom.c4
-rw-r--r--drivers/mtd/maps/esb2rom.c4
-rw-r--r--drivers/mtd/maps/ichxrom.c4
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c2
-rw-r--r--drivers/mtd/maps/pci.c2
-rw-r--r--drivers/mtd/maps/physmap_of_core.c8
-rw-r--r--drivers/mtd/maps/physmap_of_gemini.c16
-rw-r--r--drivers/mtd/maps/physmap_of_versatile.c2
-rw-r--r--drivers/mtd/maps/sun_uflash.c4
-rw-r--r--drivers/mtd/mtdcore.c18
-rw-r--r--drivers/mtd/mtdswap.c21
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/ams-delta.c2
-rw-r--r--drivers/mtd/nand/atmel/nand-controller.c15
-rw-r--r--drivers/mtd/nand/atmel/pmecc.c2
-rw-r--r--drivers/mtd/nand/au1550nd.c2
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h2
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c2
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c2
-rw-r--r--drivers/mtd/nand/cafe_nand.c2
-rw-r--r--drivers/mtd/nand/cmx270_nand.c2
-rw-r--r--drivers/mtd/nand/cs553x_nand.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c2
-rw-r--r--drivers/mtd/nand/denali.h2
-rw-r--r--drivers/mtd/nand/denali_dt.c4
-rw-r--r--drivers/mtd/nand/diskonchip.c2
-rw-r--r--drivers/mtd/nand/docg4.c2
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c2
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c2
-rw-r--r--drivers/mtd/nand/fsl_upm.c2
-rw-r--r--drivers/mtd/nand/fsmc_nand.c2
-rw-r--r--drivers/mtd/nand/gpio.c2
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h2
-rw-r--r--drivers/mtd/nand/hisi504_nand.c2
-rw-r--r--drivers/mtd/nand/jz4740_nand.c2
-rw-r--r--drivers/mtd/nand/jz4780_nand.c2
-rw-r--r--drivers/mtd/nand/lpc32xx_mlc.c11
-rw-r--r--drivers/mtd/nand/lpc32xx_slc.c11
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c2
-rw-r--r--drivers/mtd/nand/mtk_ecc.c4
-rw-r--r--drivers/mtd/nand/mtk_nand.c2
-rw-r--r--drivers/mtd/nand/mxc_nand.c9
-rw-r--r--drivers/mtd/nand/nand_amd.c2
-rw-r--r--drivers/mtd/nand/nand_base.c304
-rw-r--r--drivers/mtd/nand/nand_bbt.c2
-rw-r--r--drivers/mtd/nand/nand_bch.c2
-rw-r--r--drivers/mtd/nand/nand_ecc.c2
-rw-r--r--drivers/mtd/nand/nand_hynix.c6
-rw-r--r--drivers/mtd/nand/nand_ids.c2
-rw-r--r--drivers/mtd/nand/nand_macronix.c2
-rw-r--r--drivers/mtd/nand/nand_micron.c2
-rw-r--r--drivers/mtd/nand/nand_samsung.c2
-rw-r--r--drivers/mtd/nand/nand_timings.c2
-rw-r--r--drivers/mtd/nand/nand_toshiba.c2
-rw-r--r--drivers/mtd/nand/nandsim.c51
-rw-r--r--drivers/mtd/nand/ndfc.c2
-rw-r--r--drivers/mtd/nand/nuc900_nand.c2
-rw-r--r--drivers/mtd/nand/omap2.c2
-rw-r--r--drivers/mtd/nand/orion_nand.c9
-rw-r--r--drivers/mtd/nand/oxnas_nand.c27
-rw-r--r--drivers/mtd/nand/pasemi_nand.c2
-rw-r--r--drivers/mtd/nand/plat_nand.c2
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c2
-rw-r--r--drivers/mtd/nand/qcom_nandc.c919
-rw-r--r--drivers/mtd/nand/r852.h2
-rw-r--r--drivers/mtd/nand/s3c2410.c2
-rw-r--r--drivers/mtd/nand/sh_flctl.c8
-rw-r--r--drivers/mtd/nand/sharpsl.c4
-rw-r--r--drivers/mtd/nand/sm_common.c2
-rw-r--r--drivers/mtd/nand/socrates_nand.c2
-rw-r--r--drivers/mtd/nand/sunxi_nand.c4
-rw-r--r--drivers/mtd/nand/tango_nand.c2
-rw-r--r--drivers/mtd/nand/tmio_nand.c6
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c2
-rw-r--r--drivers/mtd/nand/vf610_nfc.c11
-rw-r--r--drivers/mtd/nand/xway_nand.c2
-rw-r--r--drivers/mtd/nftlcore.c2
-rw-r--r--drivers/mtd/nftlmount.c2
-rw-r--r--drivers/mtd/ofpart.c23
-rw-r--r--drivers/mtd/spi-nor/Kconfig16
-rw-r--r--drivers/mtd/spi-nor/Makefile3
-rw-r--r--drivers/mtd/spi-nor/aspeed-smc.c13
-rw-r--r--drivers/mtd/spi-nor/atmel-quadspi.c1
-rw-r--r--drivers/mtd/spi-nor/hisi-sfc.c8
-rw-r--r--drivers/mtd/spi-nor/intel-spi-pci.c82
-rw-r--r--drivers/mtd/spi-nor/mtk-quadspi.c1
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c805
-rw-r--r--drivers/mtd/ssfdc.c2
-rw-r--r--drivers/mtd/tests/nandbiterrs.c2
95 files changed, 1951 insertions, 672 deletions
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index b833e6cc684c..84b16133554b 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1809,37 +1809,22 @@ static int dbg_protection_show(struct seq_file *s, void *p)
}
DEBUGFS_RO_ATTR(protection, dbg_protection_show);
-static int __init doc_dbg_register(struct docg3 *docg3)
-{
- struct dentry *root, *entry;
-
- root = debugfs_create_dir("docg3", NULL);
- if (!root)
- return -ENOMEM;
-
- entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3,
- &flashcontrol_fops);
- if (entry)
- entry = debugfs_create_file("asic_mode", S_IRUSR, root,
- docg3, &asic_mode_fops);
- if (entry)
- entry = debugfs_create_file("device_id", S_IRUSR, root,
- docg3, &device_id_fops);
- if (entry)
- entry = debugfs_create_file("protection", S_IRUSR, root,
- docg3, &protection_fops);
- if (entry) {
- docg3->debugfs_root = root;
- return 0;
- } else {
- debugfs_remove_recursive(root);
- return -ENOMEM;
- }
-}
-
-static void doc_dbg_unregister(struct docg3 *docg3)
+static void __init doc_dbg_register(struct mtd_info *floor)
{
- debugfs_remove_recursive(docg3->debugfs_root);
+ struct dentry *root = floor->dbg.dfs_dir;
+ struct docg3 *docg3 = floor->priv;
+
+ if (IS_ERR_OR_NULL(root))
+ return;
+
+ debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3,
+ &flashcontrol_fops);
+ debugfs_create_file("docg3_asic_mode", S_IRUSR, root, docg3,
+ &asic_mode_fops);
+ debugfs_create_file("docg3_device_id", S_IRUSR, root, docg3,
+ &device_id_fops);
+ debugfs_create_file("docg3_protection", S_IRUSR, root, docg3,
+ &protection_fops);
}
/**
@@ -2114,6 +2099,8 @@ static int __init docg3_probe(struct platform_device *pdev)
0);
if (ret)
goto err_probe;
+
+ doc_dbg_register(cascade->floors[floor]);
}
ret = doc_register_sysfs(pdev, cascade);
@@ -2121,7 +2108,6 @@ static int __init docg3_probe(struct platform_device *pdev)
goto err_probe;
platform_set_drvdata(pdev, cascade);
- doc_dbg_register(cascade->floors[0]->priv);
return 0;
notfound:
@@ -2148,7 +2134,6 @@ static int docg3_release(struct platform_device *pdev)
int floor;
doc_unregister_sysfs(pdev, cascade);
- doc_dbg_unregister(docg3);
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
if (cascade->floors[floor])
doc_release_device(cascade->floors[floor]);
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 19fb93f96a3a..e99946575398 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -299,7 +299,6 @@ struct docg3_cascade {
* @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC
* if 0, use all the 16 bytes.
* @oob_write_buf: prepared OOB for next page_write
- * @debugfs_root: debugfs root node
*/
struct docg3 {
struct device *dev;
@@ -312,7 +311,6 @@ struct docg3 {
loff_t oob_write_ofs;
int oob_autoecc;
u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE];
- struct dentry *debugfs_root;
};
#define doc_err(fmt, arg...) dev_err(docg3->dev, (fmt), ## arg)
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index dd5069876537..ddf478976013 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -775,6 +775,8 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev,
pdata->board_flash_info = devm_kzalloc(&pdev->dev,
sizeof(*pdata->board_flash_info),
GFP_KERNEL);
+ if (!pdata->board_flash_info)
+ return -ENOMEM;
/* Fill structs for each subnode (flash device) */
while ((pp = of_get_next_child(np, pp))) {
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 21afd94cd904..7bc29d725200 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -2073,15 +2073,17 @@ static int stfsm_probe(struct platform_device *pdev)
ret = stfsm_init(fsm);
if (ret) {
dev_err(&pdev->dev, "Failed to initialise FSM Controller\n");
- return ret;
+ goto err_clk_unprepare;
}
stfsm_fetch_platform_configs(pdev);
/* Detect SPI FLASH device */
info = stfsm_jedec_probe(fsm);
- if (!info)
- return -ENODEV;
+ if (!info) {
+ ret = -ENODEV;
+ goto err_clk_unprepare;
+ }
fsm->info = info;
/* Use device size to determine address width */
@@ -2095,11 +2097,11 @@ static int stfsm_probe(struct platform_device *pdev)
if (info->config) {
ret = info->config(fsm);
if (ret)
- return ret;
+ goto err_clk_unprepare;
} else {
ret = stfsm_prepare_rwe_seqs_default(fsm);
if (ret)
- return ret;
+ goto err_clk_unprepare;
}
fsm->mtd.name = info->name;
@@ -2124,6 +2126,10 @@ static int stfsm_probe(struct platform_device *pdev)
fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10));
return mtd_device_register(&fsm->mtd, NULL, 0);
+
+err_clk_unprepare:
+ clk_disable_unprepare(fsm->clk);
+ return ret;
}
static int stfsm_remove(struct platform_device *pdev)
@@ -2147,9 +2153,7 @@ static int stfsmfsm_resume(struct device *dev)
{
struct stfsm *fsm = dev_get_drvdata(dev);
- clk_prepare_enable(fsm->clk);
-
- return 0;
+ return clk_prepare_enable(fsm->clk);
}
#endif
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index 8db740d6eb08..57ef1fb42a04 100644
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -33,7 +33,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/inftl.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index f2b68667ea59..26de0a1d08cf 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -296,7 +296,7 @@ static void amd76xrom_remove_one(struct pci_dev *pdev)
amd76xrom_cleanup(window);
}
-static struct pci_device_id amd76xrom_pci_tbl[] = {
+static const struct pci_device_id amd76xrom_pci_tbl[] = {
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,
@@ -319,7 +319,7 @@ static struct pci_driver amd76xrom_driver = {
static int __init init_amd76xrom(void)
{
struct pci_dev *pdev;
- struct pci_device_id *id;
+ const struct pci_device_id *id;
pdev = NULL;
for(id = amd76xrom_pci_tbl; id->vendor; id++) {
pdev = pci_get_device(id->vendor, id->device, NULL);
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c
index 4f206a99164c..584962ec49f8 100644
--- a/drivers/mtd/maps/ck804xrom.c
+++ b/drivers/mtd/maps/ck804xrom.c
@@ -326,7 +326,7 @@ static void ck804xrom_remove_one(struct pci_dev *pdev)
ck804xrom_cleanup(window);
}
-static struct pci_device_id ck804xrom_pci_tbl[] = {
+static const struct pci_device_id ck804xrom_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0051), .driver_data = DEV_CK804 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0360), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0361), .driver_data = DEV_MCP55 },
@@ -353,7 +353,7 @@ static struct pci_driver ck804xrom_driver = {
static int __init init_ck804xrom(void)
{
struct pci_dev *pdev;
- struct pci_device_id *id;
+ const struct pci_device_id *id;
int retVal;
pdev = NULL;
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c
index 9646b0766ce0..da9f6d76ce1d 100644
--- a/drivers/mtd/maps/esb2rom.c
+++ b/drivers/mtd/maps/esb2rom.c
@@ -384,7 +384,7 @@ static void esb2rom_remove_one(struct pci_dev *pdev)
esb2rom_cleanup(window);
}
-static struct pci_device_id esb2rom_pci_tbl[] = {
+static const struct pci_device_id esb2rom_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
@@ -414,7 +414,7 @@ static struct pci_driver esb2rom_driver = {
static int __init init_esb2rom(void)
{
struct pci_dev *pdev;
- struct pci_device_id *id;
+ const struct pci_device_id *id;
int retVal;
pdev = NULL;
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
index 976d42f63aef..1888c5bf13f8 100644
--- a/drivers/mtd/maps/ichxrom.c
+++ b/drivers/mtd/maps/ichxrom.c
@@ -323,7 +323,7 @@ static void ichxrom_remove_one(struct pci_dev *pdev)
ichxrom_cleanup(window);
}
-static struct pci_device_id ichxrom_pci_tbl[] = {
+static const struct pci_device_id ichxrom_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
@@ -351,7 +351,7 @@ static struct pci_driver ichxrom_driver = {
static int __init init_ichxrom(void)
{
struct pci_dev *pdev;
- struct pci_device_id *id;
+ const struct pci_device_id *id;
pdev = NULL;
for (id = ichxrom_pci_tbl; id->vendor; id++) {
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
index 8bf79775e7c1..dd5d6855f543 100644
--- a/drivers/mtd/maps/intel_vr_nor.c
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -170,7 +170,7 @@ static int vr_nor_init_maps(struct vr_nor_mtd *p)
return err;
}
-static struct pci_device_id vr_nor_pci_ids[] = {
+static const struct pci_device_id vr_nor_pci_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
{0,}
};
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index eb0242e0b2d9..7b3bb40aff72 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -228,7 +228,7 @@ static struct mtd_pci_info intel_dc21285_info = {
* PCI device ID table
*/
-static struct pci_device_id mtd_pci_ids[] = {
+static const struct pci_device_id mtd_pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x530d,
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index 62fa6836f218..b1bd4faecfb2 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -178,8 +178,8 @@ static int of_flash_probe(struct platform_device *dev)
*/
p = of_get_property(dp, "reg", &count);
if (!p || count % reg_tuple_size != 0) {
- dev_err(&dev->dev, "Malformed reg property on %s\n",
- dev->dev.of_node->full_name);
+ dev_err(&dev->dev, "Malformed reg property on %pOF\n",
+ dev->dev.of_node);
err = -EINVAL;
goto err_flash_remove;
}
@@ -235,10 +235,10 @@ static int of_flash_probe(struct platform_device *dev)
err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
if (err)
- return err;
+ goto err_out;
err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
if (err)
- return err;
+ goto err_out;
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
index 05b286b5289f..4ed1a6bb4d3c 100644
--- a/drivers/mtd/maps/physmap_of_gemini.c
+++ b/drivers/mtd/maps/physmap_of_gemini.c
@@ -43,13 +43,6 @@
#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */
-/* Miscellaneous Control Register */
-#define GLOBAL_MISC_CTRL 0x30
-#define FLASH_PADS_MASK 0x07
-#define NAND_PADS_DISABLE BIT(2)
-#define PFLASH_PADS_DISABLE BIT(1)
-#define SFLASH_PADS_DISABLE BIT(0)
-
static const struct of_device_id syscon_match[] = {
{ .compatible = "cortina,gemini-syscon" },
{ },
@@ -102,15 +95,6 @@ int of_flash_probe_gemini(struct platform_device *pdev,
map->bankwidth * 8);
}
- /* Activate parallel (NOR flash) mode */
- ret = regmap_update_bits(rmap, GLOBAL_MISC_CTRL,
- FLASH_PADS_MASK,
- SFLASH_PADS_DISABLE | NAND_PADS_DISABLE);
- if (ret) {
- dev_err(dev, "unable to set up physmap pads\n");
- return -ENODEV;
- }
-
dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
return 0;
diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c
index 8c6ccded9be8..03f2b6e7bc7e 100644
--- a/drivers/mtd/maps/physmap_of_versatile.c
+++ b/drivers/mtd/maps/physmap_of_versatile.c
@@ -97,7 +97,7 @@ static const struct of_device_id ebi_match[] = {
static int ap_flash_init(struct platform_device *pdev)
{
struct device_node *ebi;
- static void __iomem *ebi_base;
+ void __iomem *ebi_base;
u32 val;
int ret;
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 414956eca0c9..1e73bba6e286 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -55,8 +55,8 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp)
/* Non-CFI userflash device-- once I find one we
* can work on supporting it.
*/
- printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n",
- dp->full_name, (unsigned long long)op->resource[0].start);
+ printk(KERN_ERR PFX "Unsupported device at %pOF, 0x%llx\n",
+ dp, (unsigned long long)op->resource[0].start);
return -ENODEV;
}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 956382cea256..e7ea842ba3db 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/reboot.h>
#include <linux/leds.h>
+#include <linux/debugfs.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@@ -339,7 +340,7 @@ static struct attribute *mtd_attrs[] = {
};
ATTRIBUTE_GROUPS(mtd);
-static struct device_type mtd_devtype = {
+static const struct device_type mtd_devtype = {
.name = "mtd",
.groups = mtd_groups,
.release = mtd_release,
@@ -477,6 +478,8 @@ int mtd_pairing_groups(struct mtd_info *mtd)
}
EXPORT_SYMBOL_GPL(mtd_pairing_groups);
+static struct dentry *dfs_dir_mtd;
+
/**
* add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
@@ -552,6 +555,14 @@ int add_mtd_device(struct mtd_info *mtd)
if (error)
goto fail_added;
+ if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
+ mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
+ if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
+ pr_debug("mtd device %s won't show data in debugfs\n",
+ dev_name(&mtd->dev));
+ }
+ }
+
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
"mtd%dro", i);
@@ -594,6 +605,8 @@ int del_mtd_device(struct mtd_info *mtd)
mutex_lock(&mtd_table_mutex);
+ debugfs_remove_recursive(mtd->dbg.dfs_dir);
+
if (idr_find(&mtd_idr, mtd->index) != mtd) {
ret = -ENODEV;
goto out_error;
@@ -1811,6 +1824,8 @@ static int __init init_mtd(void)
if (ret)
goto out_procfs;
+ dfs_dir_mtd = debugfs_create_dir("mtd", NULL);
+
return 0;
out_procfs:
@@ -1826,6 +1841,7 @@ err_reg:
static void __exit cleanup_mtd(void)
{
+ debugfs_remove_recursive(dfs_dir_mtd);
cleanup_mtdchar();
if (proc_mtd)
remove_proc_entry("mtd", NULL);
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index f12879a3d4ff..7d9080e33865 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -138,8 +138,6 @@ struct mtdswap_dev {
char *page_buf;
char *oob_buf;
-
- struct dentry *debugfs_root;
};
struct mtdswap_oobdata {
@@ -1315,29 +1313,19 @@ static const struct file_operations mtdswap_fops = {
static int mtdswap_add_debugfs(struct mtdswap_dev *d)
{
- struct gendisk *gd = d->mbd_dev->disk;
- struct device *dev = disk_to_dev(gd);
-
- struct dentry *root;
+ struct dentry *root = d->mtd->dbg.dfs_dir;
struct dentry *dent;
- root = debugfs_create_dir(gd->disk_name, NULL);
- if (IS_ERR(root))
+ if (!IS_ENABLED(CONFIG_DEBUG_FS))
return 0;
- if (!root) {
- dev_err(dev, "failed to initialize debugfs\n");
+ if (IS_ERR_OR_NULL(root))
return -1;
- }
-
- d->debugfs_root = root;
- dent = debugfs_create_file("stats", S_IRUSR, root, d,
+ dent = debugfs_create_file("mtdswap_stats", S_IRUSR, root, d,
&mtdswap_fops);
if (!dent) {
dev_err(d->dev, "debugfs_create_file failed\n");
- debugfs_remove_recursive(root);
- d->debugfs_root = NULL;
return -1;
}
@@ -1540,7 +1528,6 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
{
struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
- debugfs_remove_recursive(d->debugfs_root);
del_mtd_blktrans_dev(dev);
mtdswap_cleanup(d);
kfree(d);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index dbfa72d61d5a..3f2036f31da4 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -315,7 +315,7 @@ config MTD_NAND_ATMEL
config MTD_NAND_PXA3xx
tristate "NAND support on PXA3xx and Armada 370/XP"
- depends on PXA3xx || ARCH_MMP || PLAT_ORION
+ depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
help
This enables the driver for the NAND flash device found on
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 5d6c26f3cf7f..dcec9cf4983f 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -20,7 +20,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
index 1913ce18fb1c..f25eca79f4e5 100644
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ b/drivers/mtd/nand/atmel/nand-controller.c
@@ -59,7 +59,7 @@
#include <linux/mfd/syscon/atmel-matrix.h>
#include <linux/mfd/syscon/atmel-smc.h>
#include <linux/module.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -2091,8 +2091,8 @@ atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
}
nc->irq = of_irq_get(nand_np, 0);
- if (nc->irq < 0) {
- ret = nc->irq;
+ if (nc->irq <= 0) {
+ ret = nc->irq ?: -ENXIO;
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get IRQ number (err = %d)\n",
ret);
@@ -2183,11 +2183,12 @@ atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
nc->irq = of_irq_get(np, 0);
of_node_put(np);
- if (nc->irq < 0) {
- if (nc->irq != -EPROBE_DEFER)
+ if (nc->irq <= 0) {
+ ret = nc->irq ?: -ENXIO;
+ if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get IRQ number (err = %d)\n",
- nc->irq);
- return nc->irq;
+ ret);
+ return ret;
}
np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
index 8c210a5776bc..146af8218314 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/atmel/pmecc.c
@@ -47,7 +47,7 @@
#include <linux/genalloc.h>
#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 9bf6d9915694..9d4a28fa6b73 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -14,7 +14,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <asm/io.h>
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
index 8ea75710a854..c8834767ab6d 100644
--- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
+++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
@@ -6,7 +6,7 @@
#endif
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
struct bcm47xxnflash {
struct bcma_drv_cc *cc;
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 3962f55bd034..5655dca6ce43 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -49,7 +49,7 @@
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 7419c5ce63f8..e0eb51d8c012 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -29,7 +29,7 @@
#include <linux/bitops.h>
#include <linux/mm.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_platform.h>
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 2fd733eba0a3..bc558c438a57 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -13,7 +13,7 @@
#include <linux/device.h>
#undef DEBUG
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/rslib.h>
#include <linux/pci.h>
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
index 949b9400dcb7..1fc435f994e1 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -18,7 +18,7 @@
* CM-X270 board.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/gpio.h>
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 594b28684138..d48877540f14 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -24,7 +24,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 7b26e53b95b1..ccc8c43abcff 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -29,7 +29,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/of_device.h>
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 237cc706b0fb..9239e6793e6e 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -21,7 +21,7 @@
#define __DENALI_H__
#include <linux/bitops.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#define DEVICE_RESET 0x0
#define DEVICE_RESET__BANK(bank) BIT(bank)
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 47f398edf18f..56e2e177644d 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -118,7 +118,9 @@ static int denali_dt_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "no clk available\n");
return PTR_ERR(dt->clk);
}
- clk_prepare_enable(dt->clk);
+ ret = clk_prepare_enable(dt->clk);
+ if (ret)
+ return ret;
denali->clk_x_rate = clk_get_rate(dt->clk);
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index a023ab9e9cbf..c3aa53caab5c 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -27,7 +27,7 @@
#include <linux/io.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/doc2000.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/inftl.h>
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
index a27a84fbfb84..2436cbc71662 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/docg4.c
@@ -41,7 +41,7 @@
#include <linux/bitops.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/bch.h>
#include <linux/bitrev.h>
#include <linux/jiffies.h>
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index b9ac16f05057..17db2f90aa2c 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -34,7 +34,7 @@
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 59408ec2c69f..9e03bac7f34c 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -26,7 +26,7 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/fsl_ifc.h>
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index d85fa2555b68..a88e2cf66e0f 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index 9d8b051d3187..eac15d9bf49e 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -28,7 +28,7 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/platform_device.h>
#include <linux/of.h>
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
index 85294f150f4f..fd3648952b5a 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/gpio.c
@@ -26,7 +26,7 @@
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand-gpio.h>
#include <linux/of.h>
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 9df0ad64e7e0..a45e4ce13d10 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -17,7 +17,7 @@
#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
#define __DRIVERS_MTD_NAND_GPMI_NAND_H
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c
index 530caa80b1b6..d9ee1a7e6956 100644
--- a/drivers/mtd/nand/hisi504_nand.c
+++ b/drivers/mtd/nand/hisi504_nand.c
@@ -26,7 +26,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index 0d06a1f07d82..ad827d4af3e9 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/gpio.h>
diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c
index 8bc835f71b26..e69f6ae4c539 100644
--- a/drivers/mtd/nand/jz4780_nand.c
+++ b/drivers/mtd/nand/jz4780_nand.c
@@ -20,7 +20,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/jz4780-nemc.h>
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c
index 846a66c1b133..c3bb358ef01e 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/lpc32xx_mlc.c
@@ -27,7 +27,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -705,7 +705,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
res = -ENOENT;
goto err_exit1;
}
- clk_prepare_enable(host->clk);
+ res = clk_prepare_enable(host->clk);
+ if (res)
+ goto err_exit1;
nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
nand_chip->dev_ready = lpc32xx_nand_device_ready;
@@ -846,9 +848,12 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
static int lpc32xx_nand_resume(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
+ int ret;
/* Re-enable NAND clock */
- clk_prepare_enable(host->clk);
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ return ret;
/* Fresh init of NAND controller */
lpc32xx_nand_setup(host);
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c
index a0669a33f8fe..b61f28a1554d 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/lpc32xx_slc.c
@@ -23,7 +23,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -840,7 +840,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
res = -ENOENT;
goto err_exit1;
}
- clk_prepare_enable(host->clk);
+ res = clk_prepare_enable(host->clk);
+ if (res)
+ goto err_exit1;
/* Set NAND IO addresses and command/ready functions */
chip->IO_ADDR_R = SLC_DATA(host->io_base);
@@ -972,9 +974,12 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
static int lpc32xx_nand_resume(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
+ int ret;
/* Re-enable NAND clock */
- clk_prepare_enable(host->clk);
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ return ret;
/* Fresh init of NAND controller */
lpc32xx_nand_setup(host);
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 0e86fb6277c3..b6b97cc9fba6 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -33,7 +33,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c
index 6c3a4aab0b48..7f3b065b6b8f 100644
--- a/drivers/mtd/nand/mtk_ecc.c
+++ b/drivers/mtd/nand/mtk_ecc.c
@@ -464,8 +464,8 @@ static int mtk_ecc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_err(dev, "failed to get irq\n");
- return -EINVAL;
+ dev_err(dev, "failed to get irq: %d\n", irq);
+ return irq;
}
ret = dma_set_mask(dev, DMA_BIT_MASK(32));
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
index f7ae99464375..d86a7d131cc0 100644
--- a/drivers/mtd/nand/mtk_nand.c
+++ b/drivers/mtd/nand/mtk_nand.c
@@ -19,7 +19,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/clk.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/mtd.h>
#include <linux/module.h>
#include <linux/iopoll.h>
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index a764d5ca7536..53e5e0337c3e 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -22,7 +22,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/interrupt.h>
#include <linux/device.h>
@@ -876,6 +876,8 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
}
}
+#define MXC_V1_ECCBYTES 5
+
static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
@@ -885,7 +887,7 @@ static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
return -ERANGE;
oobregion->offset = (section * 16) + 6;
- oobregion->length = nand_chip->ecc.bytes;
+ oobregion->length = MXC_V1_ECCBYTES;
return 0;
}
@@ -907,8 +909,7 @@ static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
oobregion->length = 4;
}
} else {
- oobregion->offset = ((section - 1) * 16) +
- nand_chip->ecc.bytes + 6;
+ oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
if (section < nand_chip->ecc.steps)
oobregion->length = (section * 16) + 6 -
oobregion->offset;
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/nand_amd.c
index 170403a3bfa8..22f060f38123 100644
--- a/drivers/mtd/nand/nand_amd.c
+++ b/drivers/mtd/nand/nand_amd.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
static void amd_nand_decode_id(struct nand_chip *chip)
{
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c6c18b82f8f4..bcc8cef1c615 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -39,7 +39,7 @@
#include <linux/nmi.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/nand_bch.h>
#include <linux/interrupt.h>
@@ -1248,179 +1248,6 @@ int nand_reset(struct nand_chip *chip, int chipnr)
}
/**
- * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
- * @invert:
- * - when = 0, unlock the range of blocks within the lower and
- * upper boundary address
- * - when = 1, unlock the range of blocks outside the boundaries
- * of the lower and upper boundary address
- *
- * Returs unlock status.
- */
-static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
- uint64_t len, int invert)
-{
- int ret = 0;
- int status, page;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- /* Submit address of first page to unlock */
- page = ofs >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
-
- /* Submit address of last page to unlock */
- page = (ofs + len) >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1,
- (page | invert) & chip->pagemask);
-
- /* Call wait ready function */
- status = chip->waitfunc(mtd, chip);
- /* See if device thinks it succeeded */
- if (status & NAND_STATUS_FAIL) {
- pr_debug("%s: error status = 0x%08x\n",
- __func__, status);
- ret = -EIO;
- }
-
- return ret;
-}
-
-/**
- * nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
- *
- * Returns unlock status.
- */
-int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
- int ret = 0;
- int chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)ofs, len);
-
- if (check_offs_len(mtd, ofs, len))
- return -EINVAL;
-
- /* Align to last block address if size addresses end of the device */
- if (ofs + len == mtd->size)
- len -= mtd->erasesize;
-
- nand_get_device(mtd, FL_UNLOCKING);
-
- /* Shift to get chip number */
- chipnr = ofs >> chip->chip_shift;
-
- /*
- * Reset the chip.
- * If we want to check the WP through READ STATUS and check the bit 7
- * we must reset the chip
- * some operation can also clear the bit 7 of status register
- * eg. erase/program a locked block
- */
- nand_reset(chip, chipnr);
-
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- ret = -EIO;
- goto out;
- }
-
- ret = __nand_unlock(mtd, ofs, len, 0);
-
-out:
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
-
- return ret;
-}
-EXPORT_SYMBOL(nand_unlock);
-
-/**
- * nand_lock - [REPLACEABLE] locks all blocks present in the device
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
- *
- * This feature is not supported in many NAND parts. 'Micron' NAND parts do
- * have this feature, but it allows only to lock all blocks, not for specified
- * range for block. Implementing 'lock' feature by making use of 'unlock', for
- * now.
- *
- * Returns lock status.
- */
-int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
- int ret = 0;
- int chipnr, status, page;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)ofs, len);
-
- if (check_offs_len(mtd, ofs, len))
- return -EINVAL;
-
- nand_get_device(mtd, FL_LOCKING);
-
- /* Shift to get chip number */
- chipnr = ofs >> chip->chip_shift;
-
- /*
- * Reset the chip.
- * If we want to check the WP through READ STATUS and check the bit 7
- * we must reset the chip
- * some operation can also clear the bit 7 of status register
- * eg. erase/program a locked block
- */
- nand_reset(chip, chipnr);
-
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- status = MTD_ERASE_FAILED;
- ret = -EIO;
- goto out;
- }
-
- /* Submit address of first page to lock */
- page = ofs >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask);
-
- /* Call wait ready function */
- status = chip->waitfunc(mtd, chip);
- /* See if device thinks it succeeded */
- if (status & NAND_STATUS_FAIL) {
- pr_debug("%s: error status = 0x%08x\n",
- __func__, status);
- ret = -EIO;
- goto out;
- }
-
- ret = __nand_unlock(mtd, ofs, len, 0x1);
-
-out:
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
-
- return ret;
-}
-EXPORT_SYMBOL(nand_lock);
-
-/**
* nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
* @buf: buffer to test
* @len: buffer length
@@ -3993,10 +3820,13 @@ static void nand_manufacturer_detect(struct nand_chip *chip)
* nand_decode_ext_id() otherwise.
*/
if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
- chip->manufacturer.desc->ops->detect)
+ chip->manufacturer.desc->ops->detect) {
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
chip->manufacturer.desc->ops->detect(chip);
- else
+ } else {
nand_decode_ext_id(chip);
+ }
}
/*
@@ -4036,7 +3866,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
const struct nand_manufacturer *manufacturer;
struct mtd_info *mtd = nand_to_mtd(chip);
int busw;
- int i, ret;
+ int i;
u8 *id_data = chip->id.data;
u8 maf_id, dev_id;
@@ -4066,7 +3896,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
- for (i = 0; i < 8; i++)
+ for (i = 0; i < ARRAY_SIZE(chip->id.data); i++)
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != maf_id || id_data[1] != dev_id) {
@@ -4075,7 +3905,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
return -ENODEV;
}
- chip->id.len = nand_id_len(id_data, 8);
+ chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
/* Try to identify manufacturer */
manufacturer = nand_get_manufacturer(maf_id);
@@ -4177,10 +4007,6 @@ ident_done:
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
- ret = nand_manufacturer_init(chip);
- if (ret)
- return ret;
-
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
maf_id, dev_id);
@@ -4388,23 +4214,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
return ret;
}
- /* Initialize the ->data_interface field. */
- ret = nand_init_data_interface(chip);
- if (ret)
- goto err_nand_init;
-
- /*
- * Setup the data interface correctly on the chip and controller side.
- * This explicit call to nand_setup_data_interface() is only required
- * for the first die, because nand_reset() has been called before
- * ->data_interface and ->default_onfi_timing_mode were set.
- * For the other dies, nand_reset() will automatically switch to the
- * best mode for us.
- */
- ret = nand_setup_data_interface(chip, 0);
- if (ret)
- goto err_nand_init;
-
nand_maf_id = chip->id.data[0];
nand_dev_id = chip->id.data[1];
@@ -4434,12 +4243,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
mtd->size = i * chip->chipsize;
return 0;
-
-err_nand_init:
- /* Free manufacturer priv data. */
- nand_manufacturer_cleanup(chip);
-
- return ret;
}
EXPORT_SYMBOL(nand_scan_ident);
@@ -4826,55 +4629,60 @@ int nand_scan_tail(struct mtd_info *mtd)
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_buffers *nbuf = NULL;
- int ret;
+ int ret, i;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH))) {
- ret = -EINVAL;
- goto err_ident;
+ return -EINVAL;
}
if (invalid_ecc_page_accessors(chip)) {
pr_err("Invalid ECC page accessors setup\n");
- ret = -EINVAL;
- goto err_ident;
+ return -EINVAL;
}
if (!(chip->options & NAND_OWN_BUFFERS)) {
nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
- if (!nbuf) {
- ret = -ENOMEM;
- goto err_ident;
- }
+ if (!nbuf)
+ return -ENOMEM;
nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
if (!nbuf->ecccalc) {
ret = -ENOMEM;
- goto err_free;
+ goto err_free_nbuf;
}
nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
if (!nbuf->ecccode) {
ret = -ENOMEM;
- goto err_free;
+ goto err_free_nbuf;
}
nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
GFP_KERNEL);
if (!nbuf->databuf) {
ret = -ENOMEM;
- goto err_free;
+ goto err_free_nbuf;
}
chip->buffers = nbuf;
- } else {
- if (!chip->buffers) {
- ret = -ENOMEM;
- goto err_ident;
- }
+ } else if (!chip->buffers) {
+ return -ENOMEM;
}
+ /*
+ * FIXME: some NAND manufacturer drivers expect the first die to be
+ * selected when manufacturer->init() is called. They should be fixed
+ * to explictly select the relevant die when interacting with the NAND
+ * chip.
+ */
+ chip->select_chip(mtd, 0);
+ ret = nand_manufacturer_init(chip);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ goto err_free_nbuf;
+
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
@@ -4896,7 +4704,7 @@ int nand_scan_tail(struct mtd_info *mtd)
WARN(1, "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
}
@@ -4911,7 +4719,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
if (!ecc->read_page)
ecc->read_page = nand_read_page_hwecc_oob_first;
@@ -4943,7 +4751,7 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc->write_page == nand_write_page_hwecc)) {
WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
/* Use standard syndrome read/write page function? */
if (!ecc->read_page)
@@ -4963,7 +4771,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->strength) {
WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
break;
}
@@ -4976,7 +4784,7 @@ int nand_scan_tail(struct mtd_info *mtd)
ret = nand_set_ecc_soft_ops(mtd);
if (ret) {
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
break;
@@ -4984,7 +4792,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->read_page || !ecc->write_page) {
WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
if (!ecc->read_oob)
ecc->read_oob = nand_read_oob_std;
@@ -5008,7 +4816,7 @@ int nand_scan_tail(struct mtd_info *mtd)
default:
WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
/* For many systems, the standard OOB write also works for raw */
@@ -5029,13 +4837,13 @@ int nand_scan_tail(struct mtd_info *mtd)
if (ecc->steps * ecc->size != mtd->writesize) {
WARN(1, "Invalid ECC parameters\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
ecc->total = ecc->steps * ecc->bytes;
if (ecc->total > mtd->oobsize) {
WARN(1, "Total number of ECC bytes exceeded oobsize\n");
ret = -EINVAL;
- goto err_free;
+ goto err_nand_manuf_cleanup;
}
/*
@@ -5117,6 +4925,21 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!mtd->bitflip_threshold)
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+ /* Initialize the ->data_interface field. */
+ ret = nand_init_data_interface(chip);
+ if (ret)
+ goto err_nand_manuf_cleanup;
+
+ /* Enter fastest possible mode on all dies. */
+ for (i = 0; i < chip->numchips; i++) {
+ chip->select_chip(mtd, i);
+ ret = nand_setup_data_interface(chip, i);
+ chip->select_chip(mtd, -1);
+
+ if (ret)
+ goto err_nand_data_iface_cleanup;
+ }
+
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
@@ -5124,10 +4947,17 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Build bad block table */
ret = chip->scan_bbt(mtd);
if (ret)
- goto err_free;
+ goto err_nand_data_iface_cleanup;
+
return 0;
-err_free:
+err_nand_data_iface_cleanup:
+ nand_release_data_interface(chip);
+
+err_nand_manuf_cleanup:
+ nand_manufacturer_cleanup(chip);
+
+err_free_nbuf:
if (nbuf) {
kfree(nbuf->databuf);
kfree(nbuf->ecccode);
@@ -5135,12 +4965,6 @@ err_free:
kfree(nbuf);
}
-err_ident:
- /* Clean up nand_scan_ident(). */
-
- /* Free manufacturer priv data. */
- nand_manufacturer_cleanup(chip);
-
return ret;
}
EXPORT_SYMBOL(nand_scan_tail);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 7695efea65f2..2915b6739bf8 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -61,7 +61,7 @@
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/bbm.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index 44763f87eae4..505441c9373b 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_bch.h>
#include <linux/bch.h>
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index d1770b066396..7613a0388044 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -43,7 +43,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <asm/byteorder.h>
#else
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c
index b12dc7325378..985751eda317 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/nand_hynix.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/sizes.h>
#include <linux/slab.h>
@@ -477,7 +477,7 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
* The ECC requirements field meaning depends on the
* NAND technology.
*/
- u8 nand_tech = chip->id.data[5] & 0x3;
+ u8 nand_tech = chip->id.data[5] & 0x7;
if (nand_tech < 3) {
/* > 26nm, reference: H27UBG8T2A datasheet */
@@ -533,7 +533,7 @@ static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
if (nand_tech > 0)
chip->options |= NAND_NEED_SCRAMBLING;
} else {
- nand_tech = chip->id.data[5] & 0x3;
+ nand_tech = chip->id.data[5] & 0x7;
/* < 32nm */
if (nand_tech > 2)
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 92e2cf8e9ff9..5423c3bb388e 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -6,7 +6,7 @@
* published by the Free Software Foundation.
*
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/sizes.h>
#define LP_OPTIONS 0
diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/nand_macronix.c
index 84855c3e1a02..d290ff2a6d2f 100644
--- a/drivers/mtd/nand/nand_macronix.c
+++ b/drivers/mtd/nand/nand_macronix.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
static int macronix_nand_init(struct nand_chip *chip)
{
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c
index c30ab60f8e1b..abf6a3c376e8 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/nand_micron.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
/*
* Special Micron status bit that indicates when the block has been
diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c
index 1e0755997762..d348f0129ae7 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/nand_samsung.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
static void samsung_nand_decode_id(struct nand_chip *chip)
{
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
index 7e36d7d13c26..5d1533bcc5bd 100644
--- a/drivers/mtd/nand/nand_timings.c
+++ b/drivers/mtd/nand/nand_timings.c
@@ -11,7 +11,7 @@
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/export.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
static const struct nand_data_interface onfi_sdr_timings[] = {
/* Mode 0 */
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
index fa787ba38dcd..57df857074e6 100644
--- a/drivers/mtd/nand/nand_toshiba.c
+++ b/drivers/mtd/nand/nand_toshiba.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
static void toshiba_nand_decode_id(struct nand_chip *chip)
{
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index e4211c3cc49b..fec613221958 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -33,7 +33,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_bch.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
@@ -287,11 +287,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
#define NS_MAX_HELD_PAGES 16
-struct nandsim_debug_info {
- struct dentry *dfs_root;
- struct dentry *dfs_wear_report;
-};
-
/*
* A union to represent flash memory contents and flash buffer.
*/
@@ -370,8 +365,6 @@ struct nandsim {
void *file_buf;
struct page *held_pages[NS_MAX_HELD_PAGES];
int held_cnt;
-
- struct nandsim_debug_info dbg;
};
/*
@@ -524,39 +517,23 @@ static const struct file_operations dfs_fops = {
*/
static int nandsim_debugfs_create(struct nandsim *dev)
{
- struct nandsim_debug_info *dbg = &dev->dbg;
+ struct dentry *root = nsmtd->dbg.dfs_dir;
struct dentry *dent;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return 0;
- dent = debugfs_create_dir("nandsim", NULL);
- if (!dent) {
- NS_ERR("cannot create \"nandsim\" debugfs directory\n");
- return -ENODEV;
- }
- dbg->dfs_root = dent;
+ if (IS_ERR_OR_NULL(root))
+ return -1;
- dent = debugfs_create_file("wear_report", S_IRUSR,
- dbg->dfs_root, dev, &dfs_fops);
- if (!dent)
- goto out_remove;
- dbg->dfs_wear_report = dent;
+ dent = debugfs_create_file("nandsim_wear_report", S_IRUSR,
+ root, dev, &dfs_fops);
+ if (IS_ERR_OR_NULL(dent)) {
+ NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n");
+ return -1;
+ }
return 0;
-
-out_remove:
- debugfs_remove_recursive(dbg->dfs_root);
- return -ENODEV;
-}
-
-/**
- * nandsim_debugfs_remove - destroy all debugfs files
- */
-static void nandsim_debugfs_remove(struct nandsim *ns)
-{
- if (IS_ENABLED(CONFIG_DEBUG_FS))
- debugfs_remove_recursive(ns->dbg.dfs_root);
}
/*
@@ -2352,9 +2329,6 @@ static int __init ns_init_module(void)
if ((retval = setup_wear_reporting(nsmtd)) != 0)
goto err_exit;
- if ((retval = nandsim_debugfs_create(nand)) != 0)
- goto err_exit;
-
if ((retval = init_nandsim(nsmtd)) != 0)
goto err_exit;
@@ -2370,10 +2344,12 @@ static int __init ns_init_module(void)
if (retval != 0)
goto err_exit;
+ if ((retval = nandsim_debugfs_create(nand)) != 0)
+ goto err_exit;
+
return 0;
err_exit:
- nandsim_debugfs_remove(nand);
free_nandsim(nand);
nand_release(nsmtd);
for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
@@ -2396,7 +2372,6 @@ static void __exit ns_cleanup_module(void)
struct nandsim *ns = nand_get_controller_data(chip);
int i;
- nandsim_debugfs_remove(ns);
free_nandsim(ns); /* Free nandsim private resources */
nand_release(nsmtd); /* Unregister driver */
for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 28e6118362f7..d8a806894937 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -22,7 +22,7 @@
*
*/
#include <linux/module.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/ndfc.h>
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c
index 8f64011d32ef..7bb4d2ea9342 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/nuc900_nand.c
@@ -19,7 +19,7 @@
#include <linux/err.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#define REG_FMICSR 0x00
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 084934a9f19c..54540c8fa1a2 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -18,7 +18,7 @@
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/omap-dma.h>
#include <linux/io.h>
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 209170ed2b76..5a5aa1f07d07 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -15,7 +15,7 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -54,13 +54,16 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *io_base = chip->IO_ADDR_R;
+#if __LINUX_ARM_ARCH__ >= 5
uint64_t *buf64;
+#endif
int i = 0;
while (len && (unsigned long)buf & 7) {
*buf++ = readb(io_base);
len--;
}
+#if __LINUX_ARM_ARCH__ >= 5
buf64 = (uint64_t *)buf;
while (i < len/8) {
/*
@@ -74,6 +77,10 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
buf64[i++] = x;
}
i *= 8;
+#else
+ readsl(io_base, buf, len/4);
+ i = len / 4 * 4;
+#endif
while (i < len)
buf[i++] = readb(io_base);
}
diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/oxnas_nand.c
index 1b207aac840c..d649d5944826 100644
--- a/drivers/mtd/nand/oxnas_nand.c
+++ b/drivers/mtd/nand/oxnas_nand.c
@@ -21,7 +21,7 @@
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
@@ -112,14 +112,19 @@ static int oxnas_nand_probe(struct platform_device *pdev)
if (count > 1)
return -EINVAL;
- clk_prepare_enable(oxnas->clk);
+ err = clk_prepare_enable(oxnas->clk);
+ if (err)
+ return err;
+
device_reset_optional(&pdev->dev);
for_each_child_of_node(np, nand_np) {
chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
+ if (!chip) {
+ err = -ENOMEM;
+ goto err_clk_unprepare;
+ }
chip->controller = &oxnas->base;
@@ -139,12 +144,12 @@ static int oxnas_nand_probe(struct platform_device *pdev)
/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
- return err;
+ goto err_clk_unprepare;
err = mtd_device_register(mtd, NULL, 0);
if (err) {
nand_release(mtd);
- return err;
+ goto err_clk_unprepare;
}
oxnas->chips[nchips] = chip;
@@ -152,12 +157,18 @@ static int oxnas_nand_probe(struct platform_device *pdev)
}
/* Exit if no chips found */
- if (!nchips)
- return -ENODEV;
+ if (!nchips) {
+ err = -ENODEV;
+ goto err_clk_unprepare;
+ }
platform_set_drvdata(pdev, oxnas);
return 0;
+
+err_clk_unprepare:
+ clk_disable_unprepare(oxnas->clk);
+ return err;
}
static int oxnas_nand_remove(struct platform_device *pdev)
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index 074b8b01289e..a47a7e4bd25a 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 791de3e4bbb6..925a1323604d 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -15,7 +15,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
struct plat_nand_data {
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 74dae4bbdac8..85cff68643e0 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -21,7 +21,7 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/iopoll.h>
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 88af7145a51a..3baddfc997d1 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -17,7 +17,7 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -53,6 +53,8 @@
#define NAND_VERSION 0xf08
#define NAND_READ_LOCATION_0 0xf20
#define NAND_READ_LOCATION_1 0xf24
+#define NAND_READ_LOCATION_2 0xf28
+#define NAND_READ_LOCATION_3 0xf2c
/* dummy register offsets, used by write_reg_dma */
#define NAND_DEV_CMD1_RESTORE 0xdead
@@ -109,7 +111,11 @@
#define READ_ADDR 0
/* NAND_DEV_CMD_VLD bits */
-#define READ_START_VLD 0
+#define READ_START_VLD BIT(0)
+#define READ_STOP_VLD BIT(1)
+#define WRITE_START_VLD BIT(2)
+#define ERASE_START_VLD BIT(3)
+#define SEQ_READ_START_VLD BIT(4)
/* NAND_EBI2_ECC_BUF_CFG bits */
#define NUM_STEPS 0
@@ -131,6 +137,11 @@
#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
+/* NAND_READ_LOCATION_n bits */
+#define READ_LOCATION_OFFSET 0
+#define READ_LOCATION_SIZE 16
+#define READ_LOCATION_LAST 31
+
/* Version Mask */
#define NAND_VERSION_MAJOR_MASK 0xf0000000
#define NAND_VERSION_MAJOR_SHIFT 28
@@ -148,6 +159,13 @@
#define FETCH_ID 0xb
#define RESET_DEVICE 0xd
+/* Default Value for NAND_DEV_CMD_VLD */
+#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
+ ERASE_START_VLD | SEQ_READ_START_VLD)
+
+/* NAND_CTRL bits */
+#define BAM_MODE_EN BIT(0)
+
/*
* the NAND controller performs reads/writes with ECC in 516 byte chunks.
* the driver calls the chunks 'step' or 'codeword' interchangeably
@@ -169,11 +187,81 @@
#define ECC_BCH_4BIT BIT(2)
#define ECC_BCH_8BIT BIT(3)
+#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \
+nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
+ ((offset) << READ_LOCATION_OFFSET) | \
+ ((size) << READ_LOCATION_SIZE) | \
+ ((is_last) << READ_LOCATION_LAST))
+
+/*
+ * Returns the actual register address for all NAND_DEV_ registers
+ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
+ */
+#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
+
+#define QPIC_PER_CW_CMD_SGL 32
+#define QPIC_PER_CW_DATA_SGL 8
+
+/*
+ * Flags used in DMA descriptor preparation helper functions
+ * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
+ */
+/* Don't set the EOT in current tx BAM sgl */
+#define NAND_BAM_NO_EOT BIT(0)
+/* Set the NWD flag in current BAM sgl */
+#define NAND_BAM_NWD BIT(1)
+/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
+#define NAND_BAM_NEXT_SGL BIT(2)
+/*
+ * Erased codeword status is being used two times in single transfer so this
+ * flag will determine the current value of erased codeword status register
+ */
+#define NAND_ERASED_CW_SET BIT(4)
+
+/*
+ * This data type corresponds to the BAM transaction which will be used for all
+ * NAND transfers.
+ * @cmd_sgl - sgl for NAND BAM command pipe
+ * @data_sgl - sgl for NAND BAM consumer/producer pipe
+ * @cmd_sgl_pos - current index in command sgl.
+ * @cmd_sgl_start - start index in command sgl.
+ * @tx_sgl_pos - current index in data sgl for tx.
+ * @tx_sgl_start - start index in data sgl for tx.
+ * @rx_sgl_pos - current index in data sgl for rx.
+ * @rx_sgl_start - start index in data sgl for rx.
+ */
+struct bam_transaction {
+ struct scatterlist *cmd_sgl;
+ struct scatterlist *data_sgl;
+ u32 cmd_sgl_pos;
+ u32 cmd_sgl_start;
+ u32 tx_sgl_pos;
+ u32 tx_sgl_start;
+ u32 rx_sgl_pos;
+ u32 rx_sgl_start;
+};
+
+/*
+ * This data type corresponds to the nand dma descriptor
+ * @list - list for desc_info
+ * @dir - DMA transfer direction
+ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
+ * ADM
+ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
+ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
+ * @dma_desc - low level DMA engine descriptor
+ */
struct desc_info {
struct list_head node;
enum dma_data_direction dir;
- struct scatterlist sgl;
+ union {
+ struct scatterlist adm_sgl;
+ struct {
+ struct scatterlist *bam_sgl;
+ int sgl_cnt;
+ };
+ };
struct dma_async_tx_descriptor *dma_desc;
};
@@ -202,6 +290,13 @@ struct nandc_regs {
__le32 orig_vld;
__le32 ecc_buf_cfg;
+ __le32 read_location0;
+ __le32 read_location1;
+ __le32 read_location2;
+ __le32 read_location3;
+
+ __le32 erased_cw_detect_cfg_clr;
+ __le32 erased_cw_detect_cfg_set;
};
/*
@@ -226,14 +321,17 @@ struct nandc_regs {
* by upper layers directly
* @buf_size/count/start: markers for chip->read_buf/write_buf functions
* @reg_read_buf: local buffer for reading back registers via DMA
+ * @reg_read_dma: contains dma address for register read buffer
* @reg_read_pos: marker for data read in reg_read_buf
*
* @regs: a contiguous chunk of memory for DMA register
* writes. contains the register values to be
* written to controller
* @cmd1/vld: some fixed controller register values
- * @ecc_modes: supported ECC modes by the current controller,
+ * @props: properties of current NAND controller,
* initialized via DT match data
+ * @max_cwperpage: maximum QPIC codewords required. calculated
+ * from all connected NAND devices pagesize
*/
struct qcom_nand_controller {
struct nand_hw_control controller;
@@ -247,23 +345,39 @@ struct qcom_nand_controller {
struct clk *core_clk;
struct clk *aon_clk;
- struct dma_chan *chan;
- unsigned int cmd_crci;
- unsigned int data_crci;
+ union {
+ /* will be used only by QPIC for BAM DMA */
+ struct {
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+ struct dma_chan *cmd_chan;
+ };
+
+ /* will be used only by EBI2 for ADM DMA */
+ struct {
+ struct dma_chan *chan;
+ unsigned int cmd_crci;
+ unsigned int data_crci;
+ };
+ };
+
struct list_head desc_list;
+ struct bam_transaction *bam_txn;
u8 *data_buffer;
int buf_size;
int buf_count;
int buf_start;
+ unsigned int max_cwperpage;
__le32 *reg_read_buf;
+ dma_addr_t reg_read_dma;
int reg_read_pos;
struct nandc_regs *regs;
u32 cmd1, vld;
- u32 ecc_modes;
+ const struct qcom_nandc_props *props;
};
/*
@@ -316,6 +430,78 @@ struct qcom_nand_host {
u32 clrreadstatus;
};
+/*
+ * This data type corresponds to the NAND controller properties which varies
+ * among different NAND controllers.
+ * @ecc_modes - ecc mode for NAND
+ * @is_bam - whether NAND controller is using BAM
+ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
+ */
+struct qcom_nandc_props {
+ u32 ecc_modes;
+ bool is_bam;
+ u32 dev_cmd_reg_start;
+};
+
+/* Frees the BAM transaction memory */
+static void free_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ devm_kfree(nandc->dev, bam_txn);
+}
+
+/* Allocates and Initializes the BAM transaction */
+static struct bam_transaction *
+alloc_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn;
+ size_t bam_txn_size;
+ unsigned int num_cw = nandc->max_cwperpage;
+ void *bam_txn_buf;
+
+ bam_txn_size =
+ sizeof(*bam_txn) + num_cw *
+ ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
+ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
+
+ bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
+ if (!bam_txn_buf)
+ return NULL;
+
+ bam_txn = bam_txn_buf;
+ bam_txn_buf += sizeof(*bam_txn);
+
+ bam_txn->cmd_sgl = bam_txn_buf;
+ bam_txn_buf +=
+ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
+
+ bam_txn->data_sgl = bam_txn_buf;
+
+ return bam_txn;
+}
+
+/* Clears the BAM transaction indexes */
+static void clear_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (!nandc->props->is_bam)
+ return;
+
+ bam_txn->cmd_sgl_pos = 0;
+ bam_txn->cmd_sgl_start = 0;
+ bam_txn->tx_sgl_pos = 0;
+ bam_txn->tx_sgl_start = 0;
+ bam_txn->rx_sgl_pos = 0;
+ bam_txn->rx_sgl_start = 0;
+
+ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_CMD_SGL);
+ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_DATA_SGL);
+}
+
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
{
return container_of(chip, struct qcom_nand_host, chip);
@@ -339,6 +525,24 @@ static inline void nandc_write(struct qcom_nand_controller *nandc, int offset,
iowrite32(val, nandc->base + offset);
}
+static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
+ bool is_cpu)
+{
+ if (!nandc->props->is_bam)
+ return;
+
+ if (is_cpu)
+ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ else
+ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+}
+
static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
{
switch (offset) {
@@ -372,6 +576,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
return &regs->orig_vld;
case NAND_EBI2_ECC_BUF_CFG:
return &regs->ecc_buf_cfg;
+ case NAND_READ_LOCATION_0:
+ return &regs->read_location0;
+ case NAND_READ_LOCATION_1:
+ return &regs->read_location1;
+ case NAND_READ_LOCATION_2:
+ return &regs->read_location2;
+ case NAND_READ_LOCATION_3:
+ return &regs->read_location3;
default:
return NULL;
}
@@ -446,11 +658,119 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ if (read)
+ nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
+ host->cw_data : host->cw_size, 1);
+}
+
+/*
+ * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
+ * for BAM. This descriptor will be added in the NAND DMA descriptor queue
+ * which will be submitted to DMA engine.
+ */
+static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+ struct dma_chan *chan,
+ unsigned long flags)
+{
+ struct desc_info *desc;
+ struct scatterlist *sgl;
+ unsigned int sgl_cnt;
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ enum dma_transfer_direction dir_eng;
+ struct dma_async_tx_descriptor *dma_desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ if (chan == nandc->cmd_chan) {
+ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
+ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
+ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else if (chan == nandc->tx_chan) {
+ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
+ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
+ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else {
+ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
+ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
+ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
+ dir_eng = DMA_DEV_TO_MEM;
+ desc->dir = DMA_FROM_DEVICE;
+ }
+
+ sg_mark_end(sgl + sgl_cnt - 1);
+ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ if (ret == 0) {
+ dev_err(nandc->dev, "failure in mapping desc\n");
+ kfree(desc);
+ return -ENOMEM;
+ }
+
+ desc->sgl_cnt = sgl_cnt;
+ desc->bam_sgl = sgl;
+
+ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
+ flags);
+
+ if (!dma_desc) {
+ dev_err(nandc->dev, "failure in prep desc\n");
+ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ kfree(desc);
+ return -EINVAL;
+ }
+
+ desc->dma_desc = dma_desc;
+
+ list_add_tail(&desc->node, &nandc->desc_list);
+
+ return 0;
+}
+
+/*
+ * Prepares the data descriptor for BAM DMA which will be used for NAND
+ * data reads and writes.
+ */
+static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+ const void *vaddr,
+ int size, unsigned int flags)
+{
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (read) {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
+ vaddr, size);
+ bam_txn->rx_sgl_pos++;
+ } else {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
+ vaddr, size);
+ bam_txn->tx_sgl_pos++;
+
+ /*
+ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
+ * is not set, form the DMA descriptor
+ */
+ if (!(flags & NAND_BAM_NO_EOT)) {
+ ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
}
-static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr, int size,
- bool flow_control)
+static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr, int size,
+ bool flow_control)
{
struct desc_info *desc;
struct dma_async_tx_descriptor *dma_desc;
@@ -463,7 +783,7 @@ static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read,
if (!desc)
return -ENOMEM;
- sgl = &desc->sgl;
+ sgl = &desc->adm_sgl;
sg_init_one(sgl, vaddr, size);
@@ -524,9 +844,10 @@ err:
*
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to read
+ * @flags: flags to control DMA descriptor preparation
*/
static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs)
+ int num_regs, unsigned int flags)
{
bool flow_control = false;
void *vaddr;
@@ -535,11 +856,14 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
flow_control = true;
+ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, first);
+
size = num_regs * sizeof(u32);
vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
nandc->reg_read_pos += num_regs;
- return prep_dma_desc(nandc, true, first, vaddr, size, flow_control);
+ return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control);
}
/*
@@ -548,9 +872,10 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
*
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to write
+ * @flags: flags to control DMA descriptor preparation
*/
static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs)
+ int num_regs, unsigned int flags)
{
bool flow_control = false;
struct nandc_regs *regs = nandc->regs;
@@ -562,15 +887,26 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_FLASH_CMD)
flow_control = true;
- if (first == NAND_DEV_CMD1_RESTORE)
- first = NAND_DEV_CMD1;
+ if (first == NAND_ERASED_CW_DETECT_CFG) {
+ if (flags & NAND_ERASED_CW_SET)
+ vaddr = &regs->erased_cw_detect_cfg_set;
+ else
+ vaddr = &regs->erased_cw_detect_cfg_clr;
+ }
+
+ if (first == NAND_EXEC_CMD)
+ flags |= NAND_BAM_NWD;
+
+ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
- if (first == NAND_DEV_CMD_VLD_RESTORE)
- first = NAND_DEV_CMD_VLD;
+ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
size = num_regs * sizeof(u32);
- return prep_dma_desc(nandc, false, first, vaddr, size, flow_control);
+ return prep_adm_dma_desc(nandc, false, first, vaddr, size,
+ flow_control);
}
/*
@@ -580,11 +916,15 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
* @reg_off: offset within the controller's data buffer
* @vaddr: virtual address of the buffer we want to write to
* @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
*/
static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size)
+ const u8 *vaddr, int size, unsigned int flags)
{
- return prep_dma_desc(nandc, true, reg_off, vaddr, size, false);
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+
+ return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
}
/*
@@ -594,48 +934,84 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
* @reg_off: offset within the controller's data buffer
* @vaddr: virtual address of the buffer we want to read from
* @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
*/
static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size)
+ const u8 *vaddr, int size, unsigned int flags)
{
- return prep_dma_desc(nandc, false, reg_off, vaddr, size, false);
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+
+ return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
}
/*
- * helper to prepare dma descriptors to configure registers needed for reading a
- * codeword/step in a page
+ * Helper to prepare DMA descriptors for configuring registers
+ * before reading a NAND page.
*/
-static void config_cw_read(struct qcom_nand_controller *nandc)
+static void config_nand_page_read(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_FLASH_CMD, 3);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1);
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
+ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+}
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+/*
+ * Helper to prepare DMA descriptors for configuring registers
+ * before reading each codeword in NAND page.
+ */
+static void config_nand_cw_read(struct qcom_nand_controller *nandc)
+{
+ if (nandc->props->is_bam)
+ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
+ NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 2);
- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
}
/*
- * helpers to prepare dma descriptors used to configure registers needed for
- * writing a codeword/step in a page
+ * Helper to prepare dma descriptors to configure registers needed for reading a
+ * single codeword in page
*/
-static void config_cw_write_pre(struct qcom_nand_controller *nandc)
+static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_FLASH_CMD, 3);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1);
+ config_nand_page_read(nandc);
+ config_nand_cw_read(nandc);
}
-static void config_cw_write_post(struct qcom_nand_controller *nandc)
+/*
+ * Helper to prepare DMA descriptors used to configure registers needed for
+ * before writing a NAND page.
+ */
+static void config_nand_page_write(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
+ NAND_BAM_NEXT_SGL);
+}
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+/*
+ * Helper to prepare DMA descriptors for configuring registers
+ * before writing each codeword in NAND page.
+ */
+static void config_nand_cw_write(struct qcom_nand_controller *nandc)
+{
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_FLASH_STATUS, 1);
- write_reg_dma(nandc, NAND_READ_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
}
/*
@@ -672,8 +1048,7 @@ static int nandc_param(struct qcom_nand_host *host)
/* configure CMD1 and VLD for ONFI param probing */
nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
- (nandc->vld & ~(1 << READ_START_VLD))
- | 0 << READ_START_VLD);
+ (nandc->vld & ~READ_START_VLD));
nandc_set_reg(nandc, NAND_DEV_CMD1,
(nandc->cmd1 & ~(0xFF << READ_ADDR))
| NAND_CMD_PARAM << READ_ADDR);
@@ -682,21 +1057,22 @@ static int nandc_param(struct qcom_nand_host *host)
nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
+ nandc_set_read_loc(nandc, 0, 0, 512, 1);
- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1);
- write_reg_dma(nandc, NAND_DEV_CMD1, 1);
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
nandc->buf_count = 512;
memset(nandc->data_buffer, 0xff, nandc->buf_count);
- config_cw_read(nandc);
+ config_nand_single_cw_page_read(nandc);
read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- nandc->buf_count);
+ nandc->buf_count, 0);
/* restore CMD1 and VLD regs */
- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1);
- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1);
+ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
return 0;
}
@@ -718,14 +1094,14 @@ static int erase_block(struct qcom_nand_host *host, int page_addr)
nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
- write_reg_dma(nandc, NAND_FLASH_CMD, 3);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 2);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_FLASH_STATUS, 1);
- write_reg_dma(nandc, NAND_READ_STATUS, 1);
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
return 0;
}
@@ -742,13 +1118,14 @@ static int read_id(struct qcom_nand_host *host, int column)
nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
nandc_set_reg(nandc, NAND_ADDR0, column);
nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
+ nandc->props->is_bam ? 0 : DM_EN);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
- write_reg_dma(nandc, NAND_FLASH_CMD, 4);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_READ_ID, 1);
+ read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
return 0;
}
@@ -762,10 +1139,10 @@ static int reset(struct qcom_nand_host *host)
nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
- write_reg_dma(nandc, NAND_FLASH_CMD, 1);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
return 0;
}
@@ -775,12 +1152,43 @@ static int submit_descs(struct qcom_nand_controller *nandc)
{
struct desc_info *desc;
dma_cookie_t cookie = 0;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ int r;
+
+ if (nandc->props->is_bam) {
+ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0);
+ if (r)
+ return r;
+ }
+ }
list_for_each_entry(desc, &nandc->desc_list, node)
cookie = dmaengine_submit(desc->dma_desc);
- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
- return -ETIMEDOUT;
+ if (nandc->props->is_bam) {
+ dma_async_issue_pending(nandc->tx_chan);
+ dma_async_issue_pending(nandc->rx_chan);
+
+ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ } else {
+ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ }
return 0;
}
@@ -791,7 +1199,14 @@ static void free_descs(struct qcom_nand_controller *nandc)
list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
list_del(&desc->node);
- dma_unmap_sg(nandc->dev, &desc->sgl, 1, desc->dir);
+
+ if (nandc->props->is_bam)
+ dma_unmap_sg(nandc->dev, desc->bam_sgl,
+ desc->sgl_cnt, desc->dir);
+ else
+ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
+ desc->dir);
+
kfree(desc);
}
}
@@ -800,8 +1215,7 @@ static void free_descs(struct qcom_nand_controller *nandc)
static void clear_read_regs(struct qcom_nand_controller *nandc)
{
nandc->reg_read_pos = 0;
- memset(nandc->reg_read_buf, 0,
- MAX_REG_RD * sizeof(*nandc->reg_read_buf));
+ nandc_read_buffer_sync(nandc, false);
}
static void pre_command(struct qcom_nand_host *host, int command)
@@ -815,6 +1229,10 @@ static void pre_command(struct qcom_nand_host *host, int command)
host->last_command = command;
clear_read_regs(nandc);
+
+ if (command == NAND_CMD_RESET || command == NAND_CMD_READID ||
+ command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1)
+ clear_bam_transaction(nandc);
}
/*
@@ -831,6 +1249,7 @@ static void parse_erase_write_errors(struct qcom_nand_host *host, int command)
int i;
num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
+ nandc_read_buffer_sync(nandc, true);
for (i = 0; i < num_cw; i++) {
u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -852,6 +1271,7 @@ static void post_command(struct qcom_nand_host *host, int command)
switch (command) {
case NAND_CMD_READID:
+ nandc_read_buffer_sync(nandc, true);
memcpy(nandc->data_buffer, nandc->reg_read_buf,
nandc->buf_count);
break;
@@ -1015,6 +1435,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
int i;
buf = (struct read_stats *)nandc->reg_read_buf;
+ nandc_read_buffer_sync(nandc, true);
for (i = 0; i < ecc->steps; i++, buf++) {
u32 flash, buffer, erased_cw;
@@ -1102,6 +1523,8 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
struct nand_ecc_ctrl *ecc = &chip->ecc;
int i, ret;
+ config_nand_page_read(nandc);
+
/* queue cmd descs for each codeword */
for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size;
@@ -1115,11 +1538,24 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
oob_size = host->ecc_bytes_hw + host->spare_bytes;
}
- config_cw_read(nandc);
+ if (nandc->props->is_bam) {
+ if (data_buf && oob_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 0);
+ nandc_set_read_loc(nandc, 1, data_size,
+ oob_size, 1);
+ } else if (data_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 1);
+ } else {
+ nandc_set_read_loc(nandc, 0, data_size,
+ oob_size, 1);
+ }
+ }
+
+ config_nand_cw_read(nandc);
if (data_buf)
read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
- data_size);
+ data_size, 0);
/*
* when ecc is enabled, the controller doesn't read the real
@@ -1135,7 +1571,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
*oob_buf++ = 0xff;
read_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size);
+ oob_buf, oob_size, 0);
}
if (data_buf)
@@ -1175,9 +1611,9 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, true);
- config_cw_read(nandc);
+ config_nand_single_cw_page_read(nandc);
- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size);
+ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
ret = submit_descs(nandc);
if (ret)
@@ -1200,6 +1636,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
data_buf = buf;
oob_buf = oob_required ? chip->oob_poi : NULL;
+ clear_bam_transaction(nandc);
ret = read_page_ecc(host, data_buf, oob_buf);
if (ret) {
dev_err(nandc->dev, "failure to read page\n");
@@ -1219,12 +1656,16 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
u8 *data_buf, *oob_buf;
struct nand_ecc_ctrl *ecc = &chip->ecc;
int i, ret;
+ int read_loc;
data_buf = buf;
oob_buf = chip->oob_poi;
host->use_ecc = false;
+
+ clear_bam_transaction(nandc);
update_rw_regs(host, ecc->steps, true);
+ config_nand_page_read(nandc);
for (i = 0; i < ecc->steps; i++) {
int data_size1, data_size2, oob_size1, oob_size2;
@@ -1243,21 +1684,35 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}
- config_cw_read(nandc);
+ if (nandc->props->is_bam) {
+ read_loc = 0;
+ nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
+ read_loc += data_size1;
+
+ nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
+ read_loc += oob_size1;
+
+ nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
+ read_loc += data_size2;
- read_data_dma(nandc, reg_off, data_buf, data_size1);
+ nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
+ }
+
+ config_nand_cw_read(nandc);
+
+ read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
reg_off += data_size1;
data_buf += data_size1;
- read_data_dma(nandc, reg_off, oob_buf, oob_size1);
+ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
reg_off += oob_size1;
oob_buf += oob_size1;
- read_data_dma(nandc, reg_off, data_buf, data_size2);
+ read_data_dma(nandc, reg_off, data_buf, data_size2, 0);
reg_off += data_size2;
data_buf += data_size2;
- read_data_dma(nandc, reg_off, oob_buf, oob_size2);
+ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
}
@@ -1280,6 +1735,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int ret;
clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
host->use_ecc = true;
set_address(host, 0, page);
@@ -1303,12 +1759,14 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
int i, ret;
clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
host->use_ecc = true;
update_rw_regs(host, ecc->steps, false);
+ config_nand_page_write(nandc);
for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size;
@@ -1322,9 +1780,9 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
oob_size = ecc->bytes;
}
- config_cw_write_pre(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size);
+ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
+ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
/*
* when ECC is enabled, we don't really need to write anything
@@ -1337,10 +1795,10 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
oob_buf += host->bbm_size;
write_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size);
+ oob_buf, oob_size, 0);
}
- config_cw_write_post(nandc);
+ config_nand_cw_write(nandc);
data_buf += data_size;
oob_buf += oob_size;
@@ -1367,12 +1825,14 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
int i, ret;
clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
host->use_ecc = false;
update_rw_regs(host, ecc->steps, false);
+ config_nand_page_write(nandc);
for (i = 0; i < ecc->steps; i++) {
int data_size1, data_size2, oob_size1, oob_size2;
@@ -1391,24 +1851,25 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}
- config_cw_write_pre(nandc);
-
- write_data_dma(nandc, reg_off, data_buf, data_size1);
+ write_data_dma(nandc, reg_off, data_buf, data_size1,
+ NAND_BAM_NO_EOT);
reg_off += data_size1;
data_buf += data_size1;
- write_data_dma(nandc, reg_off, oob_buf, oob_size1);
+ write_data_dma(nandc, reg_off, oob_buf, oob_size1,
+ NAND_BAM_NO_EOT);
reg_off += oob_size1;
oob_buf += oob_size1;
- write_data_dma(nandc, reg_off, data_buf, data_size2);
+ write_data_dma(nandc, reg_off, data_buf, data_size2,
+ NAND_BAM_NO_EOT);
reg_off += data_size2;
data_buf += data_size2;
- write_data_dma(nandc, reg_off, oob_buf, oob_size2);
+ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
- config_cw_write_post(nandc);
+ config_nand_cw_write(nandc);
}
ret = submit_descs(nandc);
@@ -1441,11 +1902,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
host->use_ecc = true;
+ clear_bam_transaction(nandc);
ret = copy_last_cw(host, page);
if (ret)
return ret;
clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
/* calculate the data and oob size for the last codeword/step */
data_size = ecc->size - ((ecc->steps - 1) << 2);
@@ -1458,10 +1921,10 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, false);
- config_cw_write_pre(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- data_size + oob_size);
- config_cw_write_post(nandc);
+ config_nand_page_write(nandc);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, data_size + oob_size, 0);
+ config_nand_cw_write(nandc);
ret = submit_descs(nandc);
@@ -1498,6 +1961,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
*/
host->use_ecc = false;
+ clear_bam_transaction(nandc);
ret = copy_last_cw(host, page);
if (ret)
goto err;
@@ -1528,6 +1992,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
int page, ret, status = 0;
clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
/*
* to mark the BBM as bad, we flash the entire last codeword with 0s.
@@ -1543,9 +2008,10 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, false);
- config_cw_write_pre(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size);
- config_cw_write_post(nandc);
+ config_nand_page_write(nandc);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, host->cw_size, 0);
+ config_nand_cw_write(nandc);
ret = submit_descs(nandc);
@@ -1794,7 +2260,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
* uses lesser bytes for ECC. If RS is used, the ECC bytes is
* always 10 bytes
*/
- if (nandc->ecc_modes & ECC_BCH_4BIT) {
+ if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
/* BCH */
host->bch_enabled = true;
ecc_mode = 0;
@@ -1842,6 +2308,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
cwperpage = mtd->writesize / ecc->size;
+ nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
+ cwperpage);
/*
* DATA_UD_BYTES varies based on whether the read/write command protects
@@ -1893,7 +2361,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
| wide_bus << WIDE_FLASH
| 1 << DEV0_CFG1_ECC_DISABLE;
- host->ecc_bch_cfg = host->bch_enabled << ECC_CFG_ECC_DISABLE
+ host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
| 0 << ECC_SW_RESET
| host->cw_data << ECC_NUM_DATA_BYTES
| 1 << ECC_FORCE_CLK_OPEN
@@ -1904,6 +2372,10 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
host->clrflashstatus = FS_READY_BSY_N;
host->clrreadstatus = 0xc0;
+ nandc->regs->erased_cw_detect_cfg_clr =
+ cpu_to_le32(CLR_ERASED_PAGE_DET);
+ nandc->regs->erased_cw_detect_cfg_set =
+ cpu_to_le32(SET_ERASED_PAGE_DET);
dev_dbg(nandc->dev,
"cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
@@ -1948,10 +2420,55 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
if (!nandc->reg_read_buf)
return -ENOMEM;
- nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
- if (!nandc->chan) {
- dev_err(nandc->dev, "failed to request slave channel\n");
- return -ENODEV;
+ if (nandc->props->is_bam) {
+ nandc->reg_read_dma =
+ dma_map_single(nandc->dev, nandc->reg_read_buf,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
+ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
+ return -EIO;
+ }
+
+ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
+ if (!nandc->tx_chan) {
+ dev_err(nandc->dev, "failed to request tx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx");
+ if (!nandc->rx_chan) {
+ dev_err(nandc->dev, "failed to request rx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd");
+ if (!nandc->cmd_chan) {
+ dev_err(nandc->dev, "failed to request cmd channel\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Initially allocate BAM transaction to read ONFI param page.
+ * After detecting all the devices, this BAM transaction will
+ * be freed and the next BAM tranasction will be allocated with
+ * maximum codeword size
+ */
+ nandc->max_cwperpage = 1;
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
+ } else {
+ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
+ if (!nandc->chan) {
+ dev_err(nandc->dev,
+ "failed to request slave channel\n");
+ return -ENODEV;
+ }
}
INIT_LIST_HEAD(&nandc->desc_list);
@@ -1964,21 +2481,48 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
{
- dma_release_channel(nandc->chan);
+ if (nandc->props->is_bam) {
+ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
+ dma_unmap_single(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+
+ if (nandc->tx_chan)
+ dma_release_channel(nandc->tx_chan);
+
+ if (nandc->rx_chan)
+ dma_release_channel(nandc->rx_chan);
+
+ if (nandc->cmd_chan)
+ dma_release_channel(nandc->cmd_chan);
+ } else {
+ if (nandc->chan)
+ dma_release_channel(nandc->chan);
+ }
}
/* one time setup of a few nand controller registers */
static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
{
+ u32 nand_ctrl;
+
/* kill onenand */
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
+ nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
+ NAND_DEV_CMD_VLD_VAL);
- /* enable ADM DMA */
- nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ /* enable ADM or BAM DMA */
+ if (nandc->props->is_bam) {
+ nand_ctrl = nandc_read(nandc, NAND_CTRL);
+ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
+ } else {
+ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ }
/* save the original values of these registers */
- nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
- nandc->vld = nandc_read(nandc, NAND_DEV_CMD_VLD);
+ nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
+ nandc->vld = NAND_DEV_CMD_VLD_VAL;
return 0;
}
@@ -2034,14 +2578,77 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
return ret;
ret = qcom_nand_host_setup(host);
- if (ret)
- return ret;
+
+ return ret;
+}
+
+static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc,
+ struct qcom_nand_host *host,
+ struct device_node *dn)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
ret = nand_scan_tail(mtd);
if (ret)
return ret;
- return mtd_device_register(mtd, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ nand_cleanup(mtd_to_nand(mtd));
+
+ return ret;
+}
+
+static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
+{
+ struct device *dev = nandc->dev;
+ struct device_node *dn = dev->of_node, *child;
+ struct qcom_nand_host *host, *tmp;
+ int ret;
+
+ for_each_available_child_of_node(dn, child) {
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ of_node_put(child);
+ return -ENOMEM;
+ }
+
+ ret = qcom_nand_host_init(nandc, host, child);
+ if (ret) {
+ devm_kfree(dev, host);
+ continue;
+ }
+
+ list_add_tail(&host->node, &nandc->host_list);
+ }
+
+ if (list_empty(&nandc->host_list))
+ return -ENODEV;
+
+ if (nandc->props->is_bam) {
+ free_bam_transaction(nandc);
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
+ }
+
+ list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
+ ret = qcom_nand_mtd_register(nandc, host, child);
+ if (ret) {
+ list_del(&host->node);
+ devm_kfree(dev, host);
+ }
+ }
+
+ if (list_empty(&nandc->host_list))
+ return -ENODEV;
+
+ return 0;
}
/* parse custom DT properties here */
@@ -2051,16 +2658,20 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
struct device_node *np = nandc->dev->of_node;
int ret;
- ret = of_property_read_u32(np, "qcom,cmd-crci", &nandc->cmd_crci);
- if (ret) {
- dev_err(nandc->dev, "command CRCI unspecified\n");
- return ret;
- }
+ if (!nandc->props->is_bam) {
+ ret = of_property_read_u32(np, "qcom,cmd-crci",
+ &nandc->cmd_crci);
+ if (ret) {
+ dev_err(nandc->dev, "command CRCI unspecified\n");
+ return ret;
+ }
- ret = of_property_read_u32(np, "qcom,data-crci", &nandc->data_crci);
- if (ret) {
- dev_err(nandc->dev, "data CRCI unspecified\n");
- return ret;
+ ret = of_property_read_u32(np, "qcom,data-crci",
+ &nandc->data_crci);
+ if (ret) {
+ dev_err(nandc->dev, "data CRCI unspecified\n");
+ return ret;
+ }
}
return 0;
@@ -2069,10 +2680,8 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
static int qcom_nandc_probe(struct platform_device *pdev)
{
struct qcom_nand_controller *nandc;
- struct qcom_nand_host *host;
const void *dev_data;
struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node, *child;
struct resource *res;
int ret;
@@ -2089,7 +2698,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
return -ENODEV;
}
- nandc->ecc_modes = (unsigned long)dev_data;
+ nandc->props = dev_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nandc->base = devm_ioremap_resource(dev, res);
@@ -2112,7 +2721,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
ret = qcom_nandc_alloc(nandc);
if (ret)
- return ret;
+ goto err_core_clk;
ret = clk_prepare_enable(nandc->core_clk);
if (ret)
@@ -2126,35 +2735,12 @@ static int qcom_nandc_probe(struct platform_device *pdev)
if (ret)
goto err_setup;
- for_each_available_child_of_node(dn, child) {
- if (of_device_is_compatible(child, "qcom,nandcs")) {
- host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host) {
- of_node_put(child);
- ret = -ENOMEM;
- goto err_cs_init;
- }
-
- ret = qcom_nand_host_init(nandc, host, child);
- if (ret) {
- devm_kfree(dev, host);
- continue;
- }
-
- list_add_tail(&host->node, &nandc->host_list);
- }
- }
-
- if (list_empty(&nandc->host_list)) {
- ret = -ENODEV;
- goto err_cs_init;
- }
+ ret = qcom_probe_nand_devices(nandc);
+ if (ret)
+ goto err_setup;
return 0;
-err_cs_init:
- list_for_each_entry(host, &nandc->host_list, node)
- nand_release(nand_to_mtd(&host->chip));
err_setup:
clk_disable_unprepare(nandc->aon_clk);
err_aon_clk:
@@ -2181,15 +2767,40 @@ static int qcom_nandc_remove(struct platform_device *pdev)
return 0;
}
-#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
+static const struct qcom_nandc_props ipq806x_nandc_props = {
+ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
+ .is_bam = false,
+ .dev_cmd_reg_start = 0x0,
+};
+
+static const struct qcom_nandc_props ipq4019_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x0,
+};
+
+static const struct qcom_nandc_props ipq8074_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x7000,
+};
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
*/
static const struct of_device_id qcom_nandc_of_match[] = {
- { .compatible = "qcom,ipq806x-nand",
- .data = (void *)EBI2_NANDC_ECC_MODES,
+ {
+ .compatible = "qcom,ipq806x-nand",
+ .data = &ipq806x_nandc_props,
+ },
+ {
+ .compatible = "qcom,ipq4019-nand",
+ .data = &ipq4019_nandc_props,
+ },
+ {
+ .compatible = "qcom,ipq8074-nand",
+ .data = &ipq8074_nandc_props,
},
{}
};
diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h
index d042ddb71a8b..8713c57f6207 100644
--- a/drivers/mtd/nand/r852.h
+++ b/drivers/mtd/nand/r852.h
@@ -10,7 +10,7 @@
#include <linux/pci.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/spinlock.h>
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 9e0c849607b9..4c383eeec6f6 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -43,7 +43,7 @@
#include <linux/of_device.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 891ac7b99305..e7f3c98487e6 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -38,7 +38,7 @@
#include <linux/string.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/sh_flctl.h>
@@ -411,7 +411,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
- if (dma_addr)
+ if (!dma_mapping_error(chan->device->dev, dma_addr))
desc = dmaengine_prep_slave_single(chan, dma_addr, len,
tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -1141,8 +1141,8 @@ static int flctl_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_err(&pdev->dev, "failed to get flste irq data\n");
- return -ENXIO;
+ dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq);
+ return irq;
}
ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 064ca1757589..f59c455d9f51 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -17,7 +17,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/sharpsl.h>
@@ -183,7 +183,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
/* Register the partitions */
mtd->name = "sharpsl-nand";
- err = mtd_device_parse_register(mtd, NULL, NULL,
+ err = mtd_device_parse_register(mtd, data->part_parsers, NULL,
data->partitions, data->nr_partitions);
if (err)
goto err_add;
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
index 5939dff253c2..c378705c6e2b 100644
--- a/drivers/mtd/nand/sm_common.c
+++ b/drivers/mtd/nand/sm_common.c
@@ -7,7 +7,7 @@
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/module.h>
#include <linux/sizes.h>
#include "sm_common.h"
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index 72369bd079af..575997d0ef8a 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -13,7 +13,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 6abd142b1324..82244be3e766 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -31,7 +31,7 @@
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -2212,7 +2212,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
if (ret)
goto out_ahb_clk_unprepare;
- nfc->reset = devm_reset_control_get_optional(dev, "ahb");
+ nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
if (IS_ERR(nfc->reset)) {
ret = PTR_ERR(nfc->reset);
goto out_mod_clk_unprepare;
diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c
index 9d40b793b1c4..766906f03943 100644
--- a/drivers/mtd/nand/tango_nand.c
+++ b/drivers/mtd/nand/tango_nand.c
@@ -11,7 +11,7 @@
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index fc5e773f8b60..84dbf32332e1 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -34,7 +34,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
@@ -440,7 +440,9 @@ static int tmio_probe(struct platform_device *dev)
goto err_irq;
/* Register the partitions */
- retval = mtd_device_parse_register(mtd, NULL, NULL,
+ retval = mtd_device_parse_register(mtd,
+ data ? data->part_parsers : NULL,
+ NULL,
data ? data->partition : NULL,
data ? data->num_partitions : 0);
if (!retval)
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
index 0a14fda2e41b..b567d212fe7d 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -16,7 +16,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c
index 744ab10e8962..8037d4b48a05 100644
--- a/drivers/mtd/nand/vf610_nfc.c
+++ b/drivers/mtd/nand/vf610_nfc.c
@@ -31,10 +31,9 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -814,12 +813,14 @@ static int vf610_nfc_suspend(struct device *dev)
static int vf610_nfc_resume(struct device *dev)
{
+ int err;
+
struct mtd_info *mtd = dev_get_drvdata(dev);
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- pinctrl_pm_select_default_state(dev);
-
- clk_prepare_enable(nfc->clk);
+ err = clk_prepare_enable(nfc->clk);
+ if (err)
+ return err;
vf610_nfc_preinit_controller(nfc);
vf610_nfc_init_controller(nfc);
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
index ddee4005248c..9926b4e3d69d 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/xway_nand.c
@@ -7,7 +7,7 @@
* Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de>
*/
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index e21161353e76..1f1a61168b3d 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -34,7 +34,7 @@
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/blktrans.h>
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c
index a5dfbfbebfca..184c8fbfe465 100644
--- a/drivers/mtd/nftlmount.c
+++ b/drivers/mtd/nftlmount.c
@@ -25,7 +25,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nftl.h>
#define SECTORSIZE 512
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 2861c7079d7b..6bdf4e525677 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -50,8 +50,8 @@ static int parse_ofpart_partitions(struct mtd_info *master,
* when using another parser), so don't be louder than
* KERN_DEBUG
*/
- pr_debug("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n",
- master->name, mtd_node->full_name);
+ pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
+ master->name, mtd_node);
ofpart_node = mtd_node;
dedicated = false;
} else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) {
@@ -87,9 +87,9 @@ static int parse_ofpart_partitions(struct mtd_info *master,
reg = of_get_property(pp, "reg", &len);
if (!reg) {
if (dedicated) {
- pr_debug("%s: ofpart partition %s (%s) missing reg property.\n",
- master->name, pp->full_name,
- mtd_node->full_name);
+ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
+ master->name, pp,
+ mtd_node);
goto ofpart_fail;
} else {
nr_parts--;
@@ -100,9 +100,9 @@ static int parse_ofpart_partitions(struct mtd_info *master,
a_cells = of_n_addr_cells(pp);
s_cells = of_n_size_cells(pp);
if (len / 4 != a_cells + s_cells) {
- pr_debug("%s: ofpart partition %s (%s) error parsing reg property.\n",
- master->name, pp->full_name,
- mtd_node->full_name);
+ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
+ master->name, pp,
+ mtd_node);
goto ofpart_fail;
}
@@ -131,8 +131,8 @@ static int parse_ofpart_partitions(struct mtd_info *master,
return nr_parts;
ofpart_fail:
- pr_err("%s: error parsing ofpart partition %s (%s)\n",
- master->name, pp->full_name, mtd_node->full_name);
+ pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n",
+ master->name, pp, mtd_node);
ret = -EINVAL;
ofpart_none:
of_node_put(pp);
@@ -166,8 +166,7 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
if (!part)
return 0; /* No partitions found */
- pr_warn("Device tree uses obsolete partition map binding: %s\n",
- dp->full_name);
+ pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp);
nr_parts = plen / sizeof(part[0]);
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 293c8a4d1e49..69c638dd0484 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -89,6 +89,22 @@ config SPI_NXP_SPIFI
config SPI_INTEL_SPI
tristate
+config SPI_INTEL_SPI_PCI
+ tristate "Intel PCH/PCU SPI flash PCI driver" if EXPERT
+ depends on X86 && PCI
+ select SPI_INTEL_SPI
+ help
+ This enables PCI support for the Intel PCH/PCU SPI controller in
+ master mode. This controller is present in modern Intel hardware
+ and is used to hold BIOS and other persistent settings. Using
+ this driver it is possible to upgrade BIOS directly from Linux.
+
+ Say N here unless you know what you are doing. Overwriting the
+ SPI flash may render the system unbootable.
+
+ To compile this driver as a module, choose M here: the module
+ will be called intel-spi-pci.
+
config SPI_INTEL_SPI_PLATFORM
tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT
depends on X86
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 285aab86c7ca..7d84c5108e17 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
+obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o
obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
-obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o \ No newline at end of file
+obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 0106357421bd..8d3cbe27efb6 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -621,19 +621,18 @@ static void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type)
}
/*
- * The AST2500 FMC flash controller should be strapped by hardware, or
- * autodetected, but the AST2500 SPI flash needs to be set.
+ * The first chip of the AST2500 FMC flash controller is strapped by
+ * hardware, or autodetected, but other chips need to be set. Enforce
+ * the 4B setting for all chips.
*/
static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 reg;
- if (chip->controller->info == &spi_2500_info) {
- reg = readl(controller->regs + CE_CONTROL_REG);
- reg |= 1 << chip->cs;
- writel(reg, controller->regs + CE_CONTROL_REG);
- }
+ reg = readl(controller->regs + CE_CONTROL_REG);
+ reg |= 1 << chip->cs;
+ writel(reg, controller->regs + CE_CONTROL_REG);
}
/*
diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
index ba76fa8f2031..6c5708bacad8 100644
--- a/drivers/mtd/spi-nor/atmel-quadspi.c
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -35,7 +35,6 @@
#include <linux/io.h>
#include <linux/gpio.h>
-#include <linux/pinctrl/consumer.h>
/* QSPI register offsets */
#define QSPI_CR 0x0000 /* Control Register */
diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
index d1106832b9d5..04f9fb5cd9b6 100644
--- a/drivers/mtd/spi-nor/hisi-sfc.c
+++ b/drivers/mtd/spi-nor/hisi-sfc.c
@@ -355,16 +355,16 @@ static int hisi_spi_nor_register(struct device_node *np,
ret = of_property_read_u32(np, "reg", &priv->chipselect);
if (ret) {
- dev_err(dev, "There's no reg property for %s\n",
- np->full_name);
+ dev_err(dev, "There's no reg property for %pOF\n",
+ np);
return ret;
}
ret = of_property_read_u32(np, "spi-max-frequency",
&priv->clkrate);
if (ret) {
- dev_err(dev, "There's no spi-max-frequency property for %s\n",
- np->full_name);
+ dev_err(dev, "There's no spi-max-frequency property for %pOF\n",
+ np);
return ret;
}
priv->host = host;
diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c
new file mode 100644
index 000000000000..e82652335ede
--- /dev/null
+++ b/drivers/mtd/spi-nor/intel-spi-pci.c
@@ -0,0 +1,82 @@
+/*
+ * Intel PCH/PCU SPI flash PCI driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * 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/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "intel-spi.h"
+
+#define BCR 0xdc
+#define BCR_WPD BIT(0)
+
+static const struct intel_spi_boardinfo bxt_info = {
+ .type = INTEL_SPI_BXT,
+};
+
+static int intel_spi_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct intel_spi_boardinfo *info;
+ struct intel_spi *ispi;
+ u32 bcr;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* Try to make the chip read/write */
+ pci_read_config_dword(pdev, BCR, &bcr);
+ if (!(bcr & BCR_WPD)) {
+ bcr |= BCR_WPD;
+ pci_write_config_dword(pdev, BCR, bcr);
+ pci_read_config_dword(pdev, BCR, &bcr);
+ }
+ info->writeable = !!(bcr & BCR_WPD);
+
+ ispi = intel_spi_probe(&pdev->dev, &pdev->resource[0], info);
+ if (IS_ERR(ispi))
+ return PTR_ERR(ispi);
+
+ pci_set_drvdata(pdev, ispi);
+ return 0;
+}
+
+static void intel_spi_pci_remove(struct pci_dev *pdev)
+{
+ intel_spi_remove(pci_get_drvdata(pdev));
+}
+
+static const struct pci_device_id intel_spi_pci_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids);
+
+static struct pci_driver intel_spi_pci_driver = {
+ .name = "intel-spi",
+ .id_table = intel_spi_pci_ids,
+ .probe = intel_spi_pci_probe,
+ .remove = intel_spi_pci_remove,
+};
+
+module_pci_driver(intel_spi_pci_driver);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash PCI driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index 8a20ec4991c8..c258c7adf1c5 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -24,7 +24,6 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 1413828ff1fb..cf1d4a15e10a 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -17,6 +17,7 @@
#include <linux/mutex.h>
#include <linux/math64.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
@@ -86,6 +87,8 @@ struct flash_info {
* to support memory size above 128Mib.
*/
#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
+#define USE_CLSR BIT(14) /* use CLSR command */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -306,8 +309,18 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor)
int sr = read_sr(nor);
if (sr < 0)
return sr;
- else
- return !(sr & SR_WIP);
+
+ if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) {
+ if (sr & SR_E_ERR)
+ dev_err(nor->dev, "Erase Error occurred\n");
+ else
+ dev_err(nor->dev, "Programming Error occurred\n");
+
+ nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
+ return -EIO;
+ }
+
+ return !(sr & SR_WIP);
}
static inline int spi_nor_fsr_ready(struct spi_nor *nor)
@@ -1041,15 +1054,15 @@ static const struct flash_info spi_nor_ids[] = {
*/
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
- { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) },
+ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
- { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
@@ -1079,6 +1092,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) },
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+ { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
/* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
@@ -1380,6 +1394,16 @@ write_err:
return ret;
}
+/**
+ * macronix_quad_enable() - set QE bit in Status Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Status Register.
+ *
+ * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
static int macronix_quad_enable(struct spi_nor *nor)
{
int ret, val;
@@ -1413,22 +1437,13 @@ static int macronix_quad_enable(struct spi_nor *nor)
* second byte will be written to the configuration register.
* Return negative if error occurred.
*/
-static int write_sr_cr(struct spi_nor *nor, u16 val)
-{
- nor->cmd_buf[0] = val & 0xff;
- nor->cmd_buf[1] = (val >> 8);
-
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
-}
-
-static int spansion_quad_enable(struct spi_nor *nor)
+static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
{
int ret;
- int quad_en = CR_QUAD_EN_SPAN << 8;
write_enable(nor);
- ret = write_sr_cr(nor, quad_en);
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
if (ret < 0) {
dev_err(nor->dev,
"error while writing configuration register\n");
@@ -1442,6 +1457,41 @@ static int spansion_quad_enable(struct spi_nor *nor)
return ret;
}
+ return 0;
+}
+
+/**
+ * spansion_quad_enable() - set QE bit in Configuraiton Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function is kept for legacy purpose because it has been used for a
+ * long time without anybody complaining but it should be considered as
+ * deprecated and maybe buggy.
+ * First, this function doesn't care about the previous values of the Status
+ * and Configuration Registers when it sets the QE bit (bit 1) in the
+ * Configuration Register: all other bits are cleared, which may have unwanted
+ * side effects like removing some block protections.
+ * Secondly, it uses the Read Configuration Register (35h) instruction though
+ * some very old and few memories don't support this instruction. If a pull-up
+ * resistor is present on the MISO/IO1 line, we might still be able to pass the
+ * "read back" test because the QSPI memory doesn't recognize the command,
+ * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_quad_enable(struct spi_nor *nor)
+{
+ u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN};
+ int ret;
+
+ ret = write_sr_cr(nor, sr_cr);
+ if (ret)
+ return ret;
+
/* read back and check it */
ret = read_cr(nor);
if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
@@ -1452,6 +1502,140 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
+/**
+ * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function should be used with QSPI memories not supporting the Read
+ * Configuration Register (35h) instruction.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
+{
+ u8 sr_cr[2];
+ int ret;
+
+ /* Keep the current value of the Status Register. */
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading status register\n");
+ return -EINVAL;
+ }
+ sr_cr[0] = ret;
+ sr_cr[1] = CR_QUAD_EN_SPAN;
+
+ return write_sr_cr(nor, sr_cr);
+}
+
+/**
+ * spansion_read_cr_quad_enable() - set QE bit in Configuration Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function should be used with QSPI memories supporting the Read
+ * Configuration Register (35h) instruction.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_read_cr_quad_enable(struct spi_nor *nor)
+{
+ struct device *dev = nor->dev;
+ u8 sr_cr[2];
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_err(dev, "error while reading configuration register\n");
+ return -EINVAL;
+ }
+
+ if (ret & CR_QUAD_EN_SPAN)
+ return 0;
+
+ sr_cr[1] = ret | CR_QUAD_EN_SPAN;
+
+ /* Keep the current value of the Status Register. */
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(dev, "error while reading status register\n");
+ return -EINVAL;
+ }
+ sr_cr[0] = ret;
+
+ ret = write_sr_cr(nor, sr_cr);
+ if (ret)
+ return ret;
+
+ /* Read back and check it. */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_err(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * sr2_bit7_quad_enable() - set QE bit in Status Register 2.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Status Register 2.
+ *
+ * This is one of the procedures to set the QE bit described in the SFDP
+ * (JESD216 rev B) specification but no manufacturer using this procedure has
+ * been identified yet, hence the name of the function.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int sr2_bit7_quad_enable(struct spi_nor *nor)
+{
+ u8 sr2;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+ if (ret)
+ return ret;
+ if (sr2 & SR2_QUAD_EN_BIT7)
+ return 0;
+
+ /* Update the Quad Enable bit. */
+ sr2 |= SR2_QUAD_EN_BIT7;
+
+ write_enable(nor);
+
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while writing status register 2\n");
+ return -EINVAL;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "timeout while writing status register 2\n");
+ return ret;
+ }
+
+ /* Read back and check it. */
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+ if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) {
+ dev_err(nor->dev, "SR2 Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
@@ -1591,6 +1775,560 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
pp->proto = proto;
}
+/*
+ * Serial Flash Discoverable Parameters (SFDP) parsing.
+ */
+
+/**
+ * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the SFDP area to start reading data from
+ * @len: number of bytes to read
+ * @buf: buffer where the SFDP data are copied into
+ *
+ * Whatever the actual numbers of bytes for address and dummy cycles are
+ * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always
+ * followed by a 3-byte address and 8 dummy clock cycles.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
+ size_t len, void *buf)
+{
+ u8 addr_width, read_opcode, read_dummy;
+ int ret;
+
+ read_opcode = nor->read_opcode;
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+
+ nor->read_opcode = SPINOR_OP_RDSFDP;
+ nor->addr_width = 3;
+ nor->read_dummy = 8;
+
+ while (len) {
+ ret = nor->read(nor, addr, len, (u8 *)buf);
+ if (!ret || ret > len) {
+ ret = -EIO;
+ goto read_err;
+ }
+ if (ret < 0)
+ goto read_err;
+
+ buf += ret;
+ addr += ret;
+ len -= ret;
+ }
+ ret = 0;
+
+read_err:
+ nor->read_opcode = read_opcode;
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+
+ return ret;
+}
+
+struct sfdp_parameter_header {
+ u8 id_lsb;
+ u8 minor;
+ u8 major;
+ u8 length; /* in double words */
+ u8 parameter_table_pointer[3]; /* byte address */
+ u8 id_msb;
+};
+
+#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb)
+#define SFDP_PARAM_HEADER_PTP(p) \
+ (((p)->parameter_table_pointer[2] << 16) | \
+ ((p)->parameter_table_pointer[1] << 8) | \
+ ((p)->parameter_table_pointer[0] << 0))
+
+#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */
+#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */
+
+#define SFDP_SIGNATURE 0x50444653U
+#define SFDP_JESD216_MAJOR 1
+#define SFDP_JESD216_MINOR 0
+#define SFDP_JESD216A_MINOR 5
+#define SFDP_JESD216B_MINOR 6
+
+struct sfdp_header {
+ u32 signature; /* Ox50444653U <=> "SFDP" */
+ u8 minor;
+ u8 major;
+ u8 nph; /* 0-base number of parameter headers */
+ u8 unused;
+
+ /* Basic Flash Parameter Table. */
+ struct sfdp_parameter_header bfpt_header;
+};
+
+/* Basic Flash Parameter Table */
+
+/*
+ * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs.
+ * They are indexed from 1 but C arrays are indexed from 0.
+ */
+#define BFPT_DWORD(i) ((i) - 1)
+#define BFPT_DWORD_MAX 16
+
+/* The first version of JESB216 defined only 9 DWORDs. */
+#define BFPT_DWORD_MAX_JESD216 9
+
+/* 1st DWORD. */
+#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16)
+#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17)
+#define BFPT_DWORD1_DTR BIT(19)
+#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20)
+#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21)
+#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22)
+
+/* 5th DWORD. */
+#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0)
+#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4)
+
+/* 11th DWORD. */
+#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4
+#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4)
+
+/* 15th DWORD. */
+
+/*
+ * (from JESD216 rev B)
+ * Quad Enable Requirements (QER):
+ * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4
+ * reads based on instruction. DQ3/HOLD# functions are hold during
+ * instruction phase.
+ * - 001b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * Writing only one byte to the status register has the side-effect of
+ * clearing status register 2, including the QE bit. The 100b code is
+ * used if writing one byte to the status register does not modify
+ * status register 2.
+ * - 010b: QE is bit 6 of status register 1. It is set via Write Status with
+ * one data byte where bit 6 is one.
+ * [...]
+ * - 011b: QE is bit 7 of status register 2. It is set via Write status
+ * register 2 instruction 3Eh with one data byte where bit 7 is one.
+ * [...]
+ * The status register 2 is read using instruction 3Fh.
+ * - 100b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * In contrast to the 001b code, writing one byte to the status
+ * register does not modify status register 2.
+ * - 101b: QE is bit 1 of status register 2. Status register 1 is read using
+ * Read Status instruction 05h. Status register2 is read using
+ * instruction 35h. QE is set via Writ Status instruction 01h with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ */
+#define BFPT_DWORD15_QER_MASK GENMASK(22, 20)
+#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */
+#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20)
+#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */
+#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */
+
+struct sfdp_bfpt {
+ u32 dwords[BFPT_DWORD_MAX];
+};
+
+/* Fast Read settings. */
+
+static inline void
+spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read,
+ u16 half,
+ enum spi_nor_protocol proto)
+{
+ read->num_mode_clocks = (half >> 5) & 0x07;
+ read->num_wait_states = (half >> 0) & 0x1f;
+ read->opcode = (half >> 8) & 0xff;
+ read->proto = proto;
+}
+
+struct sfdp_bfpt_read {
+ /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */
+ u32 hwcaps;
+
+ /*
+ * The <supported_bit> bit in <supported_dword> BFPT DWORD tells us
+ * whether the Fast Read x-y-z command is supported.
+ */
+ u32 supported_dword;
+ u32 supported_bit;
+
+ /*
+ * The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD
+ * encodes the op code, the number of mode clocks and the number of wait
+ * states to be used by Fast Read x-y-z command.
+ */
+ u32 settings_dword;
+ u32 settings_shift;
+
+ /* The SPI protocol for this Fast Read x-y-z command. */
+ enum spi_nor_protocol proto;
+};
+
+static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = {
+ /* Fast Read 1-1-2 */
+ {
+ SNOR_HWCAPS_READ_1_1_2,
+ BFPT_DWORD(1), BIT(16), /* Supported bit */
+ BFPT_DWORD(4), 0, /* Settings */
+ SNOR_PROTO_1_1_2,
+ },
+
+ /* Fast Read 1-2-2 */
+ {
+ SNOR_HWCAPS_READ_1_2_2,
+ BFPT_DWORD(1), BIT(20), /* Supported bit */
+ BFPT_DWORD(4), 16, /* Settings */
+ SNOR_PROTO_1_2_2,
+ },
+
+ /* Fast Read 2-2-2 */
+ {
+ SNOR_HWCAPS_READ_2_2_2,
+ BFPT_DWORD(5), BIT(0), /* Supported bit */
+ BFPT_DWORD(6), 16, /* Settings */
+ SNOR_PROTO_2_2_2,
+ },
+
+ /* Fast Read 1-1-4 */
+ {
+ SNOR_HWCAPS_READ_1_1_4,
+ BFPT_DWORD(1), BIT(22), /* Supported bit */
+ BFPT_DWORD(3), 16, /* Settings */
+ SNOR_PROTO_1_1_4,
+ },
+
+ /* Fast Read 1-4-4 */
+ {
+ SNOR_HWCAPS_READ_1_4_4,
+ BFPT_DWORD(1), BIT(21), /* Supported bit */
+ BFPT_DWORD(3), 0, /* Settings */
+ SNOR_PROTO_1_4_4,
+ },
+
+ /* Fast Read 4-4-4 */
+ {
+ SNOR_HWCAPS_READ_4_4_4,
+ BFPT_DWORD(5), BIT(4), /* Supported bit */
+ BFPT_DWORD(7), 16, /* Settings */
+ SNOR_PROTO_4_4_4,
+ },
+};
+
+struct sfdp_bfpt_erase {
+ /*
+ * The half-word at offset <shift> in DWORD <dwoard> encodes the
+ * op code and erase sector size to be used by Sector Erase commands.
+ */
+ u32 dword;
+ u32 shift;
+};
+
+static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = {
+ /* Erase Type 1 in DWORD8 bits[15:0] */
+ {BFPT_DWORD(8), 0},
+
+ /* Erase Type 2 in DWORD8 bits[31:16] */
+ {BFPT_DWORD(8), 16},
+
+ /* Erase Type 3 in DWORD9 bits[15:0] */
+ {BFPT_DWORD(9), 0},
+
+ /* Erase Type 4 in DWORD9 bits[31:16] */
+ {BFPT_DWORD(9), 16},
+};
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
+
+/**
+ * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
+ * @nor: pointer to a 'struct spi_nor'
+ * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing
+ * the Basic Flash Parameter Table length and version
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be
+ * filled
+ *
+ * The Basic Flash Parameter Table is the main and only mandatory table as
+ * defined by the SFDP (JESD216) specification.
+ * It provides us with the total size (memory density) of the data array and
+ * the number of address bytes for Fast Read, Page Program and Sector Erase
+ * commands.
+ * For Fast READ commands, it also gives the number of mode clock cycles and
+ * wait states (regrouped in the number of dummy clock cycles) for each
+ * supported instruction op code.
+ * For Page Program, the page size is now available since JESD216 rev A, however
+ * the supported instruction op codes are still not provided.
+ * For Sector Erase commands, this table stores the supported instruction op
+ * codes and the associated sector sizes.
+ * Finally, the Quad Enable Requirements (QER) are also available since JESD216
+ * rev A. The QER bits encode the manufacturer dependent procedure to be
+ * executed to set the Quad Enable (QE) bit in some internal register of the
+ * Quad SPI memory. Indeed the QE bit, when it exists, must be set before
+ * sending any Quad SPI command to the memory. Actually, setting the QE bit
+ * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2
+ * and IO3 hence enabling 4 (Quad) I/O lines.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_bfpt(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ struct spi_nor_flash_parameter *params)
+{
+ struct mtd_info *mtd = &nor->mtd;
+ struct sfdp_bfpt bfpt;
+ size_t len;
+ int i, cmd, err;
+ u32 addr;
+ u16 half;
+
+ /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
+ if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
+ return -EINVAL;
+
+ /* Read the Basic Flash Parameter Table. */
+ len = min_t(size_t, sizeof(bfpt),
+ bfpt_header->length * sizeof(u32));
+ addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
+ memset(&bfpt, 0, sizeof(bfpt));
+ err = spi_nor_read_sfdp(nor, addr, len, &bfpt);
+ if (err < 0)
+ return err;
+
+ /* Fix endianness of the BFPT DWORDs. */
+ for (i = 0; i < BFPT_DWORD_MAX; i++)
+ bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]);
+
+ /* Number of address bytes. */
+ switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {
+ case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
+ nor->addr_width = 3;
+ break;
+
+ case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:
+ nor->addr_width = 4;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Flash Memory Density (in bits). */
+ params->size = bfpt.dwords[BFPT_DWORD(2)];
+ if (params->size & BIT(31)) {
+ params->size &= ~BIT(31);
+ params->size = 1ULL << params->size;
+ } else {
+ params->size++;
+ }
+ params->size >>= 3; /* Convert to bytes. */
+
+ /* Fast Read settings. */
+ for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) {
+ const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i];
+ struct spi_nor_read_command *read;
+
+ if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) {
+ params->hwcaps.mask &= ~rd->hwcaps;
+ continue;
+ }
+
+ params->hwcaps.mask |= rd->hwcaps;
+ cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps);
+ read = &params->reads[cmd];
+ half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift;
+ spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
+ }
+
+ /* Sector Erase settings. */
+ for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
+ const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
+ u32 erasesize;
+ u8 opcode;
+
+ half = bfpt.dwords[er->dword] >> er->shift;
+ erasesize = half & 0xff;
+
+ /* erasesize == 0 means this Erase Type is not supported. */
+ if (!erasesize)
+ continue;
+
+ erasesize = 1U << erasesize;
+ opcode = (half >> 8) & 0xff;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ if (erasesize == SZ_4K) {
+ nor->erase_opcode = opcode;
+ mtd->erasesize = erasesize;
+ break;
+ }
+#endif
+ if (!mtd->erasesize || mtd->erasesize < erasesize) {
+ nor->erase_opcode = opcode;
+ mtd->erasesize = erasesize;
+ }
+ }
+
+ /* Stop here if not JESD216 rev A or later. */
+ if (bfpt_header->length < BFPT_DWORD_MAX)
+ return 0;
+
+ /* Page size: this field specifies 'N' so the page size = 2^N bytes. */
+ params->page_size = bfpt.dwords[BFPT_DWORD(11)];
+ params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK;
+ params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT;
+ params->page_size = 1U << params->page_size;
+
+ /* Quad Enable Requirements. */
+ switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) {
+ case BFPT_DWORD15_QER_NONE:
+ params->quad_enable = NULL;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:
+ case BFPT_DWORD15_QER_SR2_BIT1_NO_RD:
+ params->quad_enable = spansion_no_read_cr_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR1_BIT6:
+ params->quad_enable = macronix_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT7:
+ params->quad_enable = sr2_bit7_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT1:
+ params->quad_enable = spansion_read_cr_quad_enable;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be
+ * filled
+ *
+ * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216
+ * specification. This is a standard which tends to supported by almost all
+ * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at
+ * runtime the main parameters needed to perform basic SPI flash operations such
+ * as Fast Read, Page Program or Sector Erase commands.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_sfdp(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params)
+{
+ const struct sfdp_parameter_header *param_header, *bfpt_header;
+ struct sfdp_parameter_header *param_headers = NULL;
+ struct sfdp_header header;
+ struct device *dev = nor->dev;
+ size_t psize;
+ int i, err;
+
+ /* Get the SFDP header. */
+ err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header);
+ if (err < 0)
+ return err;
+
+ /* Check the SFDP header version. */
+ if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
+ header.major != SFDP_JESD216_MAJOR ||
+ header.minor < SFDP_JESD216_MINOR)
+ return -EINVAL;
+
+ /*
+ * Verify that the first and only mandatory parameter header is a
+ * Basic Flash Parameter Table header as specified in JESD216.
+ */
+ bfpt_header = &header.bfpt_header;
+ if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
+ bfpt_header->major != SFDP_JESD216_MAJOR)
+ return -EINVAL;
+
+ /*
+ * Allocate memory then read all parameter headers with a single
+ * Read SFDP command. These parameter headers will actually be parsed
+ * twice: a first time to get the latest revision of the basic flash
+ * parameter table, then a second time to handle the supported optional
+ * tables.
+ * Hence we read the parameter headers once for all to reduce the
+ * processing time. Also we use kmalloc() instead of devm_kmalloc()
+ * because we don't need to keep these parameter headers: the allocated
+ * memory is always released with kfree() before exiting this function.
+ */
+ if (header.nph) {
+ psize = header.nph * sizeof(*param_headers);
+
+ param_headers = kmalloc(psize, GFP_KERNEL);
+ if (!param_headers)
+ return -ENOMEM;
+
+ err = spi_nor_read_sfdp(nor, sizeof(header),
+ psize, param_headers);
+ if (err < 0) {
+ dev_err(dev, "failed to read SFDP parameter headers\n");
+ goto exit;
+ }
+ }
+
+ /*
+ * Check other parameter headers to get the latest revision of
+ * the basic flash parameter table.
+ */
+ for (i = 0; i < header.nph; i++) {
+ param_header = &param_headers[i];
+
+ if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&
+ param_header->major == SFDP_JESD216_MAJOR &&
+ (param_header->minor > bfpt_header->minor ||
+ (param_header->minor == bfpt_header->minor &&
+ param_header->length > bfpt_header->length)))
+ bfpt_header = param_header;
+ }
+
+ err = spi_nor_parse_bfpt(nor, bfpt_header, params);
+ if (err)
+ goto exit;
+
+ /* Parse other parameter headers. */
+ for (i = 0; i < header.nph; i++) {
+ param_header = &param_headers[i];
+
+ switch (SFDP_PARAM_HEADER_ID(param_header)) {
+ case SFDP_SECTOR_MAP_ID:
+ dev_info(dev, "non-uniform erase sector maps are not supported yet.\n");
+ break;
+
+ default:
+ break;
+ }
+
+ if (err)
+ goto exit;
+ }
+
+exit:
+ kfree(param_headers);
+ return err;
+}
+
static int spi_nor_init_params(struct spi_nor *nor,
const struct flash_info *info,
struct spi_nor_flash_parameter *params)
@@ -1646,11 +2384,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
break;
default:
+ /* Kept only for backward compatibility purpose. */
params->quad_enable = spansion_quad_enable;
break;
}
}
+ /* Override the parameters with data read from SFDP tables. */
+ nor->addr_width = 0;
+ nor->mtd.erasesize = 0;
+ if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
+ !(info->flags & SPI_NOR_SKIP_SFDP)) {
+ struct spi_nor_flash_parameter sfdp_params;
+
+ memcpy(&sfdp_params, params, sizeof(sfdp_params));
+ if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
+ nor->addr_width = 0;
+ nor->mtd.erasesize = 0;
+ } else {
+ memcpy(params, &sfdp_params, sizeof(*params));
+ }
+ }
+
return 0;
}
@@ -1762,6 +2517,10 @@ static int spi_nor_select_erase(struct spi_nor *nor,
{
struct mtd_info *mtd = &nor->mtd;
+ /* Do nothing if already configured from SFDP. */
+ if (mtd->erasesize)
+ return 0;
+
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
@@ -1960,6 +2719,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
nor->flags |= SNOR_F_HAS_SR_TB;
if (info->flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+ if (info->flags & USE_CLSR)
+ nor->flags |= SNOR_F_USE_CLSR;
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
@@ -1994,9 +2755,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (ret)
return ret;
- if (info->addr_width)
+ if (nor->addr_width) {
+ /* already configured from SFDP */
+ } else if (info->addr_width) {
nor->addr_width = info->addr_width;
- else if (mtd->size > 0x1000000) {
+ } else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c
index 41b13d1cdcc4..95f0bf95f095 100644
--- a/drivers/mtd/ssfdc.c
+++ b/drivers/mtd/ssfdc.c
@@ -16,7 +16,7 @@
#include <linux/slab.h>
#include <linux/hdreg.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/blktrans.h>
struct ssfdcr_record {
diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c
index f26dec896afa..5f03b8c885a9 100644
--- a/drivers/mtd/tests/nandbiterrs.c
+++ b/drivers/mtd/tests/nandbiterrs.c
@@ -47,7 +47,7 @@
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/err.h>
-#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/slab.h>
#include "mtd_test.h"