summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2022-12-05 15:37:27 +0100
committerMiquel Raynal <miquel.raynal@bootlin.com>2022-12-05 15:37:27 +0100
commit1d46f1ae82494fae60802c1ed09ca5009e1b414c (patch)
treeb5a3d8e47289a97b8e6d2da2075cee14ef2dac23 /drivers/mtd
parent2399401feee27c639addc5b7e6ba519d3ca341bf (diff)
parent6408cc05a50aaf88074a5a31d065e5af87a456f5 (diff)
downloadlwn-1d46f1ae82494fae60802c1ed09ca5009e1b414c.tar.gz
lwn-1d46f1ae82494fae60802c1ed09ca5009e1b414c.zip
Merge tag 'nand/for-6.2' into mtd/next
Raw NAND core changes: * Drop obsolete dependencies on COMPILE_TEST * MAINTAINERS: rectify entry for MESON NAND controller bindings * Drop EXPORT_SYMBOL_GPL for nanddev_erase() Raw NAND driver changes: * marvell: Enable NFC/DEVBUS arbiter * gpmi: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync * mpc5121: Replace NO_IRQ by 0 * lpc32xx_{slc,mlc}: - Switch to using pm_ptr() - Switch to using gpiod API * lpc32xx_mlc: Switch to using pm_ptr() * cadence: Support 64-bit slave dma interface * rockchip: Describe rk3128-nfc in the bindings * brcmnand: Update interrupts description in the bindings SPI-NAND driver changes: * winbond: - Add Winbond W25N02KV flash support - Fix flash identification Fix merge conflict with mtd tree regarding the brcm bindings. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/core.c3
-rw-r--r--drivers/mtd/nand/raw/Kconfig6
-rw-r--r--drivers/mtd/nand/raw/cadence-nand-controller.c70
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c12
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c46
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c43
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c4
-rw-r--r--drivers/mtd/nand/raw/mpc5121_nfc.c2
-rw-r--r--drivers/mtd/nand/spi/winbond.c79
9 files changed, 187 insertions, 78 deletions
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
index dbd7b06524b3..7737b1a4a177 100644
--- a/drivers/mtd/nand/core.c
+++ b/drivers/mtd/nand/core.c
@@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved);
*
* Return: 0 in case of success, a negative error code otherwise.
*/
-int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
+static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
{
if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
pr_warn("attempt to erase a bad/reserved block @%llx\n",
@@ -136,7 +136,6 @@ int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
return nand->ops->erase(nand, pos);
}
-EXPORT_SYMBOL_GPL(nanddev_erase);
/**
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 4cd40af362de..98ea1c9e65c8 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -415,7 +415,7 @@ config MTD_NAND_PLATFORM
config MTD_NAND_CADENCE
tristate "Support Cadence NAND (HPNFC) controller"
- depends on (OF || COMPILE_TEST) && HAS_IOMEM
+ depends on OF && HAS_IOMEM
help
Enable the driver for NAND flash on platforms using a Cadence NAND
controller.
@@ -430,7 +430,7 @@ config MTD_NAND_ARASAN
config MTD_NAND_INTEL_LGM
tristate "Support for NAND controller on Intel LGM SoC"
- depends on OF || COMPILE_TEST
+ depends on OF
depends on HAS_IOMEM
help
Enables support for NAND Flash chips on Intel's LGM SoC.
@@ -450,7 +450,7 @@ config MTD_NAND_ROCKCHIP
config MTD_NAND_PL35X
tristate "ARM PL35X NAND controller"
- depends on OF || COMPILE_TEST
+ depends on OF
depends on PL353_SMC
help
Enables support for PrimeCell SMC PL351 and PL353 NAND
diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index 9dac3ca69d57..7661a5cf1883 100644
--- a/drivers/mtd/nand/raw/cadence-nand-controller.c
+++ b/drivers/mtd/nand/raw/cadence-nand-controller.c
@@ -1184,6 +1184,14 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl)
if (cadence_nand_read_bch_caps(cdns_ctrl))
return -EIO;
+#ifndef CONFIG_64BIT
+ if (cdns_ctrl->caps2.data_dma_width == 8) {
+ dev_err(cdns_ctrl->dev,
+ "cannot access 64-bit dma on !64-bit architectures");
+ return -EIO;
+ }
+#endif
+
/*
* Set IO width access to 8.
* It is because during SW device discovering width access
@@ -1882,17 +1890,36 @@ static int cadence_nand_read_buf(struct cdns_nand_ctrl *cdns_ctrl,
return status;
if (!cdns_ctrl->caps1->has_dma) {
- int len_in_words = len >> 2;
+ u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
+
+ int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
/* read alingment data */
- ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
+ if (data_dma_width == 4)
+ ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
+#ifdef CONFIG_64BIT
+ else
+ readsq(cdns_ctrl->io.virt, buf, len_in_words);
+#endif
+
if (sdma_size > len) {
+ int read_bytes = (data_dma_width == 4) ?
+ len_in_words << 2 : len_in_words << 3;
+
/* read rest data from slave DMA interface if any */
- ioread32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
- sdma_size / 4 - len_in_words);
+ if (data_dma_width == 4)
+ ioread32_rep(cdns_ctrl->io.virt,
+ cdns_ctrl->buf,
+ sdma_size / 4 - len_in_words);
+#ifdef CONFIG_64BIT
+ else
+ readsq(cdns_ctrl->io.virt, cdns_ctrl->buf,
+ sdma_size / 8 - len_in_words);
+#endif
+
/* copy rest of data */
- memcpy(buf + (len_in_words << 2), cdns_ctrl->buf,
- len - (len_in_words << 2));
+ memcpy(buf + read_bytes, cdns_ctrl->buf,
+ len - read_bytes);
}
return 0;
}
@@ -1936,16 +1963,35 @@ static int cadence_nand_write_buf(struct cdns_nand_ctrl *cdns_ctrl,
return status;
if (!cdns_ctrl->caps1->has_dma) {
- int len_in_words = len >> 2;
+ u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
+
+ int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
+
+ if (data_dma_width == 4)
+ iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
+#ifdef CONFIG_64BIT
+ else
+ writesq(cdns_ctrl->io.virt, buf, len_in_words);
+#endif
- iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
if (sdma_size > len) {
+ int written_bytes = (data_dma_width == 4) ?
+ len_in_words << 2 : len_in_words << 3;
+
/* copy rest of data */
- memcpy(cdns_ctrl->buf, buf + (len_in_words << 2),
- len - (len_in_words << 2));
+ memcpy(cdns_ctrl->buf, buf + written_bytes,
+ len - written_bytes);
+
/* write all expected by nand controller data */
- iowrite32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
- sdma_size / 4 - len_in_words);
+ if (data_dma_width == 4)
+ iowrite32_rep(cdns_ctrl->io.virt,
+ cdns_ctrl->buf,
+ sdma_size / 4 - len_in_words);
+#ifdef CONFIG_64BIT
+ else
+ writesq(cdns_ctrl->io.virt, cdns_ctrl->buf,
+ sdma_size / 8 - len_in_words);
+#endif
}
return 0;
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index 01ccbde748f3..ada83344b0f9 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -148,11 +148,9 @@ static int gpmi_init(struct gpmi_nand_data *this)
struct resources *r = &this->resources;
int ret;
- ret = pm_runtime_get_sync(this->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(this->dev);
+ ret = pm_runtime_resume_and_get(this->dev);
+ if (ret < 0)
return ret;
- }
ret = gpmi_reset_block(r->gpmi_regs, false);
if (ret)
@@ -2504,11 +2502,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
this->transfers[i].direction = DMA_NONE;
- ret = pm_runtime_get_sync(this->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(this->dev);
+ ret = pm_runtime_resume_and_get(this->dev);
+ if (ret < 0)
return ret;
- }
/*
* This driver currently supports only one NAND chip. Plus, dies share
diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index 452ecaf7775a..ae7f6429a5f6 100644
--- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
@@ -25,7 +25,7 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h>
#include <linux/mm.h>
@@ -122,7 +122,6 @@ struct lpc32xx_nand_cfg_mlc {
uint32_t rd_low;
uint32_t wr_high;
uint32_t wr_low;
- int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
};
@@ -177,6 +176,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk;
+ struct gpio_desc *wp_gpio;
void __iomem *io_base;
int irq;
struct lpc32xx_nand_cfg_mlc *ncfg;
@@ -370,8 +370,8 @@ static int lpc32xx_waitfunc(struct nand_chip *chip)
*/
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
{
- if (gpio_is_valid(host->ncfg->wp_gpio))
- gpio_set_value(host->ncfg->wp_gpio, 0);
+ if (host->wp_gpio)
+ gpiod_set_value_cansleep(host->wp_gpio, 1);
}
/*
@@ -379,8 +379,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
*/
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
{
- if (gpio_is_valid(host->ncfg->wp_gpio))
- gpio_set_value(host->ncfg->wp_gpio, 1);
+ if (host->wp_gpio)
+ gpiod_set_value_cansleep(host->wp_gpio, 0);
}
static void lpc32xx_dma_complete_func(void *completion)
@@ -636,8 +636,6 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
return NULL;
}
- ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
-
return ncfg;
}
@@ -713,14 +711,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n");
return -ENOENT;
}
- if (host->ncfg->wp_gpio == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (gpio_is_valid(host->ncfg->wp_gpio) &&
- gpio_request(host->ncfg->wp_gpio, "NAND WP")) {
- dev_err(&pdev->dev, "GPIO not available\n");
- return -EBUSY;
+
+ /* Start with WP disabled, if available */
+ host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
+ res = PTR_ERR_OR_ZERO(host->wp_gpio);
+ if (res) {
+ if (res != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
+ res);
+ return res;
}
- lpc32xx_wp_disable(host);
+
+ gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev);
@@ -817,7 +819,7 @@ put_clk:
clk_put(host->clk);
free_gpio:
lpc32xx_wp_enable(host);
- gpio_free(host->ncfg->wp_gpio);
+ gpiod_put(host->wp_gpio);
return res;
}
@@ -843,12 +845,11 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
clk_put(host->clk);
lpc32xx_wp_enable(host);
- gpio_free(host->ncfg->wp_gpio);
+ gpiod_put(host->wp_gpio);
return 0;
}
-#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
@@ -880,11 +881,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
return 0;
}
-#else
-#define lpc32xx_nand_resume NULL
-#define lpc32xx_nand_suspend NULL
-#endif
-
static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-mlc" },
{ /* sentinel */ },
@@ -894,8 +890,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove,
- .resume = lpc32xx_nand_resume,
- .suspend = lpc32xx_nand_suspend,
+ .resume = pm_ptr(lpc32xx_nand_resume),
+ .suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = {
.name = DRV_NAME,
.of_match_table = lpc32xx_nand_match,
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 6b7269cfb7d8..6918737346c9 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -23,9 +23,8 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_slc.h>
#define LPC32XX_MODNAME "lpc32xx-nand"
@@ -208,7 +207,6 @@ struct lpc32xx_nand_cfg_slc {
uint32_t rwidth;
uint32_t rhold;
uint32_t rsetup;
- int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
};
@@ -217,6 +215,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_slc_platform_data *pdata;
struct clk *clk;
+ struct gpio_desc *wp_gpio;
void __iomem *io_base;
struct lpc32xx_nand_cfg_slc *ncfg;
@@ -309,8 +308,8 @@ static int lpc32xx_nand_device_ready(struct nand_chip *chip)
*/
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
{
- if (gpio_is_valid(host->ncfg->wp_gpio))
- gpio_set_value(host->ncfg->wp_gpio, 0);
+ if (host->wp_gpio)
+ gpiod_set_value_cansleep(host->wp_gpio, 1);
}
/*
@@ -318,8 +317,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
*/
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
{
- if (gpio_is_valid(host->ncfg->wp_gpio))
- gpio_set_value(host->ncfg->wp_gpio, 1);
+ if (host->wp_gpio)
+ gpiod_set_value_cansleep(host->wp_gpio, 0);
}
/*
@@ -764,8 +763,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
return NULL;
}
- ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
-
return ncfg;
}
@@ -852,14 +849,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n");
return -ENOENT;
}
- if (host->ncfg->wp_gpio == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev,
- host->ncfg->wp_gpio, "NAND WP")) {
- dev_err(&pdev->dev, "GPIO not available\n");
- return -EBUSY;
+
+ /* Start with WP disabled, if available */
+ host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
+ res = PTR_ERR_OR_ZERO(host->wp_gpio);
+ if (res) {
+ if (res != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
+ res);
+ return res;
}
- lpc32xx_wp_disable(host);
+
+ gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev);
@@ -968,7 +969,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
@@ -1007,11 +1007,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
return 0;
}
-#else
-#define lpc32xx_nand_resume NULL
-#define lpc32xx_nand_suspend NULL
-#endif
-
static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-slc" },
{ /* sentinel */ },
@@ -1021,8 +1016,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove,
- .resume = lpc32xx_nand_resume,
- .suspend = lpc32xx_nand_suspend,
+ .resume = pm_ptr(lpc32xx_nand_resume),
+ .suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = {
.name = LPC32XX_MODNAME,
.of_match_table = lpc32xx_nand_match,
diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index b9d1e96e3334..42c64dcea767 100644
--- a/drivers/mtd/nand/raw/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -114,6 +114,7 @@
#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
+#define GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN BIT(27)
#define GENCONF_CLK_GATING_CTRL 0x220
#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
#define GENCONF_ND_CLK_CTRL 0x700
@@ -2880,7 +2881,8 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
GENCONF_SOC_DEVICE_MUX_NFC_EN |
GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
- GENCONF_SOC_DEVICE_MUX_NFC_INT_EN);
+ GENCONF_SOC_DEVICE_MUX_NFC_INT_EN |
+ GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN);
regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL,
GENCONF_CLK_GATING_CTRL_ND_GATE,
diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c
index 800d774aed8e..f68349cb7824 100644
--- a/drivers/mtd/nand/raw/mpc5121_nfc.c
+++ b/drivers/mtd/nand/raw/mpc5121_nfc.c
@@ -663,7 +663,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
}
prv->irq = irq_of_parse_and_map(dn, 0);
- if (prv->irq == NO_IRQ) {
+ if (!prv->irq) {
dev_err(dev, "Error mapping IRQ!\n");
return -EINVAL;
}
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 76684428354e..3ad58cd284d8 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -74,9 +74,75 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
return spi_mem_exec_op(spinand->spimem, &op);
}
+static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = 64 + (16 * section);
+ region->length = 13;
+
+ return 0;
+}
+
+static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = (16 * section) + 2;
+ region->length = 14;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
+ .ecc = w25n02kv_ooblayout_ecc,
+ .free = w25n02kv_ooblayout_free,
+};
+
+static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
+ u8 status)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ u8 mbf = 0;
+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /*
+ * Let's try to retrieve the real maximum number of bitflips
+ * in order to avoid forcing the wear-leveling layer to move
+ * data around if it's not necessary.
+ */
+ if (spi_mem_exec_op(spinand->spimem, &op))
+ return nanddev_get_ecc_conf(nand)->strength;
+
+ mbf >>= 4;
+
+ if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
+ return nanddev_get_ecc_conf(nand)->strength;
+
+ return mbf;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
static const struct spinand_info winbond_spinand_table[] = {
SPINAND_INFO("W25M02GV",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@@ -86,7 +152,7 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
SPINAND_INFO("W25N01GV",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa),
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@@ -94,6 +160,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+ SPINAND_INFO("W25N02KV",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
};
static int winbond_spinand_init(struct spinand_device *spinand)