summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/mchp23k256.c20
-rw-r--r--drivers/mtd/devices/mchp48l640.c16
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c4
-rw-r--r--drivers/mtd/devices/spear_smi.c2
-rw-r--r--drivers/mtd/devices/sst25l.c4
-rw-r--r--drivers/mtd/hyperbus/Kconfig2
-rw-r--r--drivers/mtd/mtd_blkdevs.c1
-rw-r--r--drivers/mtd/mtdcore.c48
-rw-r--r--drivers/mtd/mtdswap.c2
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/core.c12
-rw-r--r--drivers/mtd/nand/ecc-mxic.c879
-rw-r--r--drivers/mtd/nand/ecc.c119
-rw-r--r--drivers/mtd/nand/onenand/generic.c7
-rw-r--r--drivers/mtd/nand/raw/Kconfig16
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c18
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.c4
-rw-r--r--drivers/mtd/nand/raw/brcmnand/Makefile2
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcma_nand.c132
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c162
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.h29
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c27
-rw-r--r--drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c2
-rw-r--r--drivers/mtd/nand/raw/ingenic/jz4780_bch.c2
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.c2
-rw-r--r--drivers/mtd/nand/raw/nand_base.c83
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c2
-rw-r--r--drivers/mtd/nand/raw/nandsim.c47
-rw-r--r--drivers/mtd/nand/raw/omap2.c2
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c4
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c2
-rw-r--r--drivers/mtd/nand/raw/renesas-nand-controller.c2
-rw-r--r--drivers/mtd/nand/raw/rockchip-nand-controller.c1
-rw-r--r--drivers/mtd/nand/raw/sh_flctl.c2
-rw-r--r--drivers/mtd/nand/raw/sharpsl.c1
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c40
-rw-r--r--drivers/mtd/nand/spi/core.c51
-rw-r--r--drivers/mtd/nand/spi/macronix.c2
-rw-r--r--drivers/mtd/parsers/Kconfig2
-rw-r--r--drivers/mtd/spi-nor/atmel.c81
-rw-r--r--drivers/mtd/spi-nor/catalyst.c6
-rw-r--r--drivers/mtd/spi-nor/controllers/Kconfig36
-rw-r--r--drivers/mtd/spi-nor/controllers/Makefile3
-rw-r--r--drivers/mtd/spi-nor/controllers/aspeed-smc.c15
-rw-r--r--drivers/mtd/spi-nor/controllers/intel-spi-pci.c99
-rw-r--r--drivers/mtd/spi-nor/controllers/intel-spi-platform.c54
-rw-r--r--drivers/mtd/spi-nor/controllers/intel-spi.c968
-rw-r--r--drivers/mtd/spi-nor/controllers/intel-spi.h21
-rw-r--r--drivers/mtd/spi-nor/core.c268
-rw-r--r--drivers/mtd/spi-nor/core.h70
-rw-r--r--drivers/mtd/spi-nor/eon.c6
-rw-r--r--drivers/mtd/spi-nor/esmt.c6
-rw-r--r--drivers/mtd/spi-nor/everspin.c6
-rw-r--r--drivers/mtd/spi-nor/fujitsu.c6
-rw-r--r--drivers/mtd/spi-nor/gigadevice.c6
-rw-r--r--drivers/mtd/spi-nor/intel.c6
-rw-r--r--drivers/mtd/spi-nor/issi.c10
-rw-r--r--drivers/mtd/spi-nor/macronix.c14
-rw-r--r--drivers/mtd/spi-nor/micron-st.c259
-rw-r--r--drivers/mtd/spi-nor/spansion.c168
-rw-r--r--drivers/mtd/spi-nor/sst.c44
-rw-r--r--drivers/mtd/spi-nor/winbond.c29
-rw-r--r--drivers/mtd/spi-nor/xilinx.c97
-rw-r--r--drivers/mtd/spi-nor/xmc.c6
-rw-r--r--drivers/mtd/tests/speedtest.c11
66 files changed, 2136 insertions, 1920 deletions
diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c
index a8b31bddf14b..3a6ea7a6a30c 100644
--- a/drivers/mtd/devices/mchp23k256.c
+++ b/drivers/mtd/devices/mchp23k256.c
@@ -209,13 +209,11 @@ static int mchp23k256_probe(struct spi_device *spi)
return 0;
}
-static int mchp23k256_remove(struct spi_device *spi)
+static void mchp23k256_remove(struct spi_device *spi)
{
struct mchp23k256_flash *flash = spi_get_drvdata(spi);
WARN_ON(mtd_device_unregister(&flash->mtd));
-
- return 0;
}
static const struct of_device_id mchp23k256_of_table[] = {
@@ -231,13 +229,27 @@ static const struct of_device_id mchp23k256_of_table[] = {
};
MODULE_DEVICE_TABLE(of, mchp23k256_of_table);
+static const struct spi_device_id mchp23k256_spi_ids[] = {
+ {
+ .name = "mchp23k256",
+ .driver_data = (kernel_ulong_t)&mchp23k256_caps,
+ },
+ {
+ .name = "mchp23lcv1024",
+ .driver_data = (kernel_ulong_t)&mchp23lcv1024_caps,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, mchp23k256_spi_ids);
+
static struct spi_driver mchp23k256_driver = {
.driver = {
.name = "mchp23k256",
- .of_match_table = of_match_ptr(mchp23k256_of_table),
+ .of_match_table = mchp23k256_of_table,
},
.probe = mchp23k256_probe,
.remove = mchp23k256_remove,
+ .id_table = mchp23k256_spi_ids,
};
module_spi_driver(mchp23k256_driver);
diff --git a/drivers/mtd/devices/mchp48l640.c b/drivers/mtd/devices/mchp48l640.c
index 231a10790196..40cd5041174c 100644
--- a/drivers/mtd/devices/mchp48l640.c
+++ b/drivers/mtd/devices/mchp48l640.c
@@ -341,13 +341,11 @@ static int mchp48l640_probe(struct spi_device *spi)
return 0;
}
-static int mchp48l640_remove(struct spi_device *spi)
+static void mchp48l640_remove(struct spi_device *spi)
{
struct mchp48l640_flash *flash = spi_get_drvdata(spi);
WARN_ON(mtd_device_unregister(&flash->mtd));
-
- return 0;
}
static const struct of_device_id mchp48l640_of_table[] = {
@@ -359,13 +357,23 @@ static const struct of_device_id mchp48l640_of_table[] = {
};
MODULE_DEVICE_TABLE(of, mchp48l640_of_table);
+static const struct spi_device_id mchp48l640_spi_ids[] = {
+ {
+ .name = "48l640",
+ .driver_data = (kernel_ulong_t)&mchp48l640_caps,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, mchp48l640_spi_ids);
+
static struct spi_driver mchp48l640_driver = {
.driver = {
.name = "mchp48l640",
- .of_match_table = of_match_ptr(mchp48l640_of_table),
+ .of_match_table = mchp48l640_of_table,
},
.probe = mchp48l640_probe,
.remove = mchp48l640_remove,
+ .id_table = mchp48l640_spi_ids,
};
module_spi_driver(mchp48l640_driver);
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 734878abaa23..134e27328597 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -916,7 +916,7 @@ static int dataflash_probe(struct spi_device *spi)
return status;
}
-static int dataflash_remove(struct spi_device *spi)
+static void dataflash_remove(struct spi_device *spi)
{
struct dataflash *flash = spi_get_drvdata(spi);
@@ -925,8 +925,6 @@ static int dataflash_remove(struct spi_device *spi)
WARN_ON(mtd_device_unregister(&flash->mtd));
kfree(flash);
-
- return 0;
}
static struct spi_driver dataflash_driver = {
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 2e00862389dd..24073518587f 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -969,7 +969,7 @@ static int spear_smi_probe(struct platform_device *pdev)
goto err;
}
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 7f124c1bfa40..8813994ce9f4 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -398,13 +398,11 @@ static int sst25l_probe(struct spi_device *spi)
return 0;
}
-static int sst25l_remove(struct spi_device *spi)
+static void sst25l_remove(struct spi_device *spi)
{
struct sst25l_flash *flash = spi_get_drvdata(spi);
WARN_ON(mtd_device_unregister(&flash->mtd));
-
- return 0;
}
static struct spi_driver sst25l_driver = {
diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig
index 46c7e407e378..30ffc4c16e4d 100644
--- a/drivers/mtd/hyperbus/Kconfig
+++ b/drivers/mtd/hyperbus/Kconfig
@@ -15,7 +15,7 @@ if MTD_HYPERBUS
config HBMC_AM654
tristate "HyperBus controller driver for AM65x SoC"
- depends on ARM64 || COMPILE_TEST
+ depends on ARCH_K3 || COMPILE_TEST
select MULTIPLEXER
imply MUX_MMIO
help
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 243f28a3206b..64d2b093f114 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -158,6 +158,7 @@ static void mtd_blktrans_work(struct mtd_blktrans_dev *dev)
}
background_done = 0;
+ cond_resched();
spin_lock_irq(&dev->queue_lock);
}
}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index fc6090366684..7731796024e0 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -336,49 +336,31 @@ static const struct device_type mtd_devtype = {
.release = mtd_release,
};
-static int mtd_partid_debug_show(struct seq_file *s, void *p)
-{
- struct mtd_info *mtd = s->private;
-
- seq_printf(s, "%s\n", mtd->dbg.partid);
+static bool mtd_expert_analysis_mode;
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(mtd_partid_debug);
-
-static int mtd_partname_debug_show(struct seq_file *s, void *p)
+#ifdef CONFIG_DEBUG_FS
+bool mtd_check_expert_analysis_mode(void)
{
- struct mtd_info *mtd = s->private;
-
- seq_printf(s, "%s\n", mtd->dbg.partname);
+ const char *mtd_expert_analysis_warning =
+ "Bad block checks have been entirely disabled.\n"
+ "This is only reserved for post-mortem forensics and debug purposes.\n"
+ "Never enable this mode if you do not know what you are doing!\n";
- return 0;
+ return WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning);
}
-
-DEFINE_SHOW_ATTRIBUTE(mtd_partname_debug);
+EXPORT_SYMBOL_GPL(mtd_check_expert_analysis_mode);
+#endif
static struct dentry *dfs_dir_mtd;
static void mtd_debugfs_populate(struct mtd_info *mtd)
{
- struct mtd_info *master = mtd_get_master(mtd);
struct device *dev = &mtd->dev;
- struct dentry *root;
if (IS_ERR_OR_NULL(dfs_dir_mtd))
return;
- root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
- mtd->dbg.dfs_dir = root;
-
- if (master->dbg.partid)
- debugfs_create_file("partid", 0400, root, master,
- &mtd_partid_debug_fops);
-
- if (master->dbg.partname)
- debugfs_create_file("partname", 0400, root, master,
- &mtd_partname_debug_fops);
+ mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
}
#ifndef CONFIG_MMU
@@ -2367,14 +2349,6 @@ static struct backing_dev_info * __init mtd_bdi_init(const char *name)
return ret ? ERR_PTR(ret) : bdi;
}
-char *mtd_expert_analysis_warning =
- "Bad block checks have been entirely disabled.\n"
- "This is only reserved for post-mortem forensics and debug purposes.\n"
- "Never enable this mode if you do not know what you are doing!\n";
-EXPORT_SYMBOL_GPL(mtd_expert_analysis_warning);
-bool mtd_expert_analysis_mode;
-EXPORT_SYMBOL_GPL(mtd_expert_analysis_mode);
-
static struct proc_dir_entry *proc_mtd;
static int __init init_mtd(void)
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index e86b04bc1d6b..dc7f1532a37f 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -19,7 +19,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-#include <linux/genhd.h>
+#include <linux/blkdev.h>
#include <linux/swap.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index b40455234cbd..9b249826ef93 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -46,6 +46,13 @@ config MTD_NAND_ECC_SW_BCH
ECC codes. They are used with NAND devices requiring more than 1 bit
of error correction.
+config MTD_NAND_ECC_MXIC
+ bool "Macronix external hardware ECC engine"
+ depends on HAS_IOMEM
+ select MTD_NAND_ECC
+ help
+ This enables support for the hardware ECC engine from Macronix.
+
endmenu
endmenu
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1c0b46960eb1..a4e6b7ae0614 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -10,3 +10,4 @@ obj-y += spi/
nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o
nandcore-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o
nandcore-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o
+nandcore-$(CONFIG_MTD_NAND_ECC_MXIC) += ecc-mxic.o
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
index 416947f28b67..dbd7b06524b3 100644
--- a/drivers/mtd/nand/core.c
+++ b/drivers/mtd/nand/core.c
@@ -21,7 +21,7 @@
*/
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos)
{
- if (WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning))
+ if (mtd_check_expert_analysis_mode())
return false;
if (nanddev_bbt_is_initialized(nand)) {
@@ -235,7 +235,9 @@ static int nanddev_get_ecc_engine(struct nand_device *nand)
nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
break;
case NAND_ECC_ENGINE_TYPE_ON_HOST:
- pr_err("On-host hardware ECC engines not supported yet\n");
+ nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);
+ if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
break;
default:
pr_err("Missing ECC engine type\n");
@@ -255,7 +257,7 @@ static int nanddev_put_ecc_engine(struct nand_device *nand)
{
switch (nand->ecc.ctx.conf.engine_type) {
case NAND_ECC_ENGINE_TYPE_ON_HOST:
- pr_err("On-host hardware ECC engines not supported yet\n");
+ nand_ecc_put_on_host_hw_engine(nand);
break;
case NAND_ECC_ENGINE_TYPE_NONE:
case NAND_ECC_ENGINE_TYPE_SOFT:
@@ -300,7 +302,9 @@ int nanddev_ecc_engine_init(struct nand_device *nand)
/* Look for the ECC engine to use */
ret = nanddev_get_ecc_engine(nand);
if (ret) {
- pr_err("No ECC engine found\n");
+ if (ret != -EPROBE_DEFER)
+ pr_err("No ECC engine found\n");
+
return ret;
}
diff --git a/drivers/mtd/nand/ecc-mxic.c b/drivers/mtd/nand/ecc-mxic.c
new file mode 100644
index 000000000000..8afdca731b87
--- /dev/null
+++ b/drivers/mtd/nand/ecc-mxic.c
@@ -0,0 +1,879 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for Macronix external hardware ECC engine for NAND devices, also
+ * called DPE for Data Processing Engine.
+ *
+ * Copyright © 2019 Macronix
+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand-ecc-mxic.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* DPE Configuration */
+#define DP_CONFIG 0x00
+#define ECC_EN BIT(0)
+#define ECC_TYP(idx) (((idx) << 3) & GENMASK(6, 3))
+/* DPE Interrupt Status */
+#define INTRPT_STS 0x04
+#define TRANS_CMPLT BIT(0)
+#define SDMA_MAIN BIT(1)
+#define SDMA_SPARE BIT(2)
+#define ECC_ERR BIT(3)
+#define TO_SPARE BIT(4)
+#define TO_MAIN BIT(5)
+/* DPE Interrupt Status Enable */
+#define INTRPT_STS_EN 0x08
+/* DPE Interrupt Signal Enable */
+#define INTRPT_SIG_EN 0x0C
+/* Host Controller Configuration */
+#define HC_CONFIG 0x10
+#define DEV2MEM 0 /* TRANS_TYP_DMA in the spec */
+#define MEM2MEM BIT(4) /* TRANS_TYP_IO in the spec */
+#define MAPPING BIT(5) /* TRANS_TYP_MAPPING in the spec */
+#define ECC_PACKED 0 /* LAYOUT_TYP_INTEGRATED in the spec */
+#define ECC_INTERLEAVED BIT(2) /* LAYOUT_TYP_DISTRIBUTED in the spec */
+#define BURST_TYP_FIXED 0
+#define BURST_TYP_INCREASING BIT(0)
+/* Host Controller Slave Address */
+#define HC_SLV_ADDR 0x14
+/* ECC Chunk Size */
+#define CHUNK_SIZE 0x20
+/* Main Data Size */
+#define MAIN_SIZE 0x24
+/* Spare Data Size */
+#define SPARE_SIZE 0x28
+#define META_SZ(reg) ((reg) & GENMASK(7, 0))
+#define PARITY_SZ(reg) (((reg) & GENMASK(15, 8)) >> 8)
+#define RSV_SZ(reg) (((reg) & GENMASK(23, 16)) >> 16)
+#define SPARE_SZ(reg) ((reg) >> 24)
+/* ECC Chunk Count */
+#define CHUNK_CNT 0x30
+/* SDMA Control */
+#define SDMA_CTRL 0x40
+#define WRITE_NAND 0
+#define READ_NAND BIT(1)
+#define CONT_NAND BIT(29)
+#define CONT_SYSM BIT(30) /* Continue System Memory? */
+#define SDMA_STRT BIT(31)
+/* SDMA Address of Main Data */
+#define SDMA_MAIN_ADDR 0x44
+/* SDMA Address of Spare Data */
+#define SDMA_SPARE_ADDR 0x48
+/* DPE Version Number */
+#define DP_VER 0xD0
+#define DP_VER_OFFSET 16
+
+/* Status bytes between each chunk of spare data */
+#define STAT_BYTES 4
+#define NO_ERR 0x00
+#define MAX_CORR_ERR 0x28
+#define UNCORR_ERR 0xFE
+#define ERASED_CHUNK 0xFF
+
+struct mxic_ecc_engine {
+ struct device *dev;
+ void __iomem *regs;
+ int irq;
+ struct completion complete;
+ struct nand_ecc_engine external_engine;
+ struct nand_ecc_engine pipelined_engine;
+ struct mutex lock;
+};
+
+struct mxic_ecc_ctx {
+ /* ECC machinery */
+ unsigned int data_step_sz;
+ unsigned int oob_step_sz;
+ unsigned int parity_sz;
+ unsigned int meta_sz;
+ u8 *status;
+ int steps;
+
+ /* DMA boilerplate */
+ struct nand_ecc_req_tweak_ctx req_ctx;
+ u8 *oobwithstat;
+ struct scatterlist sg[2];
+ struct nand_page_io_req *req;
+ unsigned int pageoffs;
+};
+
+static struct mxic_ecc_engine *ext_ecc_eng_to_mxic(struct nand_ecc_engine *eng)
+{
+ return container_of(eng, struct mxic_ecc_engine, external_engine);
+}
+
+static struct mxic_ecc_engine *pip_ecc_eng_to_mxic(struct nand_ecc_engine *eng)
+{
+ return container_of(eng, struct mxic_ecc_engine, pipelined_engine);
+}
+
+static struct mxic_ecc_engine *nand_to_mxic(struct nand_device *nand)
+{
+ struct nand_ecc_engine *eng = nand->ecc.engine;
+
+ if (eng->integration == NAND_ECC_ENGINE_INTEGRATION_EXTERNAL)
+ return ext_ecc_eng_to_mxic(eng);
+ else
+ return pip_ecc_eng_to_mxic(eng);
+}
+
+static int mxic_ecc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+
+ if (section < 0 || section >= ctx->steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * ctx->oob_step_sz) + ctx->meta_sz;
+ oobregion->length = ctx->parity_sz;
+
+ return 0;
+}
+
+static int mxic_ecc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+
+ if (section < 0 || section >= ctx->steps)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 2;
+ oobregion->length = ctx->meta_sz - 2;
+ } else {
+ oobregion->offset = section * ctx->oob_step_sz;
+ oobregion->length = ctx->meta_sz;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mxic_ecc_ooblayout_ops = {
+ .ecc = mxic_ecc_ooblayout_ecc,
+ .free = mxic_ecc_ooblayout_free,
+};
+
+static void mxic_ecc_disable_engine(struct mxic_ecc_engine *mxic)
+{
+ u32 reg;
+
+ reg = readl(mxic->regs + DP_CONFIG);
+ reg &= ~ECC_EN;
+ writel(reg, mxic->regs + DP_CONFIG);
+}
+
+static void mxic_ecc_enable_engine(struct mxic_ecc_engine *mxic)
+{
+ u32 reg;
+
+ reg = readl(mxic->regs + DP_CONFIG);
+ reg |= ECC_EN;
+ writel(reg, mxic->regs + DP_CONFIG);
+}
+
+static void mxic_ecc_disable_int(struct mxic_ecc_engine *mxic)
+{
+ writel(0, mxic->regs + INTRPT_SIG_EN);
+}
+
+static void mxic_ecc_enable_int(struct mxic_ecc_engine *mxic)
+{
+ writel(TRANS_CMPLT, mxic->regs + INTRPT_SIG_EN);
+}
+
+static irqreturn_t mxic_ecc_isr(int irq, void *dev_id)
+{
+ struct mxic_ecc_engine *mxic = dev_id;
+ u32 sts;
+
+ sts = readl(mxic->regs + INTRPT_STS);
+ if (!sts)
+ return IRQ_NONE;
+
+ if (sts & TRANS_CMPLT)
+ complete(&mxic->complete);
+
+ writel(sts, mxic->regs + INTRPT_STS);
+
+ return IRQ_HANDLED;
+}
+
+static int mxic_ecc_init_ctx(struct nand_device *nand, struct device *dev)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct nand_ecc_props *reqs = &nand->ecc.requirements;
+ struct nand_ecc_props *user = &nand->ecc.user_conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int step_size = 0, strength = 0, desired_correction = 0, steps, idx;
+ static const int possible_strength[] = {4, 8, 40, 48};
+ static const int spare_size[] = {32, 32, 96, 96};
+ struct mxic_ecc_ctx *ctx;
+ u32 spare_reg;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ nand->ecc.ctx.priv = ctx;
+
+ /* Only large page NAND chips may use BCH */
+ if (mtd->oobsize < 64) {
+ pr_err("BCH cannot be used with small page NAND chips\n");
+ return -EINVAL;
+ }
+
+ mtd_set_ooblayout(mtd, &mxic_ecc_ooblayout_ops);
+
+ /* Enable all status bits */
+ writel(TRANS_CMPLT | SDMA_MAIN | SDMA_SPARE | ECC_ERR |
+ TO_SPARE | TO_MAIN, mxic->regs + INTRPT_STS_EN);
+
+ /* Configure the correction depending on the NAND device topology */
+ if (user->step_size && user->strength) {
+ step_size = user->step_size;
+ strength = user->strength;
+ } else if (reqs->step_size && reqs->strength) {
+ step_size = reqs->step_size;
+ strength = reqs->strength;
+ }
+
+ if (step_size && strength) {
+ steps = mtd->writesize / step_size;
+ desired_correction = steps * strength;
+ }
+
+ /* Step size is fixed to 1kiB, strength may vary (4 possible values) */
+ conf->step_size = SZ_1K;
+ steps = mtd->writesize / conf->step_size;
+
+ ctx->status = devm_kzalloc(dev, steps * sizeof(u8), GFP_KERNEL);
+ if (!ctx->status)
+ return -ENOMEM;
+
+ if (desired_correction) {
+ strength = desired_correction / steps;
+
+ for (idx = 0; idx < ARRAY_SIZE(possible_strength); idx++)
+ if (possible_strength[idx] >= strength)
+ break;
+
+ idx = min_t(unsigned int, idx,
+ ARRAY_SIZE(possible_strength) - 1);
+ } else {
+ /* Missing data, maximize the correction */
+ idx = ARRAY_SIZE(possible_strength) - 1;
+ }
+
+ /* Tune the selected strength until it fits in the OOB area */
+ for (; idx >= 0; idx--) {
+ if (spare_size[idx] * steps <= mtd->oobsize)
+ break;
+ }
+
+ /* This engine cannot be used with this NAND device */
+ if (idx < 0)
+ return -EINVAL;
+
+ /* Configure the engine for the desired strength */
+ writel(ECC_TYP(idx), mxic->regs + DP_CONFIG);
+ conf->strength = possible_strength[idx];
+ spare_reg = readl(mxic->regs + SPARE_SIZE);
+
+ ctx->steps = steps;
+ ctx->data_step_sz = mtd->writesize / steps;
+ ctx->oob_step_sz = mtd->oobsize / steps;
+ ctx->parity_sz = PARITY_SZ(spare_reg);
+ ctx->meta_sz = META_SZ(spare_reg);
+
+ /* Ensure buffers will contain enough bytes to store the STAT_BYTES */
+ ctx->req_ctx.oob_buffer_size = nanddev_per_page_oobsize(nand) +
+ (ctx->steps * STAT_BYTES);
+ ret = nand_ecc_init_req_tweaking(&ctx->req_ctx, nand);
+ if (ret)
+ return ret;
+
+ ctx->oobwithstat = kmalloc(mtd->oobsize + (ctx->steps * STAT_BYTES),
+ GFP_KERNEL);
+ if (!ctx->oobwithstat) {
+ ret = -ENOMEM;
+ goto cleanup_req_tweak;
+ }
+
+ sg_init_table(ctx->sg, 2);
+
+ /* Configuration dump and sanity checks */
+ dev_err(dev, "DPE version number: %d\n",
+ readl(mxic->regs + DP_VER) >> DP_VER_OFFSET);
+ dev_err(dev, "Chunk size: %d\n", readl(mxic->regs + CHUNK_SIZE));
+ dev_err(dev, "Main size: %d\n", readl(mxic->regs + MAIN_SIZE));
+ dev_err(dev, "Spare size: %d\n", SPARE_SZ(spare_reg));
+ dev_err(dev, "Rsv size: %ld\n", RSV_SZ(spare_reg));
+ dev_err(dev, "Parity size: %d\n", ctx->parity_sz);
+ dev_err(dev, "Meta size: %d\n", ctx->meta_sz);
+
+ if ((ctx->meta_sz + ctx->parity_sz + RSV_SZ(spare_reg)) !=
+ SPARE_SZ(spare_reg)) {
+ dev_err(dev, "Wrong OOB configuration: %d + %d + %ld != %d\n",
+ ctx->meta_sz, ctx->parity_sz, RSV_SZ(spare_reg),
+ SPARE_SZ(spare_reg));
+ ret = -EINVAL;
+ goto free_oobwithstat;
+ }
+
+ if (ctx->oob_step_sz != SPARE_SZ(spare_reg)) {
+ dev_err(dev, "Wrong OOB configuration: %d != %d\n",
+ ctx->oob_step_sz, SPARE_SZ(spare_reg));
+ ret = -EINVAL;
+ goto free_oobwithstat;
+ }
+
+ return 0;
+
+free_oobwithstat:
+ kfree(ctx->oobwithstat);
+cleanup_req_tweak:
+ nand_ecc_cleanup_req_tweaking(&ctx->req_ctx);
+
+ return ret;
+}
+
+static int mxic_ecc_init_ctx_external(struct nand_device *nand)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct device *dev = nand->ecc.engine->dev;
+ int ret;
+
+ dev_info(dev, "Macronix ECC engine in external mode\n");
+
+ ret = mxic_ecc_init_ctx(nand, dev);
+ if (ret)
+ return ret;
+
+ /* Trigger each step manually */
+ writel(1, mxic->regs + CHUNK_CNT);
+ writel(BURST_TYP_INCREASING | ECC_PACKED | MEM2MEM,
+ mxic->regs + HC_CONFIG);
+
+ return 0;
+}
+
+static int mxic_ecc_init_ctx_pipelined(struct nand_device *nand)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct mxic_ecc_ctx *ctx;
+ struct device *dev;
+ int ret;
+
+ dev = nand_ecc_get_engine_dev(nand->ecc.engine->dev);
+ if (!dev)
+ return -EINVAL;
+
+ dev_info(dev, "Macronix ECC engine in pipelined/mapping mode\n");
+
+ ret = mxic_ecc_init_ctx(nand, dev);
+ if (ret)
+ return ret;
+
+ ctx = nand_to_ecc_ctx(nand);
+
+ /* All steps should be handled in one go directly by the internal DMA */
+ writel(ctx->steps, mxic->regs + CHUNK_CNT);
+
+ /*
+ * Interleaved ECC scheme cannot be used otherwise factory bad block
+ * markers would be lost. A packed layout is mandatory.
+ */
+ writel(BURST_TYP_INCREASING | ECC_PACKED | MAPPING,
+ mxic->regs + HC_CONFIG);
+
+ return 0;
+}
+
+static void mxic_ecc_cleanup_ctx(struct nand_device *nand)
+{
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+
+ if (ctx) {
+ nand_ecc_cleanup_req_tweaking(&ctx->req_ctx);
+ kfree(ctx->oobwithstat);
+ }
+}
+
+static int mxic_ecc_data_xfer_wait_for_completion(struct mxic_ecc_engine *mxic)
+{
+ u32 val;
+ int ret;
+
+ if (mxic->irq) {
+ reinit_completion(&mxic->complete);
+ mxic_ecc_enable_int(mxic);
+ ret = wait_for_completion_timeout(&mxic->complete,
+ msecs_to_jiffies(1000));
+ mxic_ecc_disable_int(mxic);
+ } else {
+ ret = readl_poll_timeout(mxic->regs + INTRPT_STS, val,
+ val & TRANS_CMPLT, 10, USEC_PER_SEC);
+ writel(val, mxic->regs + INTRPT_STS);
+ }
+
+ if (ret) {
+ dev_err(mxic->dev, "Timeout on data xfer completion\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int mxic_ecc_process_data(struct mxic_ecc_engine *mxic,
+ unsigned int direction)
+{
+ unsigned int dir = (direction == NAND_PAGE_READ) ?
+ READ_NAND : WRITE_NAND;
+ int ret;
+
+ mxic_ecc_enable_engine(mxic);
+
+ /* Trigger processing */
+ writel(SDMA_STRT | dir, mxic->regs + SDMA_CTRL);
+
+ /* Wait for completion */
+ ret = mxic_ecc_data_xfer_wait_for_completion(mxic);
+
+ mxic_ecc_disable_engine(mxic);
+
+ return ret;
+}
+
+int mxic_ecc_process_data_pipelined(struct nand_ecc_engine *eng,
+ unsigned int direction, dma_addr_t dirmap)
+{
+ struct mxic_ecc_engine *mxic = pip_ecc_eng_to_mxic(eng);
+
+ if (dirmap)
+ writel(dirmap, mxic->regs + HC_SLV_ADDR);
+
+ return mxic_ecc_process_data(mxic, direction);
+}
+EXPORT_SYMBOL_GPL(mxic_ecc_process_data_pipelined);
+
+static void mxic_ecc_extract_status_bytes(struct mxic_ecc_ctx *ctx)
+{
+ u8 *buf = ctx->oobwithstat;
+ int next_stat_pos;
+ int step;
+
+ /* Extract the ECC status */
+ for (step = 0; step < ctx->steps; step++) {
+ next_stat_pos = ctx->oob_step_sz +
+ ((STAT_BYTES + ctx->oob_step_sz) * step);
+
+ ctx->status[step] = buf[next_stat_pos];
+ }
+}
+
+static void mxic_ecc_reconstruct_oobbuf(struct mxic_ecc_ctx *ctx,
+ u8 *dst, const u8 *src)
+{
+ int step;
+
+ /* Reconstruct the OOB buffer linearly (without the ECC status bytes) */
+ for (step = 0; step < ctx->steps; step++)
+ memcpy(dst + (step * ctx->oob_step_sz),
+ src + (step * (ctx->oob_step_sz + STAT_BYTES)),
+ ctx->oob_step_sz);
+}
+
+static void mxic_ecc_add_room_in_oobbuf(struct mxic_ecc_ctx *ctx,
+ u8 *dst, const u8 *src)
+{
+ int step;
+
+ /* Add some space in the OOB buffer for the status bytes */
+ for (step = 0; step < ctx->steps; step++)
+ memcpy(dst + (step * (ctx->oob_step_sz + STAT_BYTES)),
+ src + (step * ctx->oob_step_sz),
+ ctx->oob_step_sz);
+}
+
+static int mxic_ecc_count_biterrs(struct mxic_ecc_engine *mxic,
+ struct nand_device *nand)
+{
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct device *dev = mxic->dev;
+ unsigned int max_bf = 0;
+ bool failure = false;
+ int step;
+
+ for (step = 0; step < ctx->steps; step++) {
+ u8 stat = ctx->status[step];
+
+ if (stat == NO_ERR) {
+ dev_dbg(dev, "ECC step %d: no error\n", step);
+ } else if (stat == ERASED_CHUNK) {
+ dev_dbg(dev, "ECC step %d: erased\n", step);
+ } else if (stat == UNCORR_ERR || stat > MAX_CORR_ERR) {
+ dev_dbg(dev, "ECC step %d: uncorrectable\n", step);
+ mtd->ecc_stats.failed++;
+ failure = true;
+ } else {
+ dev_dbg(dev, "ECC step %d: %d bits corrected\n",
+ step, stat);
+ max_bf = max_t(unsigned int, max_bf, stat);
+ mtd->ecc_stats.corrected += stat;
+ }
+ }
+
+ return failure ? -EBADMSG : max_bf;
+}
+
+/* External ECC engine helpers */
+static int mxic_ecc_prepare_io_req_external(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int offset, nents, step, ret;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ nand_ecc_tweak_req(&ctx->req_ctx, req);
+ ctx->req = req;
+
+ if (req->type == NAND_PAGE_READ)
+ return 0;
+
+ mxic_ecc_add_room_in_oobbuf(ctx, ctx->oobwithstat,
+ ctx->req->oobbuf.out);
+
+ sg_set_buf(&ctx->sg[0], req->databuf.out, req->datalen);
+ sg_set_buf(&ctx->sg[1], ctx->oobwithstat,
+ req->ooblen + (ctx->steps * STAT_BYTES));
+
+ nents = dma_map_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+ if (!nents)
+ return -EINVAL;
+
+ mutex_lock(&mxic->lock);
+
+ for (step = 0; step < ctx->steps; step++) {
+ writel(sg_dma_address(&ctx->sg[0]) + (step * ctx->data_step_sz),
+ mxic->regs + SDMA_MAIN_ADDR);
+ writel(sg_dma_address(&ctx->sg[1]) + (step * (ctx->oob_step_sz + STAT_BYTES)),
+ mxic->regs + SDMA_SPARE_ADDR);
+ ret = mxic_ecc_process_data(mxic, ctx->req->type);
+ if (ret)
+ break;
+ }
+
+ mutex_unlock(&mxic->lock);
+
+ dma_unmap_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+
+ if (ret)
+ return ret;
+
+ /* Retrieve the calculated ECC bytes */
+ for (step = 0; step < ctx->steps; step++) {
+ offset = ctx->meta_sz + (step * ctx->oob_step_sz);
+ mtd_ooblayout_get_eccbytes(mtd,
+ (u8 *)ctx->req->oobbuf.out + offset,
+ ctx->oobwithstat + (step * STAT_BYTES),
+ step * ctx->parity_sz,
+ ctx->parity_sz);
+ }
+
+ return 0;
+}
+
+static int mxic_ecc_finish_io_req_external(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+ int nents, step, ret;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ if (req->type == NAND_PAGE_WRITE) {
+ nand_ecc_restore_req(&ctx->req_ctx, req);
+ return 0;
+ }
+
+ /* Copy the OOB buffer and add room for the ECC engine status bytes */
+ mxic_ecc_add_room_in_oobbuf(ctx, ctx->oobwithstat, ctx->req->oobbuf.in);
+
+ sg_set_buf(&ctx->sg[0], req->databuf.in, req->datalen);
+ sg_set_buf(&ctx->sg[1], ctx->oobwithstat,
+ req->ooblen + (ctx->steps * STAT_BYTES));
+ nents = dma_map_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+ if (!nents)
+ return -EINVAL;
+
+ mutex_lock(&mxic->lock);
+
+ for (step = 0; step < ctx->steps; step++) {
+ writel(sg_dma_address(&ctx->sg[0]) + (step * ctx->data_step_sz),
+ mxic->regs + SDMA_MAIN_ADDR);
+ writel(sg_dma_address(&ctx->sg[1]) + (step * (ctx->oob_step_sz + STAT_BYTES)),
+ mxic->regs + SDMA_SPARE_ADDR);
+ ret = mxic_ecc_process_data(mxic, ctx->req->type);
+ if (ret)
+ break;
+ }
+
+ mutex_unlock(&mxic->lock);
+
+ dma_unmap_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+
+ if (ret) {
+ nand_ecc_restore_req(&ctx->req_ctx, req);
+ return ret;
+ }
+
+ /* Extract the status bytes and reconstruct the buffer */
+ mxic_ecc_extract_status_bytes(ctx);
+ mxic_ecc_reconstruct_oobbuf(ctx, ctx->req->oobbuf.in, ctx->oobwithstat);
+
+ nand_ecc_restore_req(&ctx->req_ctx, req);
+
+ return mxic_ecc_count_biterrs(mxic, nand);
+}
+
+/* Pipelined ECC engine helpers */
+static int mxic_ecc_prepare_io_req_pipelined(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+ int nents;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ nand_ecc_tweak_req(&ctx->req_ctx, req);
+ ctx->req = req;
+
+ /* Copy the OOB buffer and add room for the ECC engine status bytes */
+ mxic_ecc_add_room_in_oobbuf(ctx, ctx->oobwithstat, ctx->req->oobbuf.in);
+
+ sg_set_buf(&ctx->sg[0], req->databuf.in, req->datalen);
+ sg_set_buf(&ctx->sg[1], ctx->oobwithstat,
+ req->ooblen + (ctx->steps * STAT_BYTES));
+
+ nents = dma_map_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+ if (!nents)
+ return -EINVAL;
+
+ mutex_lock(&mxic->lock);
+
+ writel(sg_dma_address(&ctx->sg[0]), mxic->regs + SDMA_MAIN_ADDR);
+ writel(sg_dma_address(&ctx->sg[1]), mxic->regs + SDMA_SPARE_ADDR);
+
+ return 0;
+}
+
+static int mxic_ecc_finish_io_req_pipelined(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct mxic_ecc_engine *mxic = nand_to_mxic(nand);
+ struct mxic_ecc_ctx *ctx = nand_to_ecc_ctx(nand);
+ int ret = 0;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ mutex_unlock(&mxic->lock);
+
+ dma_unmap_sg(mxic->dev, ctx->sg, 2, DMA_BIDIRECTIONAL);
+
+ if (req->type == NAND_PAGE_READ) {
+ mxic_ecc_extract_status_bytes(ctx);
+ mxic_ecc_reconstruct_oobbuf(ctx, ctx->req->oobbuf.in,
+ ctx->oobwithstat);
+ ret = mxic_ecc_count_biterrs(mxic, nand);
+ }
+
+ nand_ecc_restore_req(&ctx->req_ctx, req);
+
+ return ret;
+}
+
+static struct nand_ecc_engine_ops mxic_ecc_engine_external_ops = {
+ .init_ctx = mxic_ecc_init_ctx_external,
+ .cleanup_ctx = mxic_ecc_cleanup_ctx,
+ .prepare_io_req = mxic_ecc_prepare_io_req_external,
+ .finish_io_req = mxic_ecc_finish_io_req_external,
+};
+
+static struct nand_ecc_engine_ops mxic_ecc_engine_pipelined_ops = {
+ .init_ctx = mxic_ecc_init_ctx_pipelined,
+ .cleanup_ctx = mxic_ecc_cleanup_ctx,
+ .prepare_io_req = mxic_ecc_prepare_io_req_pipelined,
+ .finish_io_req = mxic_ecc_finish_io_req_pipelined,
+};
+
+struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void)
+{
+ return &mxic_ecc_engine_pipelined_ops;
+}
+EXPORT_SYMBOL_GPL(mxic_ecc_get_pipelined_ops);
+
+static struct platform_device *
+mxic_ecc_get_pdev(struct platform_device *spi_pdev)
+{
+ struct platform_device *eng_pdev;
+ struct device_node *np;
+
+ /* Retrieve the nand-ecc-engine phandle */
+ np = of_parse_phandle(spi_pdev->dev.of_node, "nand-ecc-engine", 0);
+ if (!np)
+ return NULL;
+
+ /* Jump to the engine's device node */
+ eng_pdev = of_find_device_by_node(np);
+ of_node_put(np);
+
+ return eng_pdev;
+}
+
+void mxic_ecc_put_pipelined_engine(struct nand_ecc_engine *eng)
+{
+ struct mxic_ecc_engine *mxic = pip_ecc_eng_to_mxic(eng);
+
+ platform_device_put(to_platform_device(mxic->dev));
+}
+EXPORT_SYMBOL_GPL(mxic_ecc_put_pipelined_engine);
+
+struct nand_ecc_engine *
+mxic_ecc_get_pipelined_engine(struct platform_device *spi_pdev)
+{
+ struct platform_device *eng_pdev;
+ struct mxic_ecc_engine *mxic;
+
+ eng_pdev = mxic_ecc_get_pdev(spi_pdev);
+ if (!eng_pdev)
+ return ERR_PTR(-ENODEV);
+
+ mxic = platform_get_drvdata(eng_pdev);
+ if (!mxic) {
+ platform_device_put(eng_pdev);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return &mxic->pipelined_engine;
+}
+EXPORT_SYMBOL_GPL(mxic_ecc_get_pipelined_engine);
+
+/*
+ * Only the external ECC engine is exported as the pipelined is SoC specific, so
+ * it is registered directly by the drivers that wrap it.
+ */
+static int mxic_ecc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxic_ecc_engine *mxic;
+ int ret;
+
+ mxic = devm_kzalloc(&pdev->dev, sizeof(*mxic), GFP_KERNEL);
+ if (!mxic)
+ return -ENOMEM;
+
+ mxic->dev = &pdev->dev;
+
+ /*
+ * Both memory regions for the ECC engine itself and the AXI slave
+ * address are mandatory.
+ */
+ mxic->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mxic->regs)) {
+ dev_err(&pdev->dev, "Missing memory region\n");
+ return PTR_ERR(mxic->regs);
+ }
+
+ mxic_ecc_disable_engine(mxic);
+ mxic_ecc_disable_int(mxic);
+
+ /* IRQ is optional yet much more efficient */
+ mxic->irq = platform_get_irq_byname_optional(pdev, "ecc-engine");
+ if (mxic->irq > 0) {
+ ret = devm_request_irq(&pdev->dev, mxic->irq, mxic_ecc_isr, 0,
+ "mxic-ecc", mxic);
+ if (ret)
+ return ret;
+ } else {
+ dev_info(dev, "Invalid or missing IRQ, fallback to polling\n");
+ mxic->irq = 0;
+ }
+
+ mutex_init(&mxic->lock);
+
+ /*
+ * In external mode, the device is the ECC engine. In pipelined mode,
+ * the device is the host controller. The device is used to match the
+ * right ECC engine based on the DT properties.
+ */
+ mxic->external_engine.dev = &pdev->dev;
+ mxic->external_engine.integration = NAND_ECC_ENGINE_INTEGRATION_EXTERNAL;
+ mxic->external_engine.ops = &mxic_ecc_engine_external_ops;
+
+ nand_ecc_register_on_host_hw_engine(&mxic->external_engine);
+
+ platform_set_drvdata(pdev, mxic);
+
+ return 0;
+}
+
+static int mxic_ecc_remove(struct platform_device *pdev)
+{
+ struct mxic_ecc_engine *mxic = platform_get_drvdata(pdev);
+
+ nand_ecc_unregister_on_host_hw_engine(&mxic->external_engine);
+
+ return 0;
+}
+
+static const struct of_device_id mxic_ecc_of_ids[] = {
+ {
+ .compatible = "mxicy,nand-ecc-engine-rev3",
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mxic_ecc_of_ids);
+
+static struct platform_driver mxic_ecc_driver = {
+ .driver = {
+ .name = "mxic-nand-ecc-engine",
+ .of_match_table = mxic_ecc_of_ids,
+ },
+ .probe = mxic_ecc_probe,
+ .remove = mxic_ecc_remove,
+};
+module_platform_driver(mxic_ecc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("Macronix NAND hardware ECC controller");
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
index 6c43dfda01d4..5250764cedee 100644
--- a/drivers/mtd/nand/ecc.c
+++ b/drivers/mtd/nand/ecc.c
@@ -96,6 +96,12 @@
#include <linux/module.h>
#include <linux/mtd/nand.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+static LIST_HEAD(on_host_hw_engines);
+static DEFINE_MUTEX(on_host_hw_engines_mutex);
/**
* nand_ecc_init_ctx - Init the ECC engine context
@@ -611,6 +617,119 @@ struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
}
EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
+int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
+{
+ struct nand_ecc_engine *item;
+
+ if (!engine)
+ return -EINVAL;
+
+ /* Prevent multiple registrations of one engine */
+ list_for_each_entry(item, &on_host_hw_engines, node)
+ if (item == engine)
+ return 0;
+
+ mutex_lock(&on_host_hw_engines_mutex);
+ list_add_tail(&engine->node, &on_host_hw_engines);
+ mutex_unlock(&on_host_hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine);
+
+int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
+{
+ if (!engine)
+ return -EINVAL;
+
+ mutex_lock(&on_host_hw_engines_mutex);
+ list_del(&engine->node);
+ mutex_unlock(&on_host_hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine);
+
+static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev)
+{
+ struct nand_ecc_engine *item;
+
+ list_for_each_entry(item, &on_host_hw_engines, node)
+ if (item->dev == dev)
+ return item;
+
+ return NULL;
+}
+
+struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand)
+{
+ struct nand_ecc_engine *engine = NULL;
+ struct device *dev = &nand->mtd.dev;
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ if (list_empty(&on_host_hw_engines))
+ return NULL;
+
+ /* Check for an explicit nand-ecc-engine property */
+ np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
+ if (np) {
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ engine = nand_ecc_match_on_host_hw_engine(&pdev->dev);
+ platform_device_put(pdev);
+ of_node_put(np);
+
+ if (!engine)
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ if (engine)
+ get_device(engine->dev);
+
+ return engine;
+}
+EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine);
+
+void nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
+{
+ put_device(nand->ecc.engine->dev);
+}
+EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
+
+/*
+ * In the case of a pipelined engine, the device registering the ECC
+ * engine is not necessarily the ECC engine itself but may be a host controller.
+ * It is then useful to provide a helper to retrieve the right device object
+ * which actually represents the ECC engine.
+ */
+struct device *nand_ecc_get_engine_dev(struct device *host)
+{
+ struct platform_device *ecc_pdev;
+ struct device_node *np;
+
+ /*
+ * If the device node contains this property, it means we need to follow
+ * it in order to get the right ECC engine device we are looking for.
+ */
+ np = of_parse_phandle(host->of_node, "nand-ecc-engine", 0);
+ if (!np)
+ return host;
+
+ ecc_pdev = of_find_device_by_node(np);
+ if (!ecc_pdev) {
+ of_node_put(np);
+ return NULL;
+ }
+
+ platform_device_put(ecc_pdev);
+ of_node_put(np);
+
+ return &ecc_pdev->dev;
+}
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Generic ECC engine");
diff --git a/drivers/mtd/nand/onenand/generic.c b/drivers/mtd/nand/onenand/generic.c
index 8b6f4da5d720..a4b8b65fe15f 100644
--- a/drivers/mtd/nand/onenand/generic.c
+++ b/drivers/mtd/nand/onenand/generic.c
@@ -53,7 +53,12 @@ static int generic_onenand_probe(struct platform_device *pdev)
}
info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
- info->onenand.irq = platform_get_irq(pdev, 0);
+
+ err = platform_get_irq(pdev, 0);
+ if (err < 0)
+ goto out_iounmap;
+
+ info->onenand.irq = err;
info->mtd.dev.parent = &pdev->dev;
info->mtd.priv = &info->onenand;
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index d986ab4e4c35..9b078e78f3fa 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -42,8 +42,7 @@ config MTD_NAND_OMAP2
tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
depends on HAS_IOMEM
- select MEMORY
- select OMAP_GPMC
+ depends on OMAP_GPMC
help
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
and Keystone platforms.
@@ -210,6 +209,19 @@ config MTD_NAND_BRCMNAND
originally designed for Set-Top Box but is used on various BCM7xxx,
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
+if MTD_NAND_BRCMNAND
+
+config MTD_NAND_BRCMNAND_BCMA
+ tristate "Broadcom BCMA NAND controller"
+ depends on BCMA_NFLASH
+ depends on BCMA
+ help
+ Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs.
+ The glue driver will take care of performing the low-level I/O
+ operations to interface the BRCMNAND controller over the BCMA bus.
+
+endif # MTD_NAND_BRCMNAND
+
config MTD_NAND_BCM47XXNFLASH
tristate "BCM4706 BCMA NAND controller"
depends on BCMA_NFLASH
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index f3276ee9e4fe..6ef14442c71a 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -1938,7 +1938,7 @@ static const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = {
.nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16,
};
-static const struct of_device_id atmel_ebi_csa_regmap_of_ids[] = {
+static const struct of_device_id __maybe_unused atmel_ebi_csa_regmap_of_ids[] = {
{
.compatible = "atmel,at91sam9260-matrix",
.data = &at91sam9260_ebi_csa,
@@ -2060,13 +2060,15 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
nc->mck = of_clk_get(dev->parent->of_node, 0);
if (IS_ERR(nc->mck)) {
dev_err(dev, "Failed to retrieve MCK clk\n");
- return PTR_ERR(nc->mck);
+ ret = PTR_ERR(nc->mck);
+ goto out_release_dma;
}
np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
if (!np) {
dev_err(dev, "Missing or invalid atmel,smc property\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_release_dma;
}
nc->smc = syscon_node_to_regmap(np);
@@ -2074,10 +2076,16 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (IS_ERR(nc->smc)) {
ret = PTR_ERR(nc->smc);
dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
- return ret;
+ goto out_release_dma;
}
return 0;
+
+out_release_dma:
+ if (nc->dmac)
+ dma_release_channel(nc->dmac);
+
+ return ret;
}
static int
@@ -2648,7 +2656,7 @@ static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
static struct platform_driver atmel_nand_controller_driver = {
.driver = {
.name = "atmel-nand-controller",
- .of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
+ .of_match_table = atmel_nand_controller_of_ids,
.pm = &atmel_nand_controller_pm_ops,
},
.probe = atmel_nand_controller_probe,
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
index 498e41ccabbd..4d7dc8a9c373 100644
--- a/drivers/mtd/nand/raw/atmel/pmecc.c
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -920,7 +920,7 @@ static struct atmel_pmecc_caps sama5d2_caps = {
.correct_erased_chunks = true,
};
-static const struct of_device_id atmel_pmecc_legacy_match[] = {
+static const struct of_device_id __maybe_unused atmel_pmecc_legacy_match[] = {
{ .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
{ .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
{ /* sentinel */ }
@@ -1003,7 +1003,7 @@ static int atmel_pmecc_probe(struct platform_device *pdev)
static struct platform_driver atmel_pmecc_driver = {
.driver = {
.name = "atmel-pmecc",
- .of_match_table = of_match_ptr(atmel_pmecc_match),
+ .of_match_table = atmel_pmecc_match,
},
.probe = atmel_pmecc_probe,
};
diff --git a/drivers/mtd/nand/raw/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile
index 195b845e48b8..16dc7254200e 100644
--- a/drivers/mtd/nand/raw/brcmnand/Makefile
+++ b/drivers/mtd/nand/raw/brcmnand/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
+
+obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMA) += bcma_nand.o
diff --git a/drivers/mtd/nand/raw/brcmnand/bcma_nand.c b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c
new file mode 100644
index 000000000000..dd27977919fb
--- /dev/null
+++ b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright © 2021 Broadcom
+ */
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_driver_chipcommon.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "brcmnand.h"
+
+struct brcmnand_bcma_soc {
+ struct brcmnand_soc soc;
+ struct bcma_drv_cc *cc;
+};
+
+static inline bool brcmnand_bcma_needs_swapping(u32 offset)
+{
+ switch (offset) {
+ case BCMA_CC_NAND_SPARE_RD0:
+ case BCMA_CC_NAND_SPARE_RD4:
+ case BCMA_CC_NAND_SPARE_RD8:
+ case BCMA_CC_NAND_SPARE_RD12:
+ case BCMA_CC_NAND_SPARE_WR0:
+ case BCMA_CC_NAND_SPARE_WR4:
+ case BCMA_CC_NAND_SPARE_WR8:
+ case BCMA_CC_NAND_SPARE_WR12:
+ case BCMA_CC_NAND_DEVID:
+ case BCMA_CC_NAND_DEVID_X:
+ case BCMA_CC_NAND_SPARE_RD16:
+ case BCMA_CC_NAND_SPARE_RD20:
+ case BCMA_CC_NAND_SPARE_RD24:
+ case BCMA_CC_NAND_SPARE_RD28:
+ return true;
+ }
+
+ return false;
+}
+
+static inline struct brcmnand_bcma_soc *to_bcma_soc(struct brcmnand_soc *soc)
+{
+ return container_of(soc, struct brcmnand_bcma_soc, soc);
+}
+
+static u32 brcmnand_bcma_read_reg(struct brcmnand_soc *soc, u32 offset)
+{
+ struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
+ u32 val;
+
+ /* Offset into the NAND block and deal with the flash cache separately */
+ if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
+ offset = BCMA_CC_NAND_CACHE_DATA;
+ else
+ offset += BCMA_CC_NAND_REVISION;
+
+ val = bcma_cc_read32(sc->cc, offset);
+
+ /* Swap if necessary */
+ if (brcmnand_bcma_needs_swapping(offset))
+ val = be32_to_cpu((__force __be32)val);
+ return val;
+}
+
+static void brcmnand_bcma_write_reg(struct brcmnand_soc *soc, u32 val,
+ u32 offset)
+{
+ struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
+
+ /* Offset into the NAND block */
+ if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
+ offset = BCMA_CC_NAND_CACHE_DATA;
+ else
+ offset += BCMA_CC_NAND_REVISION;
+
+ /* Swap if necessary */
+ if (brcmnand_bcma_needs_swapping(offset))
+ val = (__force u32)cpu_to_be32(val);
+
+ bcma_cc_write32(sc->cc, offset, val);
+}
+
+static struct brcmnand_io_ops brcmnand_bcma_io_ops = {
+ .read_reg = brcmnand_bcma_read_reg,
+ .write_reg = brcmnand_bcma_write_reg,
+};
+
+static void brcmnand_bcma_prepare_data_bus(struct brcmnand_soc *soc, bool prepare,
+ bool is_param)
+{
+ struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
+
+ /* Reset the cache address to ensure we are already accessing the
+ * beginning of a sub-page.
+ */
+ bcma_cc_write32(sc->cc, BCMA_CC_NAND_CACHE_ADDR, 0);
+}
+
+static int brcmnand_bcma_nand_probe(struct platform_device *pdev)
+{
+ struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
+ struct brcmnand_bcma_soc *soc;
+
+ soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
+ if (!soc)
+ return -ENOMEM;
+
+ soc->cc = container_of(nflash, struct bcma_drv_cc, nflash);
+ soc->soc.prepare_data_bus = brcmnand_bcma_prepare_data_bus;
+ soc->soc.ops = &brcmnand_bcma_io_ops;
+
+ if (soc->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
+ dev_err(&pdev->dev, "Use bcm47xxnflash for 4706!\n");
+ return -ENODEV;
+ }
+
+ return brcmnand_probe(pdev, &soc->soc);
+}
+
+static struct platform_driver brcmnand_bcma_nand_driver = {
+ .probe = brcmnand_bcma_nand_probe,
+ .remove = brcmnand_remove,
+ .driver = {
+ .name = "bcma_brcmnand",
+ .pm = &brcmnand_pm_ops,
+ }
+};
+module_platform_driver(brcmnand_bcma_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("NAND controller driver glue for BCMA chips");
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
index aee78f5f4f15..2e9c2e2d9c9f 100644
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/platform_data/brcmnand.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
@@ -25,6 +26,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
+#include <linux/static_key.h>
#include <linux/list.h>
#include <linux/log2.h>
@@ -207,13 +209,15 @@ enum {
struct brcmnand_host;
+static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);
+
struct brcmnand_controller {
struct device *dev;
struct nand_controller controller;
void __iomem *nand_base;
void __iomem *nand_fc; /* flash cache */
void __iomem *flash_dma_base;
- unsigned int irq;
+ int irq;
unsigned int dma_irq;
int nand_version;
@@ -592,15 +596,29 @@ enum {
INTFC_CTLR_READY = BIT(31),
};
+static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
+{
+#if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA)
+ return static_branch_unlikely(&brcmnand_soc_has_ops_key);
+#else
+ return false;
+#endif
+}
+
static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
{
+ if (brcmnand_non_mmio_ops(ctrl))
+ return brcmnand_soc_read(ctrl->soc, offs);
return brcmnand_readl(ctrl->nand_base + offs);
}
static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
u32 val)
{
- brcmnand_writel(val, ctrl->nand_base + offs);
+ if (brcmnand_non_mmio_ops(ctrl))
+ brcmnand_soc_write(ctrl->soc, val, offs);
+ else
+ brcmnand_writel(val, ctrl->nand_base + offs);
}
static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
@@ -766,13 +784,18 @@ static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
{
+ if (brcmnand_non_mmio_ops(ctrl))
+ return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR);
return __raw_readl(ctrl->nand_fc + word * 4);
}
static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
int word, u32 val)
{
- __raw_writel(val, ctrl->nand_fc + word * 4);
+ if (brcmnand_non_mmio_ops(ctrl))
+ brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR);
+ else
+ __raw_writel(val, ctrl->nand_fc + word * 4);
}
static inline void edu_writel(struct brcmnand_controller *ctrl,
@@ -897,6 +920,12 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
{
+ /* Kludge for the BCMA-based NAND controller which does not actually
+ * shift the command
+ */
+ if (ctrl->nand_version == 0x0304 && brcmnand_non_mmio_ops(ctrl))
+ return 0;
+
if (ctrl->nand_version < 0x0602)
return 24;
return 0;
@@ -1592,7 +1621,7 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip)
bool err = false;
int sts;
- if (mtd->oops_panic_write) {
+ if (mtd->oops_panic_write || ctrl->irq < 0) {
/* switch to interrupt polling and PIO mode */
disable_ctrl_irqs(ctrl);
sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY,
@@ -2750,33 +2779,27 @@ static const struct nand_controller_ops brcmnand_controller_ops = {
.attach_chip = brcmnand_attach_chip,
};
-static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
+static int brcmnand_init_cs(struct brcmnand_host *host,
+ const char * const *part_probe_types)
{
struct brcmnand_controller *ctrl = host->ctrl;
- struct platform_device *pdev = host->pdev;
+ struct device *dev = ctrl->dev;
struct mtd_info *mtd;
struct nand_chip *chip;
int ret;
u16 cfg_offs;
- ret = of_property_read_u32(dn, "reg", &host->cs);
- if (ret) {
- dev_err(&pdev->dev, "can't get chip-select\n");
- return -ENXIO;
- }
-
mtd = nand_to_mtd(&host->chip);
chip = &host->chip;
- nand_set_flash_node(chip, dn);
nand_set_controller_data(chip, host);
- mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
host->cs);
if (!mtd->name)
return -ENOMEM;
mtd->owner = THIS_MODULE;
- mtd->dev.parent = &pdev->dev;
+ mtd->dev.parent = dev;
chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
chip->legacy.cmdfunc = brcmnand_cmdfunc;
@@ -2810,7 +2833,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
if (ret)
return ret;
- ret = mtd_device_register(mtd, NULL, 0);
+ ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
if (ret)
nand_cleanup(chip);
@@ -2914,7 +2937,7 @@ const struct dev_pm_ops brcmnand_pm_ops = {
};
EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
-static const struct of_device_id brcmnand_of_match[] = {
+static const struct of_device_id __maybe_unused brcmnand_of_match[] = {
{ .compatible = "brcm,brcmnand-v2.1" },
{ .compatible = "brcm,brcmnand-v2.2" },
{ .compatible = "brcm,brcmnand-v4.0" },
@@ -2979,17 +3002,15 @@ static int brcmnand_edu_setup(struct platform_device *pdev)
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
{
+ struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
struct brcmnand_controller *ctrl;
+ struct brcmnand_host *host;
struct resource *res;
int ret;
- /* We only support device-tree instantiation */
- if (!dn)
- return -ENODEV;
-
- if (!of_match_node(brcmnand_of_match, dn))
+ if (dn && !of_match_node(brcmnand_of_match, dn))
return -ENODEV;
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
@@ -2998,6 +3019,13 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
dev_set_drvdata(dev, ctrl);
ctrl->dev = dev;
+ ctrl->soc = soc;
+
+ /* Enable the static key if the soc provides I/O operations indicating
+ * that a non-memory mapped IO access path must be used
+ */
+ if (brcmnand_soc_has_ops(ctrl->soc))
+ static_branch_enable(&brcmnand_soc_has_ops_key);
init_completion(&ctrl->done);
init_completion(&ctrl->dma_done);
@@ -3009,7 +3037,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
/* NAND register range */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctrl->nand_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->nand_base))
+ if (IS_ERR(ctrl->nand_base) && !brcmnand_soc_has_ops(soc))
return PTR_ERR(ctrl->nand_base);
/* Enable clock before using NAND registers */
@@ -3126,40 +3154,33 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
}
/* IRQ */
- ctrl->irq = platform_get_irq(pdev, 0);
- if ((int)ctrl->irq < 0) {
- dev_err(dev, "no IRQ defined\n");
- ret = -ENODEV;
- goto err;
- }
-
- /*
- * Some SoCs integrate this controller (e.g., its interrupt bits) in
- * interesting ways
- */
- if (soc) {
- ctrl->soc = soc;
-
- ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
- DRV_NAME, ctrl);
+ ctrl->irq = platform_get_irq_optional(pdev, 0);
+ if (ctrl->irq > 0) {
+ /*
+ * Some SoCs integrate this controller (e.g., its interrupt bits) in
+ * interesting ways
+ */
+ if (soc) {
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+ DRV_NAME, ctrl);
- /* Enable interrupt */
- ctrl->soc->ctlrdy_ack(ctrl->soc);
- ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
- } else {
- /* Use standard interrupt infrastructure */
- ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
- DRV_NAME, ctrl);
- }
- if (ret < 0) {
- dev_err(dev, "can't allocate IRQ %d: error %d\n",
- ctrl->irq, ret);
- goto err;
+ /* Enable interrupt */
+ ctrl->soc->ctlrdy_ack(ctrl->soc);
+ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+ } else {
+ /* Use standard interrupt infrastructure */
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+ DRV_NAME, ctrl);
+ }
+ if (ret < 0) {
+ dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ ctrl->irq, ret);
+ goto err;
+ }
}
for_each_available_child_of_node(dn, child) {
if (of_device_is_compatible(child, "brcm,nandcs")) {
- struct brcmnand_host *host;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
@@ -3170,7 +3191,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
host->pdev = pdev;
host->ctrl = ctrl;
- ret = brcmnand_init_cs(host, child);
+ ret = of_property_read_u32(child, "reg", &host->cs);
+ if (ret) {
+ dev_err(dev, "can't get chip-select\n");
+ devm_kfree(dev, host);
+ continue;
+ }
+
+ nand_set_flash_node(&host->chip, child);
+
+ ret = brcmnand_init_cs(host, NULL);
if (ret) {
devm_kfree(dev, host);
continue; /* Try all chip-selects */
@@ -3180,6 +3210,32 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
}
}
+ if (!list_empty(&ctrl->host_list))
+ return 0;
+
+ if (!pd) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* If we got there we must have been probing via platform data */
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ host->pdev = pdev;
+ host->ctrl = ctrl;
+ host->cs = pd->chip_select;
+ host->chip.ecc.size = pd->ecc_stepsize;
+ host->chip.ecc.strength = pd->ecc_strength;
+
+ ret = brcmnand_init_cs(host, pd->part_probe_types);
+ if (ret)
+ goto err;
+
+ list_add_tail(&host->node, &ctrl->host_list);
+
/* No chip-selects could initialize properly */
if (list_empty(&ctrl->host_list)) {
ret = -ENODEV;
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
index eb498fbe505e..f1f93d85f50d 100644
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.h
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
@@ -11,12 +11,25 @@
struct platform_device;
struct dev_pm_ops;
+struct brcmnand_io_ops;
+
+/* Special register offset constant to intercept a non-MMIO access
+ * to the flash cache register space. This is intentionally large
+ * not to overlap with an existing offset.
+ */
+#define BRCMNAND_NON_MMIO_FC_ADDR 0xffffffff
struct brcmnand_soc {
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
bool is_param);
+ const struct brcmnand_io_ops *ops;
+};
+
+struct brcmnand_io_ops {
+ u32 (*read_reg)(struct brcmnand_soc *soc, u32 offset);
+ void (*write_reg)(struct brcmnand_soc *soc, u32 val, u32 offset);
};
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
@@ -58,6 +71,22 @@ static inline void brcmnand_writel(u32 val, void __iomem *addr)
writel_relaxed(val, addr);
}
+static inline bool brcmnand_soc_has_ops(struct brcmnand_soc *soc)
+{
+ return soc && soc->ops && soc->ops->read_reg && soc->ops->write_reg;
+}
+
+static inline u32 brcmnand_soc_read(struct brcmnand_soc *soc, u32 offset)
+{
+ return soc->ops->read_reg(soc, offset);
+}
+
+static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val,
+ u32 offset)
+{
+ soc->ops->write_reg(soc, val, offset);
+}
+
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
int brcmnand_remove(struct platform_device *pdev);
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index ded4df473928..44b14c9dc9a7 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -644,10 +644,11 @@ err_out:
* RDN_DELAY = ----------------------- {3}
* RP
*/
-static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
- const struct nand_sdr_timings *sdr)
+static int gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
+ const struct nand_sdr_timings *sdr)
{
struct gpmi_nfc_hardware_timing *hw = &this->hw;
+ struct resources *r = &this->resources;
unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
unsigned int period_ps, reference_period_ps;
unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
@@ -656,21 +657,33 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
int sample_delay_ps, sample_delay_factor;
u16 busy_timeout_cycles;
u8 wrn_dly_sel;
+ unsigned long clk_rate, min_rate;
if (sdr->tRC_min >= 30000) {
/* ONFI non-EDO modes [0-3] */
hw->clk_rate = 22000000;
+ min_rate = 0;
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
} else if (sdr->tRC_min >= 25000) {
/* ONFI EDO mode 4 */
hw->clk_rate = 80000000;
+ min_rate = 22000000;
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
} else {
/* ONFI EDO mode 5 */
hw->clk_rate = 100000000;
+ min_rate = 80000000;
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
}
+ clk_rate = clk_round_rate(r->clock[0], hw->clk_rate);
+ if (clk_rate <= min_rate) {
+ dev_err(this->dev, "clock setting: expected %ld, got %ld\n",
+ hw->clk_rate, clk_rate);
+ return -ENOTSUPP;
+ }
+
+ hw->clk_rate = clk_rate;
/* SDR core timings are given in picoseconds */
period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
@@ -711,6 +724,7 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) |
BM_GPMI_CTRL1_DLL_ENABLE |
(use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
+ return 0;
}
static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
@@ -766,14 +780,15 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
{
struct gpmi_nand_data *this = nand_get_controller_data(chip);
const struct nand_sdr_timings *sdr;
+ int ret;
/* Retrieve required NAND timings */
sdr = nand_get_sdr_timings(conf);
if (IS_ERR(sdr))
return PTR_ERR(sdr);
- /* Only MX6 GPMI controller can reach EDO timings */
- if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this))
+ /* Only MX28/MX6 GPMI controller can reach EDO timings */
+ if (sdr->tRC_min <= 25000 && !GPMI_IS_MX28(this) && !GPMI_IS_MX6(this))
return -ENOTSUPP;
/* Stop here if this call was just a check */
@@ -781,7 +796,9 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
return 0;
/* Do the actual derivation of the controller timings */
- gpmi_nfc_compute_timings(this, sdr);
+ ret = gpmi_nfc_compute_timings(this, sdr);
+ if (ret)
+ return ret;
this->hw.must_apply_timings = true;
diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
index b18861bdcdc8..ff26c10f295d 100644
--- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
+++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
@@ -567,7 +567,7 @@ static struct platform_driver ingenic_nand_driver = {
.remove = ingenic_nand_remove,
.driver = {
.name = DRV_NAME,
- .of_match_table = of_match_ptr(ingenic_nand_dt_match),
+ .of_match_table = ingenic_nand_dt_match,
},
};
module_platform_driver(ingenic_nand_driver);
diff --git a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c
index d67dbfff76cc..12b5b0484fe9 100644
--- a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c
+++ b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c
@@ -260,7 +260,7 @@ static struct platform_driver jz4780_bch_driver = {
.probe = jz4780_bch_probe,
.driver = {
.name = "jz4780-bch",
- .of_match_table = of_match_ptr(jz4780_bch_dt_match),
+ .of_match_table = jz4780_bch_dt_match,
},
};
module_platform_driver(jz4780_bch_driver);
diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
index 1b47964cb6da..e7df3dac705e 100644
--- a/drivers/mtd/nand/raw/mtk_ecc.c
+++ b/drivers/mtd/nand/raw/mtk_ecc.c
@@ -579,7 +579,7 @@ static struct platform_driver mtk_ecc_driver = {
.probe = mtk_ecc_probe,
.driver = {
.name = "mtk-ecc",
- .of_match_table = of_match_ptr(mtk_ecc_dt_match),
+ .of_match_table = mtk_ecc_dt_match,
#ifdef CONFIG_PM_SLEEP
.pm = &mtk_ecc_pm_ops,
#endif
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index e7b2ba016d8c..284fff62ac49 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -321,7 +321,7 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
if (nand_region_is_secured(chip, ofs, mtd->erasesize))
return -EIO;
- if (WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning))
+ if (mtd_check_expert_analysis_mode())
return 0;
if (chip->legacy.block_bad)
@@ -338,16 +338,19 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
*
* Return: -EBUSY if the chip has been suspended, 0 otherwise
*/
-static int nand_get_device(struct nand_chip *chip)
+static void nand_get_device(struct nand_chip *chip)
{
- mutex_lock(&chip->lock);
- if (chip->suspended) {
+ /* Wait until the device is resumed. */
+ while (1) {
+ mutex_lock(&chip->lock);
+ if (!chip->suspended) {
+ mutex_lock(&chip->controller->lock);
+ return;
+ }
mutex_unlock(&chip->lock);
- return -EBUSY;
- }
- mutex_lock(&chip->controller->lock);
- return 0;
+ wait_event(chip->resume_wq, !chip->suspended);
+ }
}
/**
@@ -576,9 +579,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
nand_erase_nand(chip, &einfo, 0);
/* Write bad block marker to OOB */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
ret = nand_markbad_bbm(chip, ofs);
nand_release_device(chip);
@@ -3826,9 +3827,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
ops->mode != MTD_OPS_RAW)
return -ENOTSUPP;
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
if (!ops->datbuf)
ret = nand_do_read_oob(chip, from, ops);
@@ -4415,13 +4414,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
+ int ret = 0;
ops->retlen = 0;
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -4481,9 +4478,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
return -EIO;
/* Grab the lock and see if the device is available */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
/* Shift to get first page */
page = (int)(instr->addr >> chip->page_shift);
@@ -4570,7 +4565,7 @@ static void nand_sync(struct mtd_info *mtd)
pr_debug("%s: called\n", __func__);
/* Grab the lock and see if the device is available */
- WARN_ON(nand_get_device(chip));
+ nand_get_device(chip);
/* Release it and go back */
nand_release_device(chip);
}
@@ -4587,9 +4582,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
int ret;
/* Select the NAND device */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
nand_select_target(chip, chipnr);
@@ -4660,6 +4653,8 @@ static void nand_resume(struct mtd_info *mtd)
__func__);
}
mutex_unlock(&chip->lock);
+
+ wake_up_all(&chip->resume_wq);
}
/**
@@ -5274,25 +5269,24 @@ static void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip)
user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn);
}
-static int of_get_nand_bus_width(struct device_node *np)
+static int of_get_nand_bus_width(struct nand_chip *chip)
{
+ struct device_node *dn = nand_get_flash_node(chip);
u32 val;
+ int ret;
- if (of_property_read_u32(np, "nand-bus-width", &val))
- return 8;
-
- switch (val) {
- case 8:
- case 16:
- return val;
- default:
- return -EIO;
- }
-}
+ ret = of_property_read_u32(dn, "nand-bus-width", &val);
+ if (ret == -EINVAL)
+ /* Buswidth defaults to 8 if the property does not exist .*/
+ return 0;
+ else if (ret)
+ return ret;
-static bool of_get_nand_on_flash_bbt(struct device_node *np)
-{
- return of_property_read_bool(np, "nand-on-flash-bbt");
+ if (val == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+ else if (val != 8)
+ return -EINVAL;
+ return 0;
}
static int of_get_nand_secure_regions(struct nand_chip *chip)
@@ -5368,17 +5362,19 @@ static int rawnand_dt_init(struct nand_chip *chip)
{
struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
struct device_node *dn = nand_get_flash_node(chip);
+ int ret;
if (!dn)
return 0;
- if (of_get_nand_bus_width(dn) == 16)
- chip->options |= NAND_BUSWIDTH_16;
+ ret = of_get_nand_bus_width(chip);
+ if (ret)
+ return ret;
if (of_property_read_bool(dn, "nand-is-boot-medium"))
chip->options |= NAND_IS_BOOT_MEDIUM;
- if (of_get_nand_on_flash_bbt(dn))
+ if (of_property_read_bool(dn, "nand-on-flash-bbt"))
chip->bbt_options |= NAND_BBT_USE_FLASH;
of_get_nand_ecc_user_config(nand);
@@ -5437,6 +5433,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
chip->cur_cs = -1;
mutex_init(&chip->lock);
+ init_waitqueue_head(&chip->resume_wq);
/* Enforce the right timings for reset/detection */
chip->current_interface_config = nand_get_reset_interface_config();
diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
index ab630af3a309..a3723da2e0a0 100644
--- a/drivers/mtd/nand/raw/nand_bbt.c
+++ b/drivers/mtd/nand/raw/nand_bbt.c
@@ -1455,7 +1455,7 @@ int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
(unsigned int)offs, block, res);
- if (WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning))
+ if (mtd_check_expert_analysis_mode())
return 0;
switch (res) {
diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
index 0750121ac371..24beade95c7f 100644
--- a/drivers/mtd/nand/raw/nandsim.c
+++ b/drivers/mtd/nand/raw/nandsim.c
@@ -201,6 +201,9 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
/* Calculate the OOB offset in flash RAM image by (row, column) address */
#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
+/* Calculate the byte shift in the next page to access */
+#define NS_PAGE_BYTE_SHIFT(ns) ((ns)->regs.column + (ns)->regs.off)
+
/* After a command is input, the simulator goes to one of the following states */
#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */
#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */
@@ -979,15 +982,8 @@ static int ns_read_error(unsigned int page_no)
static int ns_setup_wear_reporting(struct mtd_info *mtd)
{
- size_t mem;
-
wear_eb_count = div_u64(mtd->size, mtd->erasesize);
- mem = wear_eb_count * sizeof(unsigned long);
- if (mem / sizeof(unsigned long) != wear_eb_count) {
- NS_ERR("Too many erase blocks for wear reporting\n");
- return -ENOMEM;
- }
- erase_block_wear = kzalloc(mem, GFP_KERNEL);
+ erase_block_wear = kcalloc(wear_eb_count, sizeof(unsigned long), GFP_KERNEL);
if (!erase_block_wear) {
NS_ERR("Too many erase blocks for wear reporting\n");
return -ENOMEM;
@@ -1389,7 +1385,7 @@ static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
*/
static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
{
- return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
+ return NS_GET_PAGE(ns)->byte + NS_PAGE_BYTE_SHIFT(ns);
}
static int ns_do_read_error(struct nandsim *ns, int num)
@@ -1415,7 +1411,7 @@ static void ns_do_bit_flips(struct nandsim *ns, int num)
ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
NS_WARN("read_page: flipping bit %d in page %d "
"reading from %d ecc: corrected=%u failed=%u\n",
- pos, ns->regs.row, ns->regs.column + ns->regs.off,
+ pos, ns->regs.row, NS_PAGE_BYTE_SHIFT(ns),
nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
}
}
@@ -1437,7 +1433,7 @@ static void ns_read_page(struct nandsim *ns, int num)
ssize_t tx;
NS_DBG("read_page: page %d written, reading from %d\n",
- ns->regs.row, ns->regs.column + ns->regs.off);
+ ns->regs.row, NS_PAGE_BYTE_SHIFT(ns));
if (ns_do_read_error(ns, num))
return;
pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
@@ -1458,7 +1454,7 @@ static void ns_read_page(struct nandsim *ns, int num)
memset(ns->buf.byte, 0xFF, num);
} else {
NS_DBG("read_page: page %d allocated, reading from %d\n",
- ns->regs.row, ns->regs.column + ns->regs.off);
+ ns->regs.row, NS_PAGE_BYTE_SHIFT(ns));
if (ns_do_read_error(ns, num))
return;
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
@@ -1509,7 +1505,7 @@ static int ns_prog_page(struct nandsim *ns, int num)
int all;
NS_DBG("prog_page: writing page %d\n", ns->regs.row);
- pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
+ pg_off = ns->file_buf + NS_PAGE_BYTE_SHIFT(ns);
off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
if (!test_bit(ns->regs.row, ns->pages_written)) {
all = 1;
@@ -1598,7 +1594,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action)
NS_ERR("do_state_action: column number is too large\n");
break;
}
- num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
ns_read_page(ns, num);
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
@@ -1666,7 +1662,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action)
return -1;
}
- num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
if (num != ns->regs.count) {
NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
ns->regs.count, num);
@@ -1738,14 +1734,6 @@ static void ns_switch_state(struct nandsim *ns)
"state: %s, nxstate: %s\n",
ns_get_state_name(ns->state),
ns_get_state_name(ns->nxstate));
-
- /* See, whether we need to do some action */
- if ((ns->state & ACTION_MASK) &&
- ns_do_state_action(ns, ns->state) < 0) {
- ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
} else {
/*
* We don't yet know which operation we perform.
@@ -1762,12 +1750,13 @@ static void ns_switch_state(struct nandsim *ns)
if (ns_find_operation(ns, 0))
return;
+ }
- if ((ns->state & ACTION_MASK) &&
- ns_do_state_action(ns, ns->state) < 0) {
- ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
+ /* See, whether we need to do some action */
+ if ((ns->state & ACTION_MASK) &&
+ ns_do_state_action(ns, ns->state) < 0) {
+ ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
}
/* For 16x devices column means the page offset in words */
@@ -1817,7 +1806,7 @@ static void ns_switch_state(struct nandsim *ns)
switch (NS_STATE(ns->state)) {
case STATE_DATAIN:
case STATE_DATAOUT:
- ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ ns->regs.num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
break;
case STATE_DATAOUT_ID:
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index f0bbbe401e76..58c32a11792e 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -2298,7 +2298,7 @@ static struct platform_driver omap_nand_driver = {
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
- .of_match_table = of_match_ptr(omap_nand_ids),
+ .of_match_table = omap_nand_ids,
},
};
diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
index db105d9b560c..893e9979c4a2 100644
--- a/drivers/mtd/nand/raw/omap_elm.c
+++ b/drivers/mtd/nand/raw/omap_elm.c
@@ -282,7 +282,7 @@ static void elm_start_processing(struct elm_info *info,
static void elm_error_correction(struct elm_info *info,
struct elm_errorvec *err_vec)
{
- int i, j, errors = 0;
+ int i, j;
int offset;
u32 reg_val;
@@ -312,8 +312,6 @@ static void elm_error_correction(struct elm_info *info,
/* Update error location register */
offset += 4;
}
-
- errors += err_vec[i].error_count;
} else {
err_vec[i].error_uncorrectable = true;
}
diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c
index 8a91e069ee2e..3c6f6aff649f 100644
--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
+++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
@@ -1062,7 +1062,7 @@ static int pl35x_nand_chip_init(struct pl35x_nandc *nfc,
chip->controller = &nfc->controller;
mtd = nand_to_mtd(chip);
mtd->dev.parent = nfc->dev;
- nand_set_flash_node(chip, nfc->dev->of_node);
+ nand_set_flash_node(chip, np);
if (!mtd->name) {
mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
"%s", PL35X_NANDC_DRIVER_NAME);
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
index 428e08362956..6db063b230a9 100644
--- a/drivers/mtd/nand/raw/renesas-nand-controller.c
+++ b/drivers/mtd/nand/raw/renesas-nand-controller.c
@@ -1412,7 +1412,7 @@ MODULE_DEVICE_TABLE(of, rnandc_id_table);
static struct platform_driver rnandc_driver = {
.driver = {
.name = "renesas-nandc",
- .of_match_table = of_match_ptr(rnandc_id_table),
+ .of_match_table = rnandc_id_table,
},
.probe = rnandc_probe,
.remove = rnandc_remove,
diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c
index b5405bc7ca3a..cbaa4f1c83da 100644
--- a/drivers/mtd/nand/raw/rockchip-nand-controller.c
+++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c
@@ -1403,7 +1403,6 @@ static int rk_nfc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_err(dev, "no NFC irq resource\n");
ret = -EINVAL;
goto clk_disable;
}
diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
index 13df4bdf792a..b85b9c6fcc42 100644
--- a/drivers/mtd/nand/raw/sh_flctl.c
+++ b/drivers/mtd/nand/raw/sh_flctl.c
@@ -1220,7 +1220,7 @@ static struct platform_driver flctl_driver = {
.remove = flctl_remove,
.driver = {
.name = "sh_flctl",
- .of_match_table = of_match_ptr(of_flctl_match),
+ .of_match_table = of_flctl_match,
},
};
diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
index 5612ee628425..52ce5162538a 100644
--- a/drivers/mtd/nand/raw/sharpsl.c
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -6,7 +6,6 @@
* Based on Sharp's NAND driver sharp_sl.c
*/
-#include <linux/genhd.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
index 97b4e02e43e4..87c1c7dd97eb 100644
--- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -9,6 +9,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
@@ -231,6 +232,7 @@ struct stm32_fmc2_timings {
struct stm32_fmc2_nand {
struct nand_chip chip;
+ struct gpio_desc *wp_gpio;
struct stm32_fmc2_timings timings;
int ncs;
int cs_used[FMC2_MAX_CE];
@@ -1747,6 +1749,18 @@ static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
.setup_interface = stm32_fmc2_nfc_setup_interface,
};
+static void stm32_fmc2_nfc_wp_enable(struct stm32_fmc2_nand *nand)
+{
+ if (nand->wp_gpio)
+ gpiod_set_value(nand->wp_gpio, 1);
+}
+
+static void stm32_fmc2_nfc_wp_disable(struct stm32_fmc2_nand *nand)
+{
+ if (nand->wp_gpio)
+ gpiod_set_value(nand->wp_gpio, 0);
+}
+
static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
struct device_node *dn)
{
@@ -1785,6 +1799,18 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
nand->cs_used[i] = cs;
}
+ nand->wp_gpio = devm_gpiod_get_from_of_node(nfc->dev, dn,
+ "wp-gpios", 0,
+ GPIOD_OUT_HIGH, "wp");
+ if (IS_ERR(nand->wp_gpio)) {
+ ret = PTR_ERR(nand->wp_gpio);
+ if (ret != -ENOENT)
+ return dev_err_probe(nfc->dev, ret,
+ "failed to request WP GPIO\n");
+
+ nand->wp_gpio = NULL;
+ }
+
nand_set_flash_node(&nand->chip, dn);
return 0;
@@ -1956,10 +1982,12 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE |
NAND_USES_DMA;
+ stm32_fmc2_nfc_wp_disable(nand);
+
/* Scan to find existence of the device */
ret = nand_scan(chip, nand->ncs);
if (ret)
- goto err_release_dma;
+ goto err_wp_enable;
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
@@ -1972,6 +2000,9 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
err_nand_cleanup:
nand_cleanup(chip);
+err_wp_enable:
+ stm32_fmc2_nfc_wp_enable(nand);
+
err_release_dma:
if (nfc->dma_ecc_ch)
dma_release_channel(nfc->dma_ecc_ch);
@@ -2012,15 +2043,20 @@ static int stm32_fmc2_nfc_remove(struct platform_device *pdev)
clk_disable_unprepare(nfc->clk);
+ stm32_fmc2_nfc_wp_enable(nand);
+
return 0;
}
static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev)
{
struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev);
+ struct stm32_fmc2_nand *nand = &nfc->nand;
clk_disable_unprepare(nfc->clk);
+ stm32_fmc2_nfc_wp_enable(nand);
+
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -2042,6 +2078,8 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
stm32_fmc2_nfc_init(nfc);
+ stm32_fmc2_nfc_wp_disable(nand);
+
for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) {
if (!(nfc->cs_assigned & BIT(chip_cs)))
continue;
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 2c8685f1f2fa..ff8336870bc0 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -381,7 +381,10 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
}
}
- rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ if (req->mode == MTD_OPS_RAW)
+ rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ else
+ rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
while (nbytes) {
ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf);
@@ -452,7 +455,10 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
req->ooblen);
}
- wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+ if (req->mode == MTD_OPS_RAW)
+ wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+ else
+ wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
while (nbytes) {
ret = spi_mem_dirmap_write(wdesc, column, nbytes, buf);
@@ -865,6 +871,31 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].rdesc = desc;
+ if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) {
+ spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
+ spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
+
+ return 0;
+ }
+
+ info.op_tmpl = *spinand->op_templates.update_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ spinand->dirmaps[plane].wdesc_ecc = desc;
+
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ spinand->dirmaps[plane].rdesc_ecc = desc;
+
return 0;
}
@@ -1208,14 +1239,6 @@ static int spinand_init(struct spinand_device *spinand)
if (ret)
goto err_free_bufs;
- ret = spinand_create_dirmaps(spinand);
- if (ret) {
- dev_err(dev,
- "Failed to create direct mappings for read/write operations (err = %d)\n",
- ret);
- goto err_manuf_cleanup;
- }
-
ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
if (ret)
goto err_manuf_cleanup;
@@ -1250,6 +1273,14 @@ static int spinand_init(struct spinand_device *spinand)
mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength;
mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size;
+ ret = spinand_create_dirmaps(spinand);
+ if (ret) {
+ dev_err(dev,
+ "Failed to create direct mappings for read/write operations (err = %d)\n",
+ ret);
+ goto err_cleanup_ecc_engine;
+ }
+
return 0;
err_cleanup_ecc_engine:
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index 3f31f1381a62..dce835132a1e 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -20,7 +20,7 @@ static SPINAND_OP_VARIANTS(read_cache_variants,
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
- SPINAND_PROG_LOAD(true, 0, NULL, 0));
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
index 337ea8b9a4c3..23763d16e4f9 100644
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -115,7 +115,7 @@ config MTD_AFS_PARTS
config MTD_PARSER_TRX
tristate "Parser for TRX format partitions"
- depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || COMPILE_TEST)
+ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
help
TRX is a firmware format used by Broadcom on their devices. It
may contain up to 3/4 partitions (depending on the version).
diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c
index d6d889ce8876..656dd80a0be7 100644
--- a/drivers/mtd/spi-nor/atmel.c
+++ b/drivers/mtd/spi-nor/atmel.c
@@ -16,12 +16,12 @@
* is to unlock the whole flash array on startup. Therefore, we have to support
* exactly this operation.
*/
-static int atmel_at25fs_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
return -EOPNOTSUPP;
}
-static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
int ret;
@@ -37,28 +37,28 @@ static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return ret;
}
-static int atmel_at25fs_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
return -EOPNOTSUPP;
}
-static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = {
- .lock = atmel_at25fs_lock,
- .unlock = atmel_at25fs_unlock,
- .is_locked = atmel_at25fs_is_locked,
+static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
+ .lock = at25fs_nor_lock,
+ .unlock = at25fs_nor_unlock,
+ .is_locked = at25fs_nor_is_locked,
};
-static void atmel_at25fs_late_init(struct spi_nor *nor)
+static void at25fs_nor_late_init(struct spi_nor *nor)
{
- nor->params->locking_ops = &atmel_at25fs_locking_ops;
+ nor->params->locking_ops = &at25fs_nor_locking_ops;
}
-static const struct spi_nor_fixups atmel_at25fs_fixups = {
- .late_init = atmel_at25fs_late_init,
+static const struct spi_nor_fixups at25fs_nor_fixups = {
+ .late_init = at25fs_nor_late_init,
};
/**
- * atmel_set_global_protection - Do a Global Protect or Unprotect command
+ * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command
* @nor: pointer to 'struct spi_nor'
* @ofs: offset in bytes
* @len: len in bytes
@@ -66,8 +66,8 @@ static const struct spi_nor_fixups atmel_at25fs_fixups = {
*
* Return: 0 on success, -error otherwise.
*/
-static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs,
- uint64_t len, bool is_protect)
+static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
+ uint64_t len, bool is_protect)
{
int ret;
u8 sr;
@@ -116,17 +116,20 @@ static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs,
return spi_nor_write_sr(nor, nor->bouncebuf, 1);
}
-static int atmel_global_protect(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs,
+ uint64_t len)
{
- return atmel_set_global_protection(nor, ofs, len, true);
+ return atmel_nor_set_global_protection(nor, ofs, len, true);
}
-static int atmel_global_unprotect(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs,
+ uint64_t len)
{
- return atmel_set_global_protection(nor, ofs, len, false);
+ return atmel_nor_set_global_protection(nor, ofs, len, false);
}
-static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
+ uint64_t len)
{
int ret;
@@ -140,47 +143,47 @@ static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t l
return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
}
-static const struct spi_nor_locking_ops atmel_global_protection_ops = {
- .lock = atmel_global_protect,
- .unlock = atmel_global_unprotect,
- .is_locked = atmel_is_global_protected,
+static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
+ .lock = atmel_nor_global_protect,
+ .unlock = atmel_nor_global_unprotect,
+ .is_locked = atmel_nor_is_global_protected,
};
-static void atmel_global_protection_late_init(struct spi_nor *nor)
+static void atmel_nor_global_protection_late_init(struct spi_nor *nor)
{
- nor->params->locking_ops = &atmel_global_protection_ops;
+ nor->params->locking_ops = &atmel_nor_global_protection_ops;
}
-static const struct spi_nor_fixups atmel_global_protection_fixups = {
- .late_init = atmel_global_protection_late_init,
+static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {
+ .late_init = atmel_nor_global_protection_late_init,
};
-static const struct flash_info atmel_parts[] = {
+static const struct flash_info atmel_nor_parts[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4)
FLAGS(SPI_NOR_HAS_LOCK)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_at25fs_fixups },
+ .fixups = &at25fs_nor_fixups },
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8)
FLAGS(SPI_NOR_HAS_LOCK)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_at25fs_fixups },
+ .fixups = &at25fs_nor_fixups },
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8)
@@ -188,21 +191,21 @@ static const struct flash_info atmel_parts[] = {
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K)
- .fixups = &atmel_global_protection_fixups },
+ .fixups = &atmel_nor_global_protection_fixups },
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K) },
};
const struct spi_nor_manufacturer spi_nor_atmel = {
.name = "atmel",
- .parts = atmel_parts,
- .nparts = ARRAY_SIZE(atmel_parts),
+ .parts = atmel_nor_parts,
+ .nparts = ARRAY_SIZE(atmel_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/catalyst.c b/drivers/mtd/spi-nor/catalyst.c
index ae4d67e01bb3..6d310815fb12 100644
--- a/drivers/mtd/spi-nor/catalyst.c
+++ b/drivers/mtd/spi-nor/catalyst.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info catalyst_parts[] = {
+static const struct flash_info catalyst_nor_parts[] = {
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO(16, 8, 16, 1) },
{ "cat25c03", CAT25_INFO(32, 8, 16, 2) },
@@ -19,6 +19,6 @@ static const struct flash_info catalyst_parts[] = {
const struct spi_nor_manufacturer spi_nor_catalyst = {
.name = "catalyst",
- .parts = catalyst_parts,
- .nparts = ARRAY_SIZE(catalyst_parts),
+ .parts = catalyst_nor_parts,
+ .nparts = ARRAY_SIZE(catalyst_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/controllers/Kconfig b/drivers/mtd/spi-nor/controllers/Kconfig
index 5c0e0ec2e6d1..50f4f3484d42 100644
--- a/drivers/mtd/spi-nor/controllers/Kconfig
+++ b/drivers/mtd/spi-nor/controllers/Kconfig
@@ -26,39 +26,3 @@ config SPI_NXP_SPIFI
SPIFI is a specialized controller for connecting serial SPI
Flash. Enable this option if you have a device with a SPIFI
controller and want to access the Flash as a mtd device.
-
-config SPI_INTEL_SPI
- tristate
-
-config SPI_INTEL_SPI_PCI
- tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
- 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 (DANGEROUS)"
- depends on X86
- select SPI_INTEL_SPI
- help
- This enables platform 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-platform.
diff --git a/drivers/mtd/spi-nor/controllers/Makefile b/drivers/mtd/spi-nor/controllers/Makefile
index e7abba491d98..6e2a1dc68466 100644
--- a/drivers/mtd/spi-nor/controllers/Makefile
+++ b/drivers/mtd/spi-nor/controllers/Makefile
@@ -2,6 +2,3 @@
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.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
diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c
index 7225870e8b18..acfe010f9dd7 100644
--- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c
@@ -769,6 +769,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
struct device_node *child;
unsigned int cs;
int ret = -ENODEV;
+ bool found_one = false;
for_each_available_child_of_node(np, child) {
struct aspeed_smc_chip *chip;
@@ -827,8 +828,17 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
* by of property.
*/
ret = spi_nor_scan(nor, NULL, &hwcaps);
- if (ret)
- break;
+ /*
+ * If we fail to scan the device it might not be present or
+ * broken. Don't fail the whole controller if others work.
+ */
+ if (ret) {
+ if (found_one)
+ ret = 0;
+
+ devm_kfree(controller->dev, chip);
+ continue;
+ }
ret = aspeed_smc_chip_setup_finish(chip);
if (ret)
@@ -839,6 +849,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
break;
controller->chips[cs] = chip;
+ found_one = true;
}
if (ret) {
diff --git a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c
deleted file mode 100644
index 1bc53b8bb88a..000000000000
--- a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Intel PCH/PCU SPI flash PCI driver.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
- */
-
-#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 const struct intel_spi_boardinfo cnl_info = {
- .type = INTEL_SPI_CNL,
-};
-
-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, 0x02a4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
- { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa3a4), (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/controllers/intel-spi-platform.c b/drivers/mtd/spi-nor/controllers/intel-spi-platform.c
deleted file mode 100644
index f80f1086f928..000000000000
--- a/drivers/mtd/spi-nor/controllers/intel-spi-platform.c
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Intel PCH/PCU SPI flash platform driver.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
- */
-
-#include <linux/ioport.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include "intel-spi.h"
-
-static int intel_spi_platform_probe(struct platform_device *pdev)
-{
- struct intel_spi_boardinfo *info;
- struct intel_spi *ispi;
- struct resource *mem;
-
- info = dev_get_platdata(&pdev->dev);
- if (!info)
- return -EINVAL;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ispi = intel_spi_probe(&pdev->dev, mem, info);
- if (IS_ERR(ispi))
- return PTR_ERR(ispi);
-
- platform_set_drvdata(pdev, ispi);
- return 0;
-}
-
-static int intel_spi_platform_remove(struct platform_device *pdev)
-{
- struct intel_spi *ispi = platform_get_drvdata(pdev);
-
- return intel_spi_remove(ispi);
-}
-
-static struct platform_driver intel_spi_platform_driver = {
- .probe = intel_spi_platform_probe,
- .remove = intel_spi_platform_remove,
- .driver = {
- .name = "intel-spi",
- },
-};
-
-module_platform_driver(intel_spi_platform_driver);
-
-MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:intel-spi");
diff --git a/drivers/mtd/spi-nor/controllers/intel-spi.c b/drivers/mtd/spi-nor/controllers/intel-spi.c
deleted file mode 100644
index a413892ff449..000000000000
--- a/drivers/mtd/spi-nor/controllers/intel-spi.c
+++ /dev/null
@@ -1,968 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Intel PCH/PCU SPI flash driver.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
- */
-
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/sizes.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/spi-nor.h>
-
-#include "intel-spi.h"
-
-/* Offsets are from @ispi->base */
-#define BFPREG 0x00
-
-#define HSFSTS_CTL 0x04
-#define HSFSTS_CTL_FSMIE BIT(31)
-#define HSFSTS_CTL_FDBC_SHIFT 24
-#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT)
-
-#define HSFSTS_CTL_FCYCLE_SHIFT 17
-#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
-/* HW sequencer opcodes */
-#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
-
-#define HSFSTS_CTL_FGO BIT(16)
-#define HSFSTS_CTL_FLOCKDN BIT(15)
-#define HSFSTS_CTL_FDV BIT(14)
-#define HSFSTS_CTL_SCIP BIT(5)
-#define HSFSTS_CTL_AEL BIT(2)
-#define HSFSTS_CTL_FCERR BIT(1)
-#define HSFSTS_CTL_FDONE BIT(0)
-
-#define FADDR 0x08
-#define DLOCK 0x0c
-#define FDATA(n) (0x10 + ((n) * 4))
-
-#define FRACC 0x50
-
-#define FREG(n) (0x54 + ((n) * 4))
-#define FREG_BASE_MASK 0x3fff
-#define FREG_LIMIT_SHIFT 16
-#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT)
-
-/* Offset is from @ispi->pregs */
-#define PR(n) ((n) * 4)
-#define PR_WPE BIT(31)
-#define PR_LIMIT_SHIFT 16
-#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT)
-#define PR_RPE BIT(15)
-#define PR_BASE_MASK 0x3fff
-
-/* Offsets are from @ispi->sregs */
-#define SSFSTS_CTL 0x00
-#define SSFSTS_CTL_FSMIE BIT(23)
-#define SSFSTS_CTL_DS BIT(22)
-#define SSFSTS_CTL_DBC_SHIFT 16
-#define SSFSTS_CTL_SPOP BIT(11)
-#define SSFSTS_CTL_ACS BIT(10)
-#define SSFSTS_CTL_SCGO BIT(9)
-#define SSFSTS_CTL_COP_SHIFT 12
-#define SSFSTS_CTL_FRS BIT(7)
-#define SSFSTS_CTL_DOFRS BIT(6)
-#define SSFSTS_CTL_AEL BIT(4)
-#define SSFSTS_CTL_FCERR BIT(3)
-#define SSFSTS_CTL_FDONE BIT(2)
-#define SSFSTS_CTL_SCIP BIT(0)
-
-#define PREOP_OPTYPE 0x04
-#define OPMENU0 0x08
-#define OPMENU1 0x0c
-
-#define OPTYPE_READ_NO_ADDR 0
-#define OPTYPE_WRITE_NO_ADDR 1
-#define OPTYPE_READ_WITH_ADDR 2
-#define OPTYPE_WRITE_WITH_ADDR 3
-
-/* CPU specifics */
-#define BYT_PR 0x74
-#define BYT_SSFSTS_CTL 0x90
-#define BYT_BCR 0xfc
-#define BYT_BCR_WPD BIT(0)
-#define BYT_FREG_NUM 5
-#define BYT_PR_NUM 5
-
-#define LPT_PR 0x74
-#define LPT_SSFSTS_CTL 0x90
-#define LPT_FREG_NUM 5
-#define LPT_PR_NUM 5
-
-#define BXT_PR 0x84
-#define BXT_SSFSTS_CTL 0xa0
-#define BXT_FREG_NUM 12
-#define BXT_PR_NUM 6
-
-#define CNL_PR 0x84
-#define CNL_FREG_NUM 6
-#define CNL_PR_NUM 5
-
-#define LVSCC 0xc4
-#define UVSCC 0xc8
-#define ERASE_OPCODE_SHIFT 8
-#define ERASE_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
-#define ERASE_64K_OPCODE_SHIFT 16
-#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
-
-#define INTEL_SPI_TIMEOUT 5000 /* ms */
-#define INTEL_SPI_FIFO_SZ 64
-
-/**
- * struct intel_spi - Driver private data
- * @dev: Device pointer
- * @info: Pointer to board specific info
- * @nor: SPI NOR layer structure
- * @base: Beginning of MMIO space
- * @pregs: Start of protection registers
- * @sregs: Start of software sequencer registers
- * @nregions: Maximum number of regions
- * @pr_num: Maximum number of protected range registers
- * @writeable: Is the chip writeable
- * @locked: Is SPI setting locked
- * @swseq_reg: Use SW sequencer in register reads/writes
- * @swseq_erase: Use SW sequencer in erase operation
- * @erase_64k: 64k erase supported
- * @atomic_preopcode: Holds preopcode when atomic sequence is requested
- * @opcodes: Opcodes which are supported. This are programmed by BIOS
- * before it locks down the controller.
- */
-struct intel_spi {
- struct device *dev;
- const struct intel_spi_boardinfo *info;
- struct spi_nor nor;
- void __iomem *base;
- void __iomem *pregs;
- void __iomem *sregs;
- size_t nregions;
- size_t pr_num;
- bool writeable;
- bool locked;
- bool swseq_reg;
- bool swseq_erase;
- bool erase_64k;
- u8 atomic_preopcode;
- u8 opcodes[8];
-};
-
-static bool writeable;
-module_param(writeable, bool, 0);
-MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
-
-static void intel_spi_dump_regs(struct intel_spi *ispi)
-{
- u32 value;
- int i;
-
- dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG));
-
- value = readl(ispi->base + HSFSTS_CTL);
- dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value);
- if (value & HSFSTS_CTL_FLOCKDN)
- dev_dbg(ispi->dev, "-> Locked\n");
-
- dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR));
- dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK));
-
- for (i = 0; i < 16; i++)
- dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n",
- i, readl(ispi->base + FDATA(i)));
-
- dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC));
-
- for (i = 0; i < ispi->nregions; i++)
- dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,
- readl(ispi->base + FREG(i)));
- for (i = 0; i < ispi->pr_num; i++)
- dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,
- readl(ispi->pregs + PR(i)));
-
- if (ispi->sregs) {
- value = readl(ispi->sregs + SSFSTS_CTL);
- dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value);
- dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n",
- readl(ispi->sregs + PREOP_OPTYPE));
- dev_dbg(ispi->dev, "OPMENU0=0x%08x\n",
- readl(ispi->sregs + OPMENU0));
- dev_dbg(ispi->dev, "OPMENU1=0x%08x\n",
- readl(ispi->sregs + OPMENU1));
- }
-
- if (ispi->info->type == INTEL_SPI_BYT)
- dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR));
-
- dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC));
- dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC));
-
- dev_dbg(ispi->dev, "Protected regions:\n");
- for (i = 0; i < ispi->pr_num; i++) {
- u32 base, limit;
-
- value = readl(ispi->pregs + PR(i));
- if (!(value & (PR_WPE | PR_RPE)))
- continue;
-
- limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
- base = value & PR_BASE_MASK;
-
- dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n",
- i, base << 12, (limit << 12) | 0xfff,
- value & PR_WPE ? 'W' : '.',
- value & PR_RPE ? 'R' : '.');
- }
-
- dev_dbg(ispi->dev, "Flash regions:\n");
- for (i = 0; i < ispi->nregions; i++) {
- u32 region, base, limit;
-
- region = readl(ispi->base + FREG(i));
- base = region & FREG_BASE_MASK;
- limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
-
- if (base >= limit || (i > 0 && limit == 0))
- dev_dbg(ispi->dev, " %02d disabled\n", i);
- else
- dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n",
- i, base << 12, (limit << 12) | 0xfff);
- }
-
- dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
- ispi->swseq_reg ? 'S' : 'H');
- dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n",
- ispi->swseq_erase ? 'S' : 'H');
-}
-
-/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
-static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size)
-{
- size_t bytes;
- int i = 0;
-
- if (size > INTEL_SPI_FIFO_SZ)
- return -EINVAL;
-
- while (size > 0) {
- bytes = min_t(size_t, size, 4);
- memcpy_fromio(buf, ispi->base + FDATA(i), bytes);
- size -= bytes;
- buf += bytes;
- i++;
- }
-
- return 0;
-}
-
-/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */
-static int intel_spi_write_block(struct intel_spi *ispi, const void *buf,
- size_t size)
-{
- size_t bytes;
- int i = 0;
-
- if (size > INTEL_SPI_FIFO_SZ)
- return -EINVAL;
-
- while (size > 0) {
- bytes = min_t(size_t, size, 4);
- memcpy_toio(ispi->base + FDATA(i), buf, bytes);
- size -= bytes;
- buf += bytes;
- i++;
- }
-
- return 0;
-}
-
-static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
-{
- u32 val;
-
- return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
- !(val & HSFSTS_CTL_SCIP), 0,
- INTEL_SPI_TIMEOUT * 1000);
-}
-
-static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
-{
- u32 val;
-
- return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
- !(val & SSFSTS_CTL_SCIP), 0,
- INTEL_SPI_TIMEOUT * 1000);
-}
-
-static int intel_spi_init(struct intel_spi *ispi)
-{
- u32 opmenu0, opmenu1, lvscc, uvscc, val;
- int i;
-
- switch (ispi->info->type) {
- case INTEL_SPI_BYT:
- ispi->sregs = ispi->base + BYT_SSFSTS_CTL;
- ispi->pregs = ispi->base + BYT_PR;
- ispi->nregions = BYT_FREG_NUM;
- ispi->pr_num = BYT_PR_NUM;
- ispi->swseq_reg = true;
-
- if (writeable) {
- /* Disable write protection */
- val = readl(ispi->base + BYT_BCR);
- if (!(val & BYT_BCR_WPD)) {
- val |= BYT_BCR_WPD;
- writel(val, ispi->base + BYT_BCR);
- val = readl(ispi->base + BYT_BCR);
- }
-
- ispi->writeable = !!(val & BYT_BCR_WPD);
- }
-
- break;
-
- case INTEL_SPI_LPT:
- ispi->sregs = ispi->base + LPT_SSFSTS_CTL;
- ispi->pregs = ispi->base + LPT_PR;
- ispi->nregions = LPT_FREG_NUM;
- ispi->pr_num = LPT_PR_NUM;
- ispi->swseq_reg = true;
- break;
-
- case INTEL_SPI_BXT:
- ispi->sregs = ispi->base + BXT_SSFSTS_CTL;
- ispi->pregs = ispi->base + BXT_PR;
- ispi->nregions = BXT_FREG_NUM;
- ispi->pr_num = BXT_PR_NUM;
- ispi->erase_64k = true;
- break;
-
- case INTEL_SPI_CNL:
- ispi->sregs = NULL;
- ispi->pregs = ispi->base + CNL_PR;
- ispi->nregions = CNL_FREG_NUM;
- ispi->pr_num = CNL_PR_NUM;
- break;
-
- default:
- return -EINVAL;
- }
-
- /* Disable #SMI generation from HW sequencer */
- val = readl(ispi->base + HSFSTS_CTL);
- val &= ~HSFSTS_CTL_FSMIE;
- writel(val, ispi->base + HSFSTS_CTL);
-
- /*
- * Determine whether erase operation should use HW or SW sequencer.
- *
- * The HW sequencer has a predefined list of opcodes, with only the
- * erase opcode being programmable in LVSCC and UVSCC registers.
- * If these registers don't contain a valid erase opcode, erase
- * cannot be done using HW sequencer.
- */
- lvscc = readl(ispi->base + LVSCC);
- uvscc = readl(ispi->base + UVSCC);
- if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK))
- ispi->swseq_erase = true;
- /* SPI controller on Intel BXT supports 64K erase opcode */
- if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase)
- if (!(lvscc & ERASE_64K_OPCODE_MASK) ||
- !(uvscc & ERASE_64K_OPCODE_MASK))
- ispi->erase_64k = false;
-
- if (ispi->sregs == NULL && (ispi->swseq_reg || ispi->swseq_erase)) {
- dev_err(ispi->dev, "software sequencer not supported, but required\n");
- return -EINVAL;
- }
-
- /*
- * Some controllers can only do basic operations using hardware
- * sequencer. All other operations are supposed to be carried out
- * using software sequencer.
- */
- if (ispi->swseq_reg) {
- /* Disable #SMI generation from SW sequencer */
- val = readl(ispi->sregs + SSFSTS_CTL);
- val &= ~SSFSTS_CTL_FSMIE;
- writel(val, ispi->sregs + SSFSTS_CTL);
- }
-
- /* Check controller's lock status */
- val = readl(ispi->base + HSFSTS_CTL);
- ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN);
-
- if (ispi->locked && ispi->sregs) {
- /*
- * BIOS programs allowed opcodes and then locks down the
- * register. So read back what opcodes it decided to support.
- * That's the set we are going to support as well.
- */
- opmenu0 = readl(ispi->sregs + OPMENU0);
- opmenu1 = readl(ispi->sregs + OPMENU1);
-
- if (opmenu0 && opmenu1) {
- for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
- ispi->opcodes[i] = opmenu0 >> i * 8;
- ispi->opcodes[i + 4] = opmenu1 >> i * 8;
- }
- }
- }
-
- intel_spi_dump_regs(ispi);
-
- return 0;
-}
-
-static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)
-{
- int i;
- int preop;
-
- if (ispi->locked) {
- for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
- if (ispi->opcodes[i] == opcode)
- return i;
-
- return -EINVAL;
- }
-
- /* The lock is off, so just use index 0 */
- writel(opcode, ispi->sregs + OPMENU0);
- preop = readw(ispi->sregs + PREOP_OPTYPE);
- writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE);
-
- return 0;
-}
-
-static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len)
-{
- u32 val, status;
- int ret;
-
- val = readl(ispi->base + HSFSTS_CTL);
- val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK);
-
- switch (opcode) {
- case SPINOR_OP_RDID:
- val |= HSFSTS_CTL_FCYCLE_RDID;
- break;
- case SPINOR_OP_WRSR:
- val |= HSFSTS_CTL_FCYCLE_WRSR;
- break;
- case SPINOR_OP_RDSR:
- val |= HSFSTS_CTL_FCYCLE_RDSR;
- break;
- default:
- return -EINVAL;
- }
-
- if (len > INTEL_SPI_FIFO_SZ)
- return -EINVAL;
-
- val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;
- val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
- val |= HSFSTS_CTL_FGO;
- writel(val, ispi->base + HSFSTS_CTL);
-
- ret = intel_spi_wait_hw_busy(ispi);
- if (ret)
- return ret;
-
- status = readl(ispi->base + HSFSTS_CTL);
- if (status & HSFSTS_CTL_FCERR)
- return -EIO;
- else if (status & HSFSTS_CTL_AEL)
- return -EACCES;
-
- return 0;
-}
-
-static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
- int optype)
-{
- u32 val = 0, status;
- u8 atomic_preopcode;
- int ret;
-
- ret = intel_spi_opcode_index(ispi, opcode, optype);
- if (ret < 0)
- return ret;
-
- if (len > INTEL_SPI_FIFO_SZ)
- return -EINVAL;
-
- /*
- * Always clear it after each SW sequencer operation regardless
- * of whether it is successful or not.
- */
- atomic_preopcode = ispi->atomic_preopcode;
- ispi->atomic_preopcode = 0;
-
- /* Only mark 'Data Cycle' bit when there is data to be transferred */
- if (len > 0)
- val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
- val |= ret << SSFSTS_CTL_COP_SHIFT;
- val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
- val |= SSFSTS_CTL_SCGO;
- if (atomic_preopcode) {
- u16 preop;
-
- switch (optype) {
- case OPTYPE_WRITE_NO_ADDR:
- case OPTYPE_WRITE_WITH_ADDR:
- /* Pick matching preopcode for the atomic sequence */
- preop = readw(ispi->sregs + PREOP_OPTYPE);
- if ((preop & 0xff) == atomic_preopcode)
- ; /* Do nothing */
- else if ((preop >> 8) == atomic_preopcode)
- val |= SSFSTS_CTL_SPOP;
- else
- return -EINVAL;
-
- /* Enable atomic sequence */
- val |= SSFSTS_CTL_ACS;
- break;
-
- default:
- return -EINVAL;
- }
-
- }
- writel(val, ispi->sregs + SSFSTS_CTL);
-
- ret = intel_spi_wait_sw_busy(ispi);
- if (ret)
- return ret;
-
- status = readl(ispi->sregs + SSFSTS_CTL);
- if (status & SSFSTS_CTL_FCERR)
- return -EIO;
- else if (status & SSFSTS_CTL_AEL)
- return -EACCES;
-
- return 0;
-}
-
-static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
- size_t len)
-{
- struct intel_spi *ispi = nor->priv;
- int ret;
-
- /* Address of the first chip */
- writel(0, ispi->base + FADDR);
-
- if (ispi->swseq_reg)
- ret = intel_spi_sw_cycle(ispi, opcode, len,
- OPTYPE_READ_NO_ADDR);
- else
- ret = intel_spi_hw_cycle(ispi, opcode, len);
-
- if (ret)
- return ret;
-
- return intel_spi_read_block(ispi, buf, len);
-}
-
-static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
- size_t len)
-{
- struct intel_spi *ispi = nor->priv;
- int ret;
-
- /*
- * This is handled with atomic operation and preop code in Intel
- * controller so we only verify that it is available. If the
- * controller is not locked, program the opcode to the PREOP
- * register for later use.
- *
- * When hardware sequencer is used there is no need to program
- * any opcodes (it handles them automatically as part of a command).
- */
- if (opcode == SPINOR_OP_WREN) {
- u16 preop;
-
- if (!ispi->swseq_reg)
- return 0;
-
- preop = readw(ispi->sregs + PREOP_OPTYPE);
- if ((preop & 0xff) != opcode && (preop >> 8) != opcode) {
- if (ispi->locked)
- return -EINVAL;
- writel(opcode, ispi->sregs + PREOP_OPTYPE);
- }
-
- /*
- * This enables atomic sequence on next SW sycle. Will
- * be cleared after next operation.
- */
- ispi->atomic_preopcode = opcode;
- return 0;
- }
-
- /*
- * We hope that HW sequencer will do the right thing automatically and
- * with the SW sequencer we cannot use preopcode anyway, so just ignore
- * the Write Disable operation and pretend it was completed
- * successfully.
- */
- if (opcode == SPINOR_OP_WRDI)
- return 0;
-
- writel(0, ispi->base + FADDR);
-
- /* Write the value beforehand */
- ret = intel_spi_write_block(ispi, buf, len);
- if (ret)
- return ret;
-
- if (ispi->swseq_reg)
- return intel_spi_sw_cycle(ispi, opcode, len,
- OPTYPE_WRITE_NO_ADDR);
- return intel_spi_hw_cycle(ispi, opcode, len);
-}
-
-static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
- u_char *read_buf)
-{
- struct intel_spi *ispi = nor->priv;
- size_t block_size, retlen = 0;
- u32 val, status;
- ssize_t ret;
-
- /*
- * Atomic sequence is not expected with HW sequencer reads. Make
- * sure it is cleared regardless.
- */
- if (WARN_ON_ONCE(ispi->atomic_preopcode))
- ispi->atomic_preopcode = 0;
-
- switch (nor->read_opcode) {
- case SPINOR_OP_READ:
- case SPINOR_OP_READ_FAST:
- case SPINOR_OP_READ_4B:
- case SPINOR_OP_READ_FAST_4B:
- break;
- default:
- return -EINVAL;
- }
-
- while (len > 0) {
- block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
-
- /* Read cannot cross 4K boundary */
- block_size = min_t(loff_t, from + block_size,
- round_up(from + 1, SZ_4K)) - from;
-
- writel(from, ispi->base + FADDR);
-
- val = readl(ispi->base + HSFSTS_CTL);
- val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
- val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
- val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
- val |= HSFSTS_CTL_FCYCLE_READ;
- val |= HSFSTS_CTL_FGO;
- writel(val, ispi->base + HSFSTS_CTL);
-
- ret = intel_spi_wait_hw_busy(ispi);
- if (ret)
- return ret;
-
- status = readl(ispi->base + HSFSTS_CTL);
- if (status & HSFSTS_CTL_FCERR)
- ret = -EIO;
- else if (status & HSFSTS_CTL_AEL)
- ret = -EACCES;
-
- if (ret < 0) {
- dev_err(ispi->dev, "read error: %llx: %#x\n", from,
- status);
- return ret;
- }
-
- ret = intel_spi_read_block(ispi, read_buf, block_size);
- if (ret)
- return ret;
-
- len -= block_size;
- from += block_size;
- retlen += block_size;
- read_buf += block_size;
- }
-
- return retlen;
-}
-
-static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
- const u_char *write_buf)
-{
- struct intel_spi *ispi = nor->priv;
- size_t block_size, retlen = 0;
- u32 val, status;
- ssize_t ret;
-
- /* Not needed with HW sequencer write, make sure it is cleared */
- ispi->atomic_preopcode = 0;
-
- while (len > 0) {
- block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
-
- /* Write cannot cross 4K boundary */
- block_size = min_t(loff_t, to + block_size,
- round_up(to + 1, SZ_4K)) - to;
-
- writel(to, ispi->base + FADDR);
-
- val = readl(ispi->base + HSFSTS_CTL);
- val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
- val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
- val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
- val |= HSFSTS_CTL_FCYCLE_WRITE;
-
- ret = intel_spi_write_block(ispi, write_buf, block_size);
- if (ret) {
- dev_err(ispi->dev, "failed to write block\n");
- return ret;
- }
-
- /* Start the write now */
- val |= HSFSTS_CTL_FGO;
- writel(val, ispi->base + HSFSTS_CTL);
-
- ret = intel_spi_wait_hw_busy(ispi);
- if (ret) {
- dev_err(ispi->dev, "timeout\n");
- return ret;
- }
-
- status = readl(ispi->base + HSFSTS_CTL);
- if (status & HSFSTS_CTL_FCERR)
- ret = -EIO;
- else if (status & HSFSTS_CTL_AEL)
- ret = -EACCES;
-
- if (ret < 0) {
- dev_err(ispi->dev, "write error: %llx: %#x\n", to,
- status);
- return ret;
- }
-
- len -= block_size;
- to += block_size;
- retlen += block_size;
- write_buf += block_size;
- }
-
- return retlen;
-}
-
-static int intel_spi_erase(struct spi_nor *nor, loff_t offs)
-{
- size_t erase_size, len = nor->mtd.erasesize;
- struct intel_spi *ispi = nor->priv;
- u32 val, status, cmd;
- int ret;
-
- /* If the hardware can do 64k erase use that when possible */
- if (len >= SZ_64K && ispi->erase_64k) {
- cmd = HSFSTS_CTL_FCYCLE_ERASE_64K;
- erase_size = SZ_64K;
- } else {
- cmd = HSFSTS_CTL_FCYCLE_ERASE;
- erase_size = SZ_4K;
- }
-
- if (ispi->swseq_erase) {
- while (len > 0) {
- writel(offs, ispi->base + FADDR);
-
- ret = intel_spi_sw_cycle(ispi, nor->erase_opcode,
- 0, OPTYPE_WRITE_WITH_ADDR);
- if (ret)
- return ret;
-
- offs += erase_size;
- len -= erase_size;
- }
-
- return 0;
- }
-
- /* Not needed with HW sequencer erase, make sure it is cleared */
- ispi->atomic_preopcode = 0;
-
- while (len > 0) {
- writel(offs, ispi->base + FADDR);
-
- val = readl(ispi->base + HSFSTS_CTL);
- val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
- val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
- val |= cmd;
- val |= HSFSTS_CTL_FGO;
- writel(val, ispi->base + HSFSTS_CTL);
-
- ret = intel_spi_wait_hw_busy(ispi);
- if (ret)
- return ret;
-
- status = readl(ispi->base + HSFSTS_CTL);
- if (status & HSFSTS_CTL_FCERR)
- return -EIO;
- else if (status & HSFSTS_CTL_AEL)
- return -EACCES;
-
- offs += erase_size;
- len -= erase_size;
- }
-
- return 0;
-}
-
-static bool intel_spi_is_protected(const struct intel_spi *ispi,
- unsigned int base, unsigned int limit)
-{
- int i;
-
- for (i = 0; i < ispi->pr_num; i++) {
- u32 pr_base, pr_limit, pr_value;
-
- pr_value = readl(ispi->pregs + PR(i));
- if (!(pr_value & (PR_WPE | PR_RPE)))
- continue;
-
- pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
- pr_base = pr_value & PR_BASE_MASK;
-
- if (pr_base >= base && pr_limit <= limit)
- return true;
- }
-
- return false;
-}
-
-/*
- * There will be a single partition holding all enabled flash regions. We
- * call this "BIOS".
- */
-static void intel_spi_fill_partition(struct intel_spi *ispi,
- struct mtd_partition *part)
-{
- u64 end;
- int i;
-
- memset(part, 0, sizeof(*part));
-
- /* Start from the mandatory descriptor region */
- part->size = 4096;
- part->name = "BIOS";
-
- /*
- * Now try to find where this partition ends based on the flash
- * region registers.
- */
- for (i = 1; i < ispi->nregions; i++) {
- u32 region, base, limit;
-
- region = readl(ispi->base + FREG(i));
- base = region & FREG_BASE_MASK;
- limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
-
- if (base >= limit || limit == 0)
- continue;
-
- /*
- * If any of the regions have protection bits set, make the
- * whole partition read-only to be on the safe side.
- */
- if (intel_spi_is_protected(ispi, base, limit))
- ispi->writeable = false;
-
- end = (limit << 12) + 4096;
- if (end > part->size)
- part->size = end;
- }
-}
-
-static const struct spi_nor_controller_ops intel_spi_controller_ops = {
- .read_reg = intel_spi_read_reg,
- .write_reg = intel_spi_write_reg,
- .read = intel_spi_read,
- .write = intel_spi_write,
- .erase = intel_spi_erase,
-};
-
-struct intel_spi *intel_spi_probe(struct device *dev,
- struct resource *mem, const struct intel_spi_boardinfo *info)
-{
- const struct spi_nor_hwcaps hwcaps = {
- .mask = SNOR_HWCAPS_READ |
- SNOR_HWCAPS_READ_FAST |
- SNOR_HWCAPS_PP,
- };
- struct mtd_partition part;
- struct intel_spi *ispi;
- int ret;
-
- if (!info || !mem)
- return ERR_PTR(-EINVAL);
-
- ispi = devm_kzalloc(dev, sizeof(*ispi), GFP_KERNEL);
- if (!ispi)
- return ERR_PTR(-ENOMEM);
-
- ispi->base = devm_ioremap_resource(dev, mem);
- if (IS_ERR(ispi->base))
- return ERR_CAST(ispi->base);
-
- ispi->dev = dev;
- ispi->info = info;
- ispi->writeable = info->writeable;
-
- ret = intel_spi_init(ispi);
- if (ret)
- return ERR_PTR(ret);
-
- ispi->nor.dev = ispi->dev;
- ispi->nor.priv = ispi;
- ispi->nor.controller_ops = &intel_spi_controller_ops;
-
- ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
- if (ret) {
- dev_info(dev, "failed to locate the chip\n");
- return ERR_PTR(ret);
- }
-
- intel_spi_fill_partition(ispi, &part);
-
- /* Prevent writes if not explicitly enabled */
- if (!ispi->writeable || !writeable)
- ispi->nor.mtd.flags &= ~MTD_WRITEABLE;
-
- ret = mtd_device_register(&ispi->nor.mtd, &part, 1);
- if (ret)
- return ERR_PTR(ret);
-
- return ispi;
-}
-EXPORT_SYMBOL_GPL(intel_spi_probe);
-
-int intel_spi_remove(struct intel_spi *ispi)
-{
- return mtd_device_unregister(&ispi->nor.mtd);
-}
-EXPORT_SYMBOL_GPL(intel_spi_remove);
-
-MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/controllers/intel-spi.h b/drivers/mtd/spi-nor/controllers/intel-spi.h
deleted file mode 100644
index f2871179fd34..000000000000
--- a/drivers/mtd/spi-nor/controllers/intel-spi.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Intel PCH/PCU SPI flash driver.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
- */
-
-#ifndef INTEL_SPI_H
-#define INTEL_SPI_H
-
-#include <linux/platform_data/x86/intel-spi.h>
-
-struct intel_spi;
-struct resource;
-
-struct intel_spi *intel_spi_probe(struct device *dev,
- struct resource *mem, const struct intel_spi_boardinfo *info);
-int intel_spi_remove(struct intel_spi *ispi);
-
-#endif /* INTEL_SPI_H */
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 04ea180118e3..b4f141ad9c9c 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -157,8 +157,8 @@ static int spi_nor_spimem_exec_op(struct spi_nor *nor, struct spi_mem_op *op)
return spi_mem_exec_op(nor->spimem, op);
}
-static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
- u8 *buf, size_t len)
+int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
+ u8 *buf, size_t len)
{
if (spi_nor_protocol_is_dtr(nor->reg_proto))
return -EOPNOTSUPP;
@@ -166,8 +166,8 @@ static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
return nor->controller_ops->read_reg(nor, opcode, buf, len);
}
-static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
- const u8 *buf, size_t len)
+int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
+ const u8 *buf, size_t len)
{
if (spi_nor_protocol_is_dtr(nor->reg_proto))
return -EOPNOTSUPP;
@@ -413,50 +413,6 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
}
/**
- * spi_nor_read_fsr() - Read the Flag Status Register.
- * @nor: pointer to 'struct spi_nor'
- * @fsr: pointer to a DMA-able buffer where the value of the
- * Flag Status Register will be written. Should be at least 2
- * bytes.
- *
- * Return: 0 on success, -errno otherwise.
- */
-static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
-{
- int ret;
-
- if (nor->spimem) {
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_IN(1, fsr, 0));
-
- if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
- op.addr.nbytes = nor->params->rdsr_addr_nbytes;
- op.dummy.nbytes = nor->params->rdsr_dummy;
- /*
- * We don't want to read only one byte in DTR mode. So,
- * read 2 and then discard the second byte.
- */
- op.data.nbytes = 2;
- }
-
- spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
-
- ret = spi_mem_exec_op(nor->spimem, &op);
- } else {
- ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr,
- 1);
- }
-
- if (ret)
- dev_dbg(nor->dev, "error %d reading FSR\n", ret);
-
- return ret;
-}
-
-/**
* spi_nor_read_cr() - Read the Configuration Register using the
* SPINOR_OP_RDCR (35h) command.
* @nor: pointer to 'struct spi_nor'
@@ -599,189 +555,21 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
}
/**
- * spi_nor_xread_sr() - Read the Status Register on S3AN flashes.
- * @nor: pointer to 'struct spi_nor'.
- * @sr: pointer to a DMA-able buffer where the value of the
- * Status Register will be written.
- *
- * Return: 0 on success, -errno otherwise.
- */
-int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
-{
- int ret;
-
- if (nor->spimem) {
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 0),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_IN(1, sr, 0));
-
- spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
-
- ret = spi_mem_exec_op(nor->spimem, &op);
- } else {
- ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_XRDSR, sr,
- 1);
- }
-
- if (ret)
- dev_dbg(nor->dev, "error %d reading XRDSR\n", ret);
-
- return ret;
-}
-
-/**
- * spi_nor_xsr_ready() - Query the Status Register of the S3AN flash to see if
- * the flash is ready for new commands.
- * @nor: pointer to 'struct spi_nor'.
- *
- * Return: 1 if ready, 0 if not ready, -errno on errors.
- */
-static int spi_nor_xsr_ready(struct spi_nor *nor)
-{
- int ret;
-
- ret = spi_nor_xread_sr(nor, nor->bouncebuf);
- if (ret)
- return ret;
-
- return !!(nor->bouncebuf[0] & XSR_RDY);
-}
-
-/**
- * spi_nor_clear_sr() - Clear the Status Register.
- * @nor: pointer to 'struct spi_nor'.
- */
-static void spi_nor_clear_sr(struct spi_nor *nor)
-{
- int ret;
-
- if (nor->spimem) {
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_NO_DATA);
-
- spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
-
- ret = spi_mem_exec_op(nor->spimem, &op);
- } else {
- ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR,
- NULL, 0);
- }
-
- if (ret)
- dev_dbg(nor->dev, "error %d clearing SR\n", ret);
-}
-
-/**
* spi_nor_sr_ready() - Query the Status Register to see if the flash is ready
* for new commands.
* @nor: pointer to 'struct spi_nor'.
*
* Return: 1 if ready, 0 if not ready, -errno on errors.
*/
-static int spi_nor_sr_ready(struct spi_nor *nor)
-{
- int ret = spi_nor_read_sr(nor, nor->bouncebuf);
-
- if (ret)
- return ret;
-
- if (nor->flags & SNOR_F_USE_CLSR &&
- nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) {
- if (nor->bouncebuf[0] & SR_E_ERR)
- dev_err(nor->dev, "Erase Error occurred\n");
- else
- dev_err(nor->dev, "Programming Error occurred\n");
-
- spi_nor_clear_sr(nor);
-
- /*
- * WEL bit remains set to one when an erase or page program
- * error occurs. Issue a Write Disable command to protect
- * against inadvertent writes that can possibly corrupt the
- * contents of the memory.
- */
- ret = spi_nor_write_disable(nor);
- if (ret)
- return ret;
-
- return -EIO;
- }
-
- return !(nor->bouncebuf[0] & SR_WIP);
-}
-
-/**
- * spi_nor_clear_fsr() - Clear the Flag Status Register.
- * @nor: pointer to 'struct spi_nor'.
- */
-static void spi_nor_clear_fsr(struct spi_nor *nor)
+int spi_nor_sr_ready(struct spi_nor *nor)
{
int ret;
- if (nor->spimem) {
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_NO_DATA);
-
- spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
-
- ret = spi_mem_exec_op(nor->spimem, &op);
- } else {
- ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR,
- NULL, 0);
- }
-
- if (ret)
- dev_dbg(nor->dev, "error %d clearing FSR\n", ret);
-}
-
-/**
- * spi_nor_fsr_ready() - Query the Flag Status Register to see if the flash is
- * ready for new commands.
- * @nor: pointer to 'struct spi_nor'.
- *
- * Return: 1 if ready, 0 if not ready, -errno on errors.
- */
-static int spi_nor_fsr_ready(struct spi_nor *nor)
-{
- int ret = spi_nor_read_fsr(nor, nor->bouncebuf);
-
+ ret = spi_nor_read_sr(nor, nor->bouncebuf);
if (ret)
return ret;
- if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) {
- if (nor->bouncebuf[0] & FSR_E_ERR)
- dev_err(nor->dev, "Erase operation failed.\n");
- else
- dev_err(nor->dev, "Program operation failed.\n");
-
- if (nor->bouncebuf[0] & FSR_PT_ERR)
- dev_err(nor->dev,
- "Attempted to modify a protected sector.\n");
-
- spi_nor_clear_fsr(nor);
-
- /*
- * WEL bit remains set to one when an erase or page program
- * error occurs. Issue a Write Disable command to protect
- * against inadvertent writes that can possibly corrupt the
- * contents of the memory.
- */
- ret = spi_nor_write_disable(nor);
- if (ret)
- return ret;
-
- return -EIO;
- }
-
- return !!(nor->bouncebuf[0] & FSR_READY);
+ return !(nor->bouncebuf[0] & SR_WIP);
}
/**
@@ -792,18 +580,11 @@ static int spi_nor_fsr_ready(struct spi_nor *nor)
*/
static int spi_nor_ready(struct spi_nor *nor)
{
- int sr, fsr;
+ /* Flashes might override the standard routine. */
+ if (nor->params->ready)
+ return nor->params->ready(nor);
- if (nor->flags & SNOR_F_READY_XSR_RDY)
- sr = spi_nor_xsr_ready(nor);
- else
- sr = spi_nor_sr_ready(nor);
- if (sr < 0)
- return sr;
- fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
- if (fsr < 0)
- return fsr;
- return sr && fsr;
+ return spi_nor_sr_ready(nor);
}
/**
@@ -2532,11 +2313,12 @@ static int spi_nor_setup(struct spi_nor *nor,
{
int ret;
- if (nor->params->setup) {
+ if (nor->params->setup)
ret = nor->params->setup(nor, hwcaps);
- if (ret)
- return ret;
- }
+ else
+ ret = spi_nor_default_setup(nor, hwcaps);
+ if (ret)
+ return ret;
return spi_nor_set_addr_width(nor);
}
@@ -2666,20 +2448,6 @@ static void spi_nor_init_flags(struct spi_nor *nor)
if (flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
-
- if (flags & USE_CLSR)
- nor->flags |= SNOR_F_USE_CLSR;
-
- if (flags & USE_FSR)
- nor->flags |= SNOR_F_USE_FSR;
-
- /*
- * Make sure the XSR_RDY flag is set before calling
- * spi_nor_wait_till_ready(). Xilinx S3AN share MFR
- * with Atmel SPI NOR.
- */
- if (flags & SPI_NOR_XSR_RDY)
- nor->flags |= SNOR_F_READY_XSR_RDY;
}
/**
@@ -2786,7 +2554,6 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
params->quad_enable = spi_nor_sr2_bit1_quad_enable;
params->set_4byte_addr_mode = spansion_set_4byte_addr_mode;
- params->setup = spi_nor_default_setup;
params->otp.org = &info->otp_org;
/* Default to 16-bit Write Status (01h) Command */
@@ -3181,10 +2948,11 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->flags = MTD_CAP_NORFLASH;
if (nor->info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
+ else
+ mtd->_erase = spi_nor_erase;
mtd->writesize = nor->params->writesize;
mtd->writebufsize = nor->params->page_size;
mtd->size = nor->params->size;
- mtd->_erase = spi_nor_erase;
mtd->_read = spi_nor_read;
/* Might be already set by some SST flashes. */
if (!mtd->_write)
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 2afb610853a9..b7fd760e3b47 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -12,23 +12,20 @@
#define SPI_NOR_MAX_ID_LEN 6
enum spi_nor_option_flags {
- SNOR_F_USE_FSR = BIT(0),
- SNOR_F_HAS_SR_TB = BIT(1),
- SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
- SNOR_F_READY_XSR_RDY = BIT(3),
- SNOR_F_USE_CLSR = BIT(4),
- SNOR_F_BROKEN_RESET = BIT(5),
- SNOR_F_4B_OPCODES = BIT(6),
- SNOR_F_HAS_4BAIT = BIT(7),
- SNOR_F_HAS_LOCK = BIT(8),
- SNOR_F_HAS_16BIT_SR = BIT(9),
- SNOR_F_NO_READ_CR = BIT(10),
- SNOR_F_HAS_SR_TB_BIT6 = BIT(11),
- SNOR_F_HAS_4BIT_BP = BIT(12),
- SNOR_F_HAS_SR_BP3_BIT6 = BIT(13),
- SNOR_F_IO_MODE_EN_VOLATILE = BIT(14),
- SNOR_F_SOFT_RESET = BIT(15),
- SNOR_F_SWP_IS_VOLATILE = BIT(16),
+ SNOR_F_HAS_SR_TB = BIT(0),
+ SNOR_F_NO_OP_CHIP_ERASE = BIT(1),
+ SNOR_F_BROKEN_RESET = BIT(2),
+ SNOR_F_4B_OPCODES = BIT(3),
+ SNOR_F_HAS_4BAIT = BIT(4),
+ SNOR_F_HAS_LOCK = BIT(5),
+ SNOR_F_HAS_16BIT_SR = BIT(6),
+ SNOR_F_NO_READ_CR = BIT(7),
+ SNOR_F_HAS_SR_TB_BIT6 = BIT(8),
+ SNOR_F_HAS_4BIT_BP = BIT(9),
+ SNOR_F_HAS_SR_BP3_BIT6 = BIT(10),
+ SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
+ SNOR_F_SOFT_RESET = BIT(12),
+ SNOR_F_SWP_IS_VOLATILE = BIT(13),
};
struct spi_nor_read_command {
@@ -257,10 +254,13 @@ struct spi_nor_otp {
* @convert_addr: converts an absolute address into something the flash
* will understand. Particularly useful when pagesize is
* not a power-of-2.
- * @setup: configures the SPI NOR memory. Useful for SPI NOR
- * flashes that have peculiarities to the SPI NOR standard
- * e.g. different opcodes, specific address calculation,
- * page size, etc.
+ * @setup: (optional) configures the SPI NOR memory. Useful for
+ * SPI NOR flashes that have peculiarities to the SPI NOR
+ * standard e.g. different opcodes, specific address
+ * calculation, page size, etc.
+ * @ready: (optional) flashes might use a different mechanism
+ * than reading the status register to indicate they
+ * are ready for a new command
* @locking_ops: SPI NOR locking methods.
*/
struct spi_nor_flash_parameter {
@@ -282,6 +282,7 @@ struct spi_nor_flash_parameter {
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps);
+ int (*ready)(struct spi_nor *nor);
const struct spi_nor_locking_ops *locking_ops;
};
@@ -345,10 +346,6 @@ struct spi_nor_fixups {
* SPI_NOR_NO_ERASE: no erase command needed.
* NO_CHIP_ERASE: chip does not support chip erase.
* SPI_NOR_NO_FR: can't do fastread.
- * USE_CLSR: use CLSR command.
- * USE_FSR: use flag status register
- * SPI_NOR_XSR_RDY: S3AN flashes have specific opcode to read the
- * status register.
*
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
* Used when SFDP tables are not defined in the flash. These
@@ -399,9 +396,6 @@ struct flash_info {
#define SPI_NOR_NO_ERASE BIT(6)
#define NO_CHIP_ERASE BIT(7)
#define SPI_NOR_NO_FR BIT(8)
-#define USE_CLSR BIT(9)
-#define USE_FSR BIT(10)
-#define SPI_NOR_XSR_RDY BIT(11)
u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0)
@@ -458,19 +452,6 @@ struct flash_info {
.addr_width = (_addr_width), \
.flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \
-#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
- .id = { \
- ((_jedec_id) >> 16) & 0xff, \
- ((_jedec_id) >> 8) & 0xff, \
- (_jedec_id) & 0xff \
- }, \
- .id_len = 3, \
- .sector_size = (8*_page_size), \
- .n_sectors = (_n_sectors), \
- .page_size = _page_size, \
- .addr_width = 3, \
- .flags = SPI_NOR_NO_FR | SPI_NOR_XSR_RDY,
-
#define OTP_INFO(_len, _n_regions, _base, _offset) \
.otp_org = { \
.len = (_len), \
@@ -554,12 +535,12 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
+int spi_nor_sr_ready(struct spi_nor *nor);
int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
-int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
u8 *buf);
ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
@@ -599,6 +580,11 @@ void spi_nor_try_unlock_all(struct spi_nor *nor);
void spi_nor_set_mtd_locking_ops(struct spi_nor *nor);
void spi_nor_set_mtd_otp_ops(struct spi_nor *nor);
+int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
+ u8 *buf, size_t len);
+int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
+ const u8 *buf, size_t len);
+
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
{
return container_of(mtd, struct spi_nor, mtd);
diff --git a/drivers/mtd/spi-nor/eon.c b/drivers/mtd/spi-nor/eon.c
index 4f3ee6331f37..8c1c57530281 100644
--- a/drivers/mtd/spi-nor/eon.c
+++ b/drivers/mtd/spi-nor/eon.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info eon_parts[] = {
+static const struct flash_info eon_nor_parts[] = {
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64)
NO_SFDP_FLAGS(SECT_4K) },
@@ -32,6 +32,6 @@ static const struct flash_info eon_parts[] = {
const struct spi_nor_manufacturer spi_nor_eon = {
.name = "eon",
- .parts = eon_parts,
- .nparts = ARRAY_SIZE(eon_parts),
+ .parts = eon_nor_parts,
+ .nparts = ARRAY_SIZE(eon_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c
index ace1da221566..79e2408f4998 100644
--- a/drivers/mtd/spi-nor/esmt.c
+++ b/drivers/mtd/spi-nor/esmt.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info esmt_parts[] = {
+static const struct flash_info esmt_nor_parts[] = {
/* ESMT */
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
@@ -23,6 +23,6 @@ static const struct flash_info esmt_parts[] = {
const struct spi_nor_manufacturer spi_nor_esmt = {
.name = "esmt",
- .parts = esmt_parts,
- .nparts = ARRAY_SIZE(esmt_parts),
+ .parts = esmt_nor_parts,
+ .nparts = ARRAY_SIZE(esmt_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c
index f6c6fb36a428..84a07c2e0536 100644
--- a/drivers/mtd/spi-nor/everspin.c
+++ b/drivers/mtd/spi-nor/everspin.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info everspin_parts[] = {
+static const struct flash_info everspin_nor_parts[] = {
/* Everspin */
{ "mr25h128", CAT25_INFO(16 * 1024, 1, 256, 2) },
{ "mr25h256", CAT25_INFO(32 * 1024, 1, 256, 2) },
@@ -18,6 +18,6 @@ static const struct flash_info everspin_parts[] = {
const struct spi_nor_manufacturer spi_nor_everspin = {
.name = "everspin",
- .parts = everspin_parts,
- .nparts = ARRAY_SIZE(everspin_parts),
+ .parts = everspin_nor_parts,
+ .nparts = ARRAY_SIZE(everspin_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/fujitsu.c b/drivers/mtd/spi-nor/fujitsu.c
index 5fa8f04f2e35..69cffc5c73ef 100644
--- a/drivers/mtd/spi-nor/fujitsu.c
+++ b/drivers/mtd/spi-nor/fujitsu.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info fujitsu_parts[] = {
+static const struct flash_info fujitsu_nor_parts[] = {
/* Fujitsu */
{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE) },
@@ -16,6 +16,6 @@ static const struct flash_info fujitsu_parts[] = {
const struct spi_nor_manufacturer spi_nor_fujitsu = {
.name = "fujitsu",
- .parts = fujitsu_parts,
- .nparts = ARRAY_SIZE(fujitsu_parts),
+ .parts = fujitsu_nor_parts,
+ .nparts = ARRAY_SIZE(fujitsu_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
index 0807d0263808..119b38e6fc2a 100644
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -23,7 +23,7 @@ static const struct spi_nor_fixups gd25q256_fixups = {
.default_init = gd25q256_default_init,
};
-static const struct flash_info gigadevice_parts[] = {
+static const struct flash_info gigadevice_nor_parts[] = {
{ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
@@ -61,6 +61,6 @@ static const struct flash_info gigadevice_parts[] = {
const struct spi_nor_manufacturer spi_nor_gigadevice = {
.name = "gigadevice",
- .parts = gigadevice_parts,
- .nparts = ARRAY_SIZE(gigadevice_parts),
+ .parts = gigadevice_nor_parts,
+ .nparts = ARRAY_SIZE(gigadevice_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/intel.c b/drivers/mtd/spi-nor/intel.c
index d64e114e9fb4..9179f2d09cba 100644
--- a/drivers/mtd/spi-nor/intel.c
+++ b/drivers/mtd/spi-nor/intel.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info intel_parts[] = {
+static const struct flash_info intel_nor_parts[] = {
/* Intel/Numonyx -- xxxs33b */
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
@@ -20,6 +20,6 @@ static const struct flash_info intel_parts[] = {
const struct spi_nor_manufacturer spi_nor_intel = {
.name = "intel",
- .parts = intel_parts,
- .nparts = ARRAY_SIZE(intel_parts),
+ .parts = intel_nor_parts,
+ .nparts = ARRAY_SIZE(intel_nor_parts),
};
diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c
index 23629b919ade..c012bc2486e1 100644
--- a/drivers/mtd/spi-nor/issi.c
+++ b/drivers/mtd/spi-nor/issi.c
@@ -29,7 +29,7 @@ static const struct spi_nor_fixups is25lp256_fixups = {
.post_bfpt = is25lp256_post_bfpt_fixups,
};
-static const struct flash_info issi_parts[] = {
+static const struct flash_info issi_nor_parts[] = {
/* ISSI */
{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2)
NO_SFDP_FLAGS(SECT_4K) },
@@ -69,18 +69,18 @@ static const struct flash_info issi_parts[] = {
NO_SFDP_FLAGS(SECT_4K) },
};
-static void issi_default_init(struct spi_nor *nor)
+static void issi_nor_default_init(struct spi_nor *nor)
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
}
static const struct spi_nor_fixups issi_fixups = {
- .default_init = issi_default_init,
+ .default_init = issi_nor_default_init,
};
const struct spi_nor_manufacturer spi_nor_issi = {
.name = "issi",
- .parts = issi_parts,
- .nparts = ARRAY_SIZE(issi_parts),
+ .parts = issi_nor_parts,
+ .nparts = ARRAY_SIZE(issi_nor_parts),
.fixups = &issi_fixups,
};
diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
index 97dba1ae7fb1..d81a4cb2812b 100644
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -32,7 +32,7 @@ static const struct spi_nor_fixups mx25l25635_fixups = {
.post_bfpt = mx25l25635_post_bfpt_fixups,
};
-static const struct flash_info macronix_parts[] = {
+static const struct flash_info macronix_nor_parts[] = {
/* Macronix */
{ "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1)
NO_SFDP_FLAGS(SECT_4K) },
@@ -102,19 +102,19 @@ static const struct flash_info macronix_parts[] = {
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
};
-static void macronix_default_init(struct spi_nor *nor)
+static void macronix_nor_default_init(struct spi_nor *nor)
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
}
-static const struct spi_nor_fixups macronix_fixups = {
- .default_init = macronix_default_init,
+static const struct spi_nor_fixups macronix_nor_fixups = {
+ .default_init = macronix_nor_default_init,
};
const struct spi_nor_manufacturer spi_nor_macronix = {
.name = "macronix",
- .parts = macronix_parts,
- .nparts = ARRAY_SIZE(macronix_parts),
- .fixups = &macronix_fixups,
+ .parts = macronix_nor_parts,
+ .nparts = ARRAY_SIZE(macronix_nor_parts),
+ .fixups = &macronix_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c
index bb95b1aabf74..8a20475ce77a 100644
--- a/drivers/mtd/spi-nor/micron-st.c
+++ b/drivers/mtd/spi-nor/micron-st.c
@@ -8,6 +8,11 @@
#include "core.h"
+/* flash_info mfr_flag. Used to read proprietary FSR register. */
+#define USE_FSR BIT(0)
+
+#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
+#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */
@@ -17,7 +22,13 @@
#define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */
#define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */
-static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable)
+/* Flag Status Register bits */
+#define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */
+#define FSR_E_ERR BIT(5) /* Erase operation status */
+#define FSR_P_ERR BIT(4) /* Program operation status */
+#define FSR_PT_ERR BIT(1) /* Protection error bit */
+
+static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
@@ -102,7 +113,7 @@ static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable)
static void mt35xu512aba_default_init(struct spi_nor *nor)
{
- nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable;
+ nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable;
}
static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
@@ -130,20 +141,22 @@ static const struct spi_nor_fixups mt35xu512aba_fixups = {
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
};
-static const struct flash_info micron_parts[] = {
+static const struct flash_info micron_nor_parts[] = {
{ "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_READ |
SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE)
- .fixups = &mt35xu512aba_fixups},
+ MFR_FLAGS(USE_FSR)
+ .fixups = &mt35xu512aba_fixups
+ },
{ "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_READ)
- FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ MFR_FLAGS(USE_FSR)
+ },
};
-static const struct flash_info st_parts[] = {
+static const struct flash_info st_nor_parts[] = {
{ "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64)
@@ -156,57 +169,79 @@ static const struct flash_info st_parts[] = {
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
- SPI_NOR_BP3_SR_BIT6 | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ SPI_NOR_BP3_SR_BIT6)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
- SPI_NOR_BP3_SR_BIT6 | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ SPI_NOR_BP3_SR_BIT6)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25ql256a", INFO6(0x20ba19, 0x104400, 64 * 1024, 512)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
- SPI_NOR_QUAD_READ) },
+ SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512)
- FLAGS(USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25ql512a", INFO6(0x20ba20, 0x104400, 64 * 1024, 1024)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
- SPI_NOR_BP3_SR_BIT6 | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ SPI_NOR_BP3_SR_BIT6)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25qu512a", INFO6(0x20bb20, 0x104400, 64 * 1024, 1024)
- FLAGS(USE_FSR)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
- SPI_NOR_BP3_SR_BIT6 | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ SPI_NOR_BP3_SR_BIT6)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
- SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048)
- FLAGS(NO_CHIP_ERASE | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ FLAGS(NO_CHIP_ERASE)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096)
- FLAGS(NO_CHIP_ERASE | USE_FSR)
- NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) },
+ FLAGS(NO_CHIP_ERASE)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096)
- FLAGS(NO_CHIP_ERASE | USE_FSR)
+ FLAGS(NO_CHIP_ERASE)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
- SPI_NOR_QUAD_READ) },
+ SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_FSR)
+ },
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2) },
{ "m25p10", INFO(0x202011, 0, 32 * 1024, 4) },
@@ -250,15 +285,15 @@ static const struct flash_info st_parts[] = {
};
/**
- * st_micron_set_4byte_addr_mode() - Set 4-byte address mode for ST and Micron
- * flashes.
+ * micron_st_nor_set_4byte_addr_mode() - Set 4-byte address mode for ST and
+ * Micron flashes.
* @nor: pointer to 'struct spi_nor'.
* @enable: true to enter the 4-byte address mode, false to exit the 4-byte
* address mode.
*
* Return: 0 on success, -errno otherwise.
*/
-static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+static int micron_st_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
{
int ret;
@@ -273,28 +308,154 @@ static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
return spi_nor_write_disable(nor);
}
-static void micron_st_default_init(struct spi_nor *nor)
+/**
+ * micron_st_nor_read_fsr() - Read the Flag Status Register.
+ * @nor: pointer to 'struct spi_nor'
+ * @fsr: pointer to a DMA-able buffer where the value of the
+ * Flag Status Register will be written. Should be at least 2
+ * bytes.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, fsr, 0));
+
+ if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
+ op.addr.nbytes = nor->params->rdsr_addr_nbytes;
+ op.dummy.nbytes = nor->params->rdsr_dummy;
+ /*
+ * We don't want to read only one byte in DTR mode. So,
+ * read 2 and then discard the second byte.
+ */
+ op.data.nbytes = 2;
+ }
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr,
+ 1);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d reading FSR\n", ret);
+
+ return ret;
+}
+
+/**
+ * micron_st_nor_clear_fsr() - Clear the Flag Status Register.
+ * @nor: pointer to 'struct spi_nor'.
+ */
+static void micron_st_nor_clear_fsr(struct spi_nor *nor)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR,
+ NULL, 0);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d clearing FSR\n", ret);
+}
+
+/**
+ * micron_st_nor_ready() - Query the Status Register as well as the Flag Status
+ * Register to see if the flash is ready for new commands. If there are any
+ * errors in the FSR clear them.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 1 if ready, 0 if not ready, -errno on errors.
+ */
+static int micron_st_nor_ready(struct spi_nor *nor)
+{
+ int sr_ready, ret;
+
+ sr_ready = spi_nor_sr_ready(nor);
+ if (sr_ready < 0)
+ return sr_ready;
+
+ ret = micron_st_nor_read_fsr(nor, nor->bouncebuf);
+ if (ret)
+ return ret;
+
+ if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) {
+ if (nor->bouncebuf[0] & FSR_E_ERR)
+ dev_err(nor->dev, "Erase operation failed.\n");
+ else
+ dev_err(nor->dev, "Program operation failed.\n");
+
+ if (nor->bouncebuf[0] & FSR_PT_ERR)
+ dev_err(nor->dev,
+ "Attempted to modify a protected sector.\n");
+
+ micron_st_nor_clear_fsr(nor);
+
+ /*
+ * WEL bit remains set to one when an erase or page program
+ * error occurs. Issue a Write Disable command to protect
+ * against inadvertent writes that can possibly corrupt the
+ * contents of the memory.
+ */
+ ret = spi_nor_write_disable(nor);
+ if (ret)
+ return ret;
+
+ return -EIO;
+ }
+
+ return sr_ready && !!(nor->bouncebuf[0] & FSR_READY);
+}
+
+static void micron_st_nor_default_init(struct spi_nor *nor)
{
nor->flags |= SNOR_F_HAS_LOCK;
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->params->quad_enable = NULL;
- nor->params->set_4byte_addr_mode = st_micron_set_4byte_addr_mode;
+ nor->params->set_4byte_addr_mode = micron_st_nor_set_4byte_addr_mode;
+}
+
+static void micron_st_nor_late_init(struct spi_nor *nor)
+{
+ if (nor->info->mfr_flags & USE_FSR)
+ nor->params->ready = micron_st_nor_ready;
}
-static const struct spi_nor_fixups micron_st_fixups = {
- .default_init = micron_st_default_init,
+static const struct spi_nor_fixups micron_st_nor_fixups = {
+ .default_init = micron_st_nor_default_init,
+ .late_init = micron_st_nor_late_init,
};
const struct spi_nor_manufacturer spi_nor_micron = {
.name = "micron",
- .parts = micron_parts,
- .nparts = ARRAY_SIZE(micron_parts),
- .fixups = &micron_st_fixups,
+ .parts = micron_nor_parts,
+ .nparts = ARRAY_SIZE(micron_nor_parts),
+ .fixups = &micron_st_nor_fixups,
};
const struct spi_nor_manufacturer spi_nor_st = {
.name = "st",
- .parts = st_parts,
- .nparts = ARRAY_SIZE(st_parts),
- .fixups = &micron_st_fixups,
+ .parts = st_nor_parts,
+ .nparts = ARRAY_SIZE(st_nor_parts),
+ .fixups = &micron_st_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c
index 534196b1d3e7..f24e546e04a5 100644
--- a/drivers/mtd/spi-nor/spansion.c
+++ b/drivers/mtd/spi-nor/spansion.c
@@ -8,6 +8,10 @@
#include "core.h"
+/* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */
+#define USE_CLSR BIT(0)
+
+#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
#define SPINOR_REG_CYPRESS_CFR2V 0x00800003
@@ -20,7 +24,7 @@
#define SPINOR_OP_CYPRESS_RD_FAST 0xee
/**
- * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
+ * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
* @nor: pointer to a 'struct spi_nor'
* @enable: whether to enable or disable Octal DTR
*
@@ -29,7 +33,7 @@
*
* Return: 0 on success, -errno otherwise.
*/
-static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable)
+static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
@@ -116,7 +120,7 @@ static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable)
static void s28hs512t_default_init(struct spi_nor *nor)
{
- nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
+ nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
nor->params->writesize = 16;
}
@@ -183,9 +187,9 @@ static const struct spi_nor_fixups s28hs512t_fixups = {
};
static int
-s25fs_s_post_bfpt_fixups(struct spi_nor *nor,
- const struct sfdp_parameter_header *bfpt_header,
- const struct sfdp_bfpt *bfpt)
+s25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt)
{
/*
* The S25FS-S chip family reports 512-byte pages in BFPT but
@@ -198,11 +202,11 @@ s25fs_s_post_bfpt_fixups(struct spi_nor *nor,
return 0;
}
-static const struct spi_nor_fixups s25fs_s_fixups = {
- .post_bfpt = s25fs_s_post_bfpt_fixups,
+static const struct spi_nor_fixups s25fs_s_nor_fixups = {
+ .post_bfpt = s25fs_s_nor_post_bfpt_fixups,
};
-static const struct flash_info spansion_parts[] = {
+static const struct flash_info spansion_nor_parts[] = {
/* Spansion/Cypress -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
*/
@@ -211,43 +215,53 @@ static const struct flash_info spansion_parts[] = {
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128)
NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fl256s0", INFO6(0x010219, 0x4d0080, 256 * 1024, 128)
- FLAGS(USE_CLSR)
NO_SFDP_FLAGS(SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ |
- SPI_NOR_QUAD_READ) },
+ SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fl256s1", INFO6(0x010219, 0x4d0180, 64 * 1024, 512)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256)
- FLAGS(SPI_NOR_HAS_LOCK | USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ FLAGS(SPI_NOR_HAS_LOCK)
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fs128s1", INFO6(0x012018, 0x4d0181, 64 * 1024, 256)
- FLAGS(USE_CLSR)
NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- .fixups = &s25fs_s_fixups, },
+ MFR_FLAGS(USE_CLSR)
+ .fixups = &s25fs_s_nor_fixups, },
{ "s25fs256s0", INFO6(0x010219, 0x4d0081, 256 * 1024, 128)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256)
- FLAGS(USE_CLSR)
NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- .fixups = &s25fs_s_fixups, },
+ MFR_FLAGS(USE_CLSR)
+ .fixups = &s25fs_s_nor_fixups, },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256)
- FLAGS(USE_CLSR)
- NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ MFR_FLAGS(USE_CLSR)
+ },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32) },
@@ -294,24 +308,92 @@ static const struct flash_info spansion_parts[] = {
},
};
-static void spansion_late_init(struct spi_nor *nor)
+/**
+ * spansion_nor_clear_sr() - Clear the Status Register.
+ * @nor: pointer to 'struct spi_nor'.
+ */
+static void spansion_nor_clear_sr(struct spi_nor *nor)
{
- if (nor->params->size <= SZ_16M)
- return;
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR,
+ NULL, 0);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d clearing SR\n", ret);
+}
+
+/**
+ * spansion_nor_sr_ready_and_clear() - Query the Status Register to see if the
+ * flash is ready for new commands and clear it if there are any errors.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 1 if ready, 0 if not ready, -errno on errors.
+ */
+static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = spi_nor_read_sr(nor, nor->bouncebuf);
+ if (ret)
+ return ret;
+
+ if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) {
+ if (nor->bouncebuf[0] & SR_E_ERR)
+ dev_err(nor->dev, "Erase Error occurred\n");
+ else
+ dev_err(nor->dev, "Programming Error occurred\n");
+
+ spansion_nor_clear_sr(nor);
+
+ /*
+ * WEL bit remains set to one when an erase or page program
+ * error occurs. Issue a Write Disable command to protect
+ * against inadvertent writes that can possibly corrupt the
+ * contents of the memory.
+ */
+ ret = spi_nor_write_disable(nor);
+ if (ret)
+ return ret;
+
+ return -EIO;
+ }
+
+ return !(nor->bouncebuf[0] & SR_WIP);
+}
+
+static void spansion_nor_late_init(struct spi_nor *nor)
+{
+ if (nor->params->size > SZ_16M) {
+ nor->flags |= SNOR_F_4B_OPCODES;
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd.erasesize = nor->info->sector_size;
+ }
- nor->flags |= SNOR_F_4B_OPCODES;
- /* No small sector erase for 4-byte command set */
- nor->erase_opcode = SPINOR_OP_SE;
- nor->mtd.erasesize = nor->info->sector_size;
+ if (nor->info->mfr_flags & USE_CLSR)
+ nor->params->ready = spansion_nor_sr_ready_and_clear;
}
-static const struct spi_nor_fixups spansion_fixups = {
- .late_init = spansion_late_init,
+static const struct spi_nor_fixups spansion_nor_fixups = {
+ .late_init = spansion_nor_late_init,
};
const struct spi_nor_manufacturer spi_nor_spansion = {
.name = "spansion",
- .parts = spansion_parts,
- .nparts = ARRAY_SIZE(spansion_parts),
- .fixups = &spansion_fixups,
+ .parts = spansion_nor_parts,
+ .nparts = ARRAY_SIZE(spansion_nor_parts),
+ .fixups = &spansion_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c
index 30183e9189b9..63bcc97bf978 100644
--- a/drivers/mtd/spi-nor/sst.c
+++ b/drivers/mtd/spi-nor/sst.c
@@ -13,12 +13,12 @@
#define SST26VF_CR_BPNV BIT(3)
-static int sst26vf_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
return -EOPNOTSUPP;
}
-static int sst26vf_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
int ret;
@@ -38,27 +38,27 @@ static int sst26vf_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return spi_nor_global_block_unlock(nor);
}
-static int sst26vf_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
return -EOPNOTSUPP;
}
-static const struct spi_nor_locking_ops sst26vf_locking_ops = {
- .lock = sst26vf_lock,
- .unlock = sst26vf_unlock,
- .is_locked = sst26vf_is_locked,
+static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = {
+ .lock = sst26vf_nor_lock,
+ .unlock = sst26vf_nor_unlock,
+ .is_locked = sst26vf_nor_is_locked,
};
-static void sst26vf_late_init(struct spi_nor *nor)
+static void sst26vf_nor_late_init(struct spi_nor *nor)
{
- nor->params->locking_ops = &sst26vf_locking_ops;
+ nor->params->locking_ops = &sst26vf_nor_locking_ops;
}
-static const struct spi_nor_fixups sst26vf_fixups = {
- .late_init = sst26vf_late_init,
+static const struct spi_nor_fixups sst26vf_nor_fixups = {
+ .late_init = sst26vf_nor_late_init,
};
-static const struct flash_info sst_parts[] = {
+static const struct flash_info sst_nor_parts[] = {
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
@@ -114,11 +114,11 @@ static const struct flash_info sst_parts[] = {
{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
- .fixups = &sst26vf_fixups },
+ .fixups = &sst26vf_nor_fixups },
};
-static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
size_t actual = 0;
@@ -203,19 +203,19 @@ out:
return ret;
}
-static void sst_late_init(struct spi_nor *nor)
+static void sst_nor_late_init(struct spi_nor *nor)
{
if (nor->info->mfr_flags & SST_WRITE)
- nor->mtd._write = sst_write;
+ nor->mtd._write = sst_nor_write;
}
-static const struct spi_nor_fixups sst_fixups = {
- .late_init = sst_late_init,
+static const struct spi_nor_fixups sst_nor_fixups = {
+ .late_init = sst_nor_late_init,
};
const struct spi_nor_manufacturer spi_nor_sst = {
.name = "sst",
- .parts = sst_parts,
- .nparts = ARRAY_SIZE(sst_parts),
- .fixups = &sst_fixups,
+ .parts = sst_nor_parts,
+ .nparts = ARRAY_SIZE(sst_nor_parts),
+ .fixups = &sst_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index 675f32c136b3..fe80dffc2e70 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -32,7 +32,7 @@ static const struct spi_nor_fixups w25q256_fixups = {
.post_bfpt = w25q256_post_bfpt_fixups,
};
-static const struct flash_info winbond_parts[] = {
+static const struct flash_info winbond_nor_parts[] = {
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
{ "w25x05", INFO(0xef3010, 0, 64 * 1024, 1)
NO_SFDP_FLAGS(SECT_4K) },
@@ -130,14 +130,15 @@ static const struct flash_info winbond_parts[] = {
};
/**
- * winbond_set_4byte_addr_mode() - Set 4-byte address mode for Winbond flashes.
+ * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
+ * flashes.
* @nor: pointer to 'struct spi_nor'.
* @enable: true to enter the 4-byte address mode, false to exit the 4-byte
* address mode.
*
* Return: 0 on success, -errno otherwise.
*/
-static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
{
int ret;
@@ -161,7 +162,7 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
return spi_nor_write_disable(nor);
}
-static const struct spi_nor_otp_ops winbond_otp_ops = {
+static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
.read = spi_nor_otp_read_secr,
.write = spi_nor_otp_write_secr,
.erase = spi_nor_otp_erase_secr,
@@ -169,25 +170,25 @@ static const struct spi_nor_otp_ops winbond_otp_ops = {
.is_locked = spi_nor_otp_is_locked_sr2,
};
-static void winbond_default_init(struct spi_nor *nor)
+static void winbond_nor_default_init(struct spi_nor *nor)
{
- nor->params->set_4byte_addr_mode = winbond_set_4byte_addr_mode;
+ nor->params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
}
-static void winbond_late_init(struct spi_nor *nor)
+static void winbond_nor_late_init(struct spi_nor *nor)
{
if (nor->params->otp.org->n_regions)
- nor->params->otp.ops = &winbond_otp_ops;
+ nor->params->otp.ops = &winbond_nor_otp_ops;
}
-static const struct spi_nor_fixups winbond_fixups = {
- .default_init = winbond_default_init,
- .late_init = winbond_late_init,
+static const struct spi_nor_fixups winbond_nor_fixups = {
+ .default_init = winbond_nor_default_init,
+ .late_init = winbond_nor_late_init,
};
const struct spi_nor_manufacturer spi_nor_winbond = {
.name = "winbond",
- .parts = winbond_parts,
- .nparts = ARRAY_SIZE(winbond_parts),
- .fixups = &winbond_fixups,
+ .parts = winbond_nor_parts,
+ .nparts = ARRAY_SIZE(winbond_nor_parts),
+ .fixups = &winbond_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c
index 580562bc1e45..9459ac2609dc 100644
--- a/drivers/mtd/spi-nor/xilinx.c
+++ b/drivers/mtd/spi-nor/xilinx.c
@@ -8,7 +8,28 @@
#include "core.h"
-static const struct flash_info xilinx_parts[] = {
+#define XILINX_OP_SE 0x50 /* Sector erase */
+#define XILINX_OP_PP 0x82 /* Page program */
+#define XILINX_OP_RDSR 0xd7 /* Read status register */
+
+#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */
+#define XSR_RDY BIT(7) /* Ready */
+
+#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
+ .id = { \
+ ((_jedec_id) >> 16) & 0xff, \
+ ((_jedec_id) >> 8) & 0xff, \
+ (_jedec_id) & 0xff \
+ }, \
+ .id_len = 3, \
+ .sector_size = (8 * (_page_size)), \
+ .n_sectors = (_n_sectors), \
+ .page_size = (_page_size), \
+ .addr_width = 3, \
+ .flags = SPI_NOR_NO_FR
+
+/* Xilinx S3AN share MFR with Atmel SPI NOR */
+static const struct flash_info xilinx_nor_parts[] = {
/* Xilinx S3AN Internal Flash */
{ "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
{ "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
@@ -26,7 +47,7 @@ static const struct flash_info xilinx_parts[] = {
* Addr can safely be unsigned int, the biggest S3AN device is smaller than
* 4 MiB.
*/
-static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr)
+static u32 s3an_nor_convert_addr(struct spi_nor *nor, u32 addr)
{
u32 page_size = nor->params->page_size;
u32 offset, page;
@@ -38,18 +59,69 @@ static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr)
return page | offset;
}
+/**
+ * xilinx_nor_read_sr() - Read the Status Register on S3AN flashes.
+ * @nor: pointer to 'struct spi_nor'.
+ * @sr: pointer to a DMA-able buffer where the value of the
+ * Status Register will be written.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, sr, 0));
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = spi_nor_controller_ops_read_reg(nor, XILINX_OP_RDSR, sr,
+ 1);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d reading SR\n", ret);
+
+ return ret;
+}
+
+/**
+ * xilinx_nor_sr_ready() - Query the Status Register of the S3AN flash to see
+ * if the flash is ready for new commands.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 1 if ready, 0 if not ready, -errno on errors.
+ */
+static int xilinx_nor_sr_ready(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = xilinx_nor_read_sr(nor, nor->bouncebuf);
+ if (ret)
+ return ret;
+
+ return !!(nor->bouncebuf[0] & XSR_RDY);
+}
+
static int xilinx_nor_setup(struct spi_nor *nor,
const struct spi_nor_hwcaps *hwcaps)
{
u32 page_size;
int ret;
- ret = spi_nor_xread_sr(nor, nor->bouncebuf);
+ ret = xilinx_nor_read_sr(nor, nor->bouncebuf);
if (ret)
return ret;
- nor->erase_opcode = SPINOR_OP_XSE;
- nor->program_opcode = SPINOR_OP_XPP;
+ nor->erase_opcode = XILINX_OP_SE;
+ nor->program_opcode = XILINX_OP_PP;
nor->read_opcode = SPINOR_OP_READ;
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
@@ -73,25 +145,26 @@ static int xilinx_nor_setup(struct spi_nor *nor,
nor->mtd.erasesize = 8 * page_size;
} else {
/* Flash in Default addressing mode */
- nor->params->convert_addr = s3an_convert_addr;
+ nor->params->convert_addr = s3an_nor_convert_addr;
nor->mtd.erasesize = nor->info->sector_size;
}
return 0;
}
-static void xilinx_late_init(struct spi_nor *nor)
+static void xilinx_nor_late_init(struct spi_nor *nor)
{
nor->params->setup = xilinx_nor_setup;
+ nor->params->ready = xilinx_nor_sr_ready;
}
-static const struct spi_nor_fixups xilinx_fixups = {
- .late_init = xilinx_late_init,
+static const struct spi_nor_fixups xilinx_nor_fixups = {
+ .late_init = xilinx_nor_late_init,
};
const struct spi_nor_manufacturer spi_nor_xilinx = {
.name = "xilinx",
- .parts = xilinx_parts,
- .nparts = ARRAY_SIZE(xilinx_parts),
- .fixups = &xilinx_fixups,
+ .parts = xilinx_nor_parts,
+ .nparts = ARRAY_SIZE(xilinx_nor_parts),
+ .fixups = &xilinx_nor_fixups,
};
diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c
index 2992af03cb0a..051411e86339 100644
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -8,7 +8,7 @@
#include "core.h"
-static const struct flash_info xmc_parts[] = {
+static const struct flash_info xmc_nor_parts[] = {
/* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
{ "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
@@ -20,6 +20,6 @@ static const struct flash_info xmc_parts[] = {
const struct spi_nor_manufacturer spi_nor_xmc = {
.name = "xmc",
- .parts = xmc_parts,
- .nparts = ARRAY_SIZE(xmc_parts),
+ .parts = xmc_nor_parts,
+ .nparts = ARRAY_SIZE(xmc_nor_parts),
};
diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c
index 93e76648f676..c9ec7086bfa1 100644
--- a/drivers/mtd/tests/speedtest.c
+++ b/drivers/mtd/tests/speedtest.c
@@ -160,14 +160,13 @@ static inline void stop_timing(void)
static long calc_speed(void)
{
- uint64_t k;
- long ms;
+ uint64_t k, us;
- ms = ktime_ms_delta(finish, start);
- if (ms == 0)
+ us = ktime_us_delta(finish, start);
+ if (us == 0)
return 0;
- k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000;
- do_div(k, ms);
+ k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000000;
+ do_div(k, us);
return k;
}