diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2022-12-05 15:37:27 +0100 |
---|---|---|
committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2022-12-05 15:37:27 +0100 |
commit | 1d46f1ae82494fae60802c1ed09ca5009e1b414c (patch) | |
tree | b5a3d8e47289a97b8e6d2da2075cee14ef2dac23 /drivers/mtd/nand | |
parent | mtd: maps: pxa2xx-flash: fix memory leak in probe (diff) | |
parent | mtd: rawnand: Drop obsolete dependencies on COMPILE_TEST (diff) | |
download | linux-1d46f1ae82494fae60802c1ed09ca5009e1b414c.tar.xz linux-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/nand')
-rw-r--r-- | drivers/mtd/nand/core.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/cadence-nand-controller.c | 70 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_mlc.c | 46 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_slc.c | 43 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/marvell_nand.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mpc5121_nfc.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/spi/winbond.c | 79 |
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) |