From 43d8b6362378913bafbc54690474131568458c42 Mon Sep 17 00:00:00 2001 From: Martin Devera Date: Thu, 16 Jan 2020 14:54:31 +0100 Subject: [PATCH 01/55] mtd: rawnand: Ensure nand_soft_waitrdy wait period is enough The used way to compute jiffies timeout brokes when jiffie difference is 1. Assume that nand_soft_waitrdy is called with timeout_ms==1. Jiffies are 1000 for example (assume something more like 1000.99 - just before incrementing to 1001). We compute timeout_ms = 1000+msecs_to_jiffies(1) = 1001. nand_read_data_op is called for the first time and returns 0. During the call jiffies changes to 1001 thus "while loop" ends here (wrongly). Notice that routine was called with expected timeout 1ms but actual timeout used was something between 0...1ms. Fixes STM32MP1 FMC2 NAND controller which sometimes failed exactly in this way. Signed-off-by: Martin Devera Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200116135431.17480-1-devik@eaxlabs.cz --- drivers/mtd/nand/raw/nand_base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f64e3b6605c6..8ad4af99eea4 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -683,7 +683,12 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) if (ret) return ret; - timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); + /* + * +1 below is necessary because if we are now in the last fraction + * of jiffy and msecs_to_jiffies is 1 then we will wait only that + * small jiffy fraction - possibly leading to false timeout + */ + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms) + 1; do { ret = nand_read_data_op(chip, &status, sizeof(status), true); if (ret) From 009264605cdf1b12962c3a46f75818d05452e890 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Thu, 23 Jan 2020 09:22:48 +0100 Subject: [PATCH 02/55] mtd: rawnand: free the nand_device object This patch releases the resources allocated in nanddev_init function. Fixes: a7ab085d7c16 ("mtd: rawnand: Initialize the nand_device object") Signed-off-by: Christophe Kerello Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1579767768-32295-1-git-send-email-christophe.kerello@st.com --- drivers/mtd/nand/raw/nand_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 8ad4af99eea4..a3ed6c54963e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5912,6 +5912,8 @@ void nand_cleanup(struct nand_chip *chip) chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + nanddev_cleanup(&chip->base); + /* Free bad block table memory */ kfree(chip->bbt); kfree(chip->data_buf); From 9afbe7c0140f663586edb6e823b616bd7076c00a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 27 Jan 2020 21:39:34 +0900 Subject: [PATCH 03/55] mtd: rawnand: denali: deassert write protect pin If the write protect signal from this IP is connected to the NAND device, this IP can handle the WP# pin via the WRITE_PROTECT register. The Denali NAND Flash Memory Controller User's Guide describes this register like follows: When the controller is in reset, the WP# pin is always asserted to the device. Once the reset is removed, the WP# is de-asserted. The software will then have to come and program this bit to assert/de-assert the same. 1 - Write protect de-assert 0 - Write protect assert The default value is 1, so the write protect is de-asserted after the reset is removed. The driver can write to the device unless someone has explicitly cleared register before booting the kernel. The boot ROM of some UniPhier SoCs (LD4, Pro4, sLD8, Pro5) is the case; the boot ROM clears the WRITE_PROTECT register when the system is booting from the NAND device, so the NAND device becomes read-only. Set it to 1 in the driver in order to allow the write access to the device. Signed-off-by: Masahiro Yamada Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200127123934.11847-1-yamada.masahiro@socionext.com --- drivers/mtd/nand/raw/denali.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index fafd0a0aa8e2..6a6c919b2569 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1317,6 +1317,7 @@ int denali_init(struct denali_controller *denali) iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); iowrite32(ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); + iowrite32(WRITE_PROTECT__FLAG, denali->reg + WRITE_PROTECT); denali_clear_irq_all(denali); From a91f8170df832967dc75d5bd594c496999882e22 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Fri, 7 Feb 2020 13:59:21 +0900 Subject: [PATCH 04/55] mtd: spinand: toshiba: Add comment about Kioxia ID Add a comment above NAND_MFR_TOSHIBA and SPINAND_MFR_TOSHIBA definitions that Toshiba and Kioxia ID are the same. Since its independence from Toshiba Group, Toshiba memory Co has become Kioxia Co. Signed-off-by: Yoshio Furuyama Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581051561-7302-1-git-send-email-ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/raw/internals.h | 1 + drivers/mtd/nand/spi/toshiba.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index cba6fe7dd8c4..9d0caadf940e 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -30,6 +30,7 @@ #define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_SANDISK 0x45 #define NAND_MFR_STMICRO 0x20 +/* Kioxia is new name of Toshiba memory. */ #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_WINBOND 0xef diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 0db5ee4e82af..833e8f64e0a0 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -10,6 +10,7 @@ #include #include +/* Kioxia is new name of Toshiba memory. */ #define SPINAND_MFR_TOSHIBA 0x98 #define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) From f1541773af49ecd1edae29c8ac0775253a0b0760 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sat, 8 Feb 2020 15:43:50 +0800 Subject: [PATCH 05/55] mtd: spinand: rework detect procedure for different READ_ID operation Currently there are 3 different variants of read_id implementation: 1. opcode only. Found in GD5FxGQ4xF. 2. opcode + 1 addr byte. Found in GD5GxGQ4xA/E 3. opcode + 1 dummy byte. Found in other currently supported chips. Original implementation was for variant 1 and let detect function of chips with variant 2 and 3 to ignore the first byte. This isn't robust: 1. For chips of variant 2, if SPI master doesn't keep MOSI low during read, chip will get a random id offset, and the entire id buffer will shift by that offset, causing detect failure. 2. For chips of variant 1, if it happens to get a devid that equals to manufacture id of variant 2 or 3 chips, it'll get incorrectly detected. This patch reworks detect procedure to address problems above. New logic do detection for all variants separatedly, in 1-2-3 order. Since all current detect methods do exactly the same id matching procedure, unify them into core.c and remove detect method from manufacture_ops. Tested on GD5F1GQ4UAYIG and W25N01GVZEIG. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200208074439.146296-1-gch981213@gmail.com --- drivers/mtd/nand/spi/core.c | 86 ++++++++++++++++++++++--------- drivers/mtd/nand/spi/gigadevice.c | 45 +++++----------- drivers/mtd/nand/spi/macronix.c | 30 +++-------- drivers/mtd/nand/spi/micron.c | 26 ++-------- drivers/mtd/nand/spi/paragon.c | 28 +++------- drivers/mtd/nand/spi/toshiba.c | 45 ++++++---------- drivers/mtd/nand/spi/winbond.c | 34 +++--------- include/linux/mtd/spinand.h | 66 ++++++++++++++++-------- 8 files changed, 157 insertions(+), 203 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 89f6beefb01c..a9e9cbad942f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -370,10 +371,11 @@ static int spinand_wait(struct spinand_device *spinand, u8 *s) return status & STATUS_BUSY ? -ETIMEDOUT : 0; } -static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, + u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, - SPINAND_MAX_ID_LEN); + struct spi_mem_op op = SPINAND_READID_OP( + naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -762,24 +764,62 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &winbond_spinand_manufacturer, }; -static int spinand_manufacturer_detect(struct spinand_device *spinand) +static int spinand_manufacturer_match(struct spinand_device *spinand, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { - ret = spinand_manufacturers[i]->ops->detect(spinand); - if (ret > 0) { - spinand->manufacturer = spinand_manufacturers[i]; - return 0; - } else if (ret < 0) { - return ret; - } - } + const struct spinand_manufacturer *manufacturer = + spinand_manufacturers[i]; + if (id[0] != manufacturer->id) + continue; + + ret = spinand_match_and_init(spinand, + manufacturer->chips, + manufacturer->nchips, + rdid_method); + if (ret < 0) + continue; + + spinand->manufacturer = manufacturer; + return 0; + } return -ENOTSUPP; } +static int spinand_id_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + ret = spinand_read_id_op(spinand, 0, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 1, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_ADDR); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 0, 1, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_DUMMY); + + return ret; +} + static int spinand_manufacturer_init(struct spinand_device *spinand) { if (spinand->manufacturer->ops->init) @@ -835,9 +875,9 @@ spinand_select_op_variant(struct spinand_device *spinand, * @spinand: SPI NAND object * @table: SPI NAND device description table * @table_size: size of the device description table + * @rdid_method: read id method to match * - * Should be used by SPI NAND manufacturer drivers when they want to find a - * match between a device ID retrieved through the READ_ID command and an + * Match between a device ID retrieved through the READ_ID command and an * entry in the SPI NAND description table. If a match is found, the spinand * object will be initialized with information provided by the matching * spinand_info entry. @@ -846,8 +886,10 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid) + unsigned int table_size, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; @@ -855,13 +897,17 @@ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *info = &table[i]; const struct spi_mem_op *op; - if (devid != info->devid) + if (rdid_method != info->devid.method) + continue; + + if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; spinand->flags = table[i].flags; + spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; op = spinand_select_op_variant(spinand, @@ -898,13 +944,7 @@ static int spinand_detect(struct spinand_device *spinand) if (ret) return ret; - ret = spinand_read_id_op(spinand, spinand->id.data); - if (ret) - return ret; - - spinand->id.len = SPINAND_MAX_ID_LEN; - - ret = spinand_manufacturer_detect(spinand); + ret = spinand_id_detect(spinand); if (ret) { dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, spinand->id.data); diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index e99d425aa93f..d219c970042a 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -195,7 +195,8 @@ static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info gigadevice_spinand_table[] = { - SPINAND_INFO("GD5F1GQ4xA", 0xF1, + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -204,7 +205,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F2GQ4xA", 0xF2, + SPINAND_INFO("GD5F2GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -213,7 +215,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F4GQ4xA", 0xF4, + SPINAND_INFO("GD5F4GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -222,7 +225,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, + SPINAND_INFO("GD5F1GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -231,7 +235,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, @@ -242,39 +247,13 @@ static const struct spinand_info gigadevice_spinand_table[] = { gd5fxgq4ufxxg_ecc_get_status)), }; -static int gigadevice_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - u16 did; - int ret; - - /* - * Earlier GDF5-series devices (A,E) return [0][MID][DID] - * Later (F) devices return [MID][DID1][DID2] - */ - - if (id[0] == SPINAND_MFR_GIGADEVICE) - did = (id[1] << 8) + id[2]; - else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) - did = id[2]; - else - return 0; - - ret = spinand_match_and_init(spinand, gigadevice_spinand_table, - ARRAY_SIZE(gigadevice_spinand_table), - did); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { - .detect = gigadevice_spinand_detect, }; const struct spinand_manufacturer gigadevice_spinand_manufacturer = { .id = SPINAND_MFR_GIGADEVICE, .name = "GigaDevice", + .chips = gigadevice_spinand_table, + .nchips = ARRAY_SIZE(gigadevice_spinand_table), .ops = &gigadevice_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 21def3f8fb36..0f900f3aa21a 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -99,7 +99,8 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info macronix_spinand_table[] = { - SPINAND_INFO("MX35LF1GE4AB", 0x12, + SPINAND_INFO("MX35LF1GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -108,7 +109,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35LF2GE4AB", 0x22, + SPINAND_INFO("MX35LF2GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -118,33 +120,13 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; -static int macronix_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Macronix SPI NAND read ID needs a dummy byte, so the first byte in - * raw_id is garbage. - */ - if (id[1] != SPINAND_MFR_MACRONIX) - return 0; - - ret = spinand_match_and_init(spinand, macronix_spinand_table, - ARRAY_SIZE(macronix_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { - .detect = macronix_spinand_detect, }; const struct spinand_manufacturer macronix_spinand_manufacturer = { .id = SPINAND_MFR_MACRONIX, .name = "Macronix", + .chips = macronix_spinand_table, + .nchips = ARRAY_SIZE(macronix_spinand_table), .ops = ¯onix_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 7d7b1f7fcf71..f56f81325e10 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -91,7 +91,8 @@ static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info micron_spinand_table[] = { - SPINAND_INFO("MT29F2G01ABAGD", 0x24, + SPINAND_INFO("MT29F2G01ABAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -102,32 +103,13 @@ static const struct spinand_info micron_spinand_table[] = { mt29f2g01abagd_ecc_get_status)), }; -static int micron_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Micron SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_MICRON) - return 0; - - ret = spinand_match_and_init(spinand, micron_spinand_table, - ARRAY_SIZE(micron_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { - .detect = micron_spinand_detect, }; const struct spinand_manufacturer micron_spinand_manufacturer = { .id = SPINAND_MFR_MICRON, .name = "Micron", + .chips = micron_spinand_table, + .nchips = ARRAY_SIZE(micron_spinand_table), .ops = µn_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c index 52307681cbd0..519ade513c1f 100644 --- a/drivers/mtd/nand/spi/paragon.c +++ b/drivers/mtd/nand/spi/paragon.c @@ -97,7 +97,8 @@ static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { static const struct spinand_info paragon_spinand_table[] = { - SPINAND_INFO("PN26G01A", 0xe1, + SPINAND_INFO("PN26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1), NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info paragon_spinand_table[] = { 0, SPINAND_ECCINFO(&pn26g0xa_ooblayout, pn26g0xa_ecc_get_status)), - SPINAND_INFO("PN26G02A", 0xe2, + SPINAND_INFO("PN26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2), NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -117,31 +119,13 @@ static const struct spinand_info paragon_spinand_table[] = { pn26g0xa_ecc_get_status)), }; -static int paragon_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* Read ID returns [0][MID][DID] */ - - if (id[1] != SPINAND_MFR_PARAGON) - return 0; - - ret = spinand_match_and_init(spinand, paragon_spinand_table, - ARRAY_SIZE(paragon_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { - .detect = paragon_spinand_detect, }; const struct spinand_manufacturer paragon_spinand_manufacturer = { .id = SPINAND_MFR_PARAGON, .name = "Paragon", + .chips = paragon_spinand_table, + .nchips = ARRAY_SIZE(paragon_spinand_table), .ops = ¶gon_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 833e8f64e0a0..d34773191700 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -96,7 +96,8 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb */ - SPINAND_INFO("TC58CVG0S3", 0xC2, + SPINAND_INFO("TC58CVG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 2Gb */ - SPINAND_INFO("TC58CVG1S3", 0xCB, + SPINAND_INFO("TC58CVG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -116,7 +118,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xCD, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -126,7 +129,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xED, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -136,7 +140,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 1Gb */ - SPINAND_INFO("TC58CYG0S3", 0xB2, + SPINAND_INFO("TC58CYG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -146,7 +151,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 2Gb */ - SPINAND_INFO("TC58CYG1S3", 0xBB, + SPINAND_INFO("TC58CYG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -156,7 +162,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 4Gb */ - SPINAND_INFO("TC58CYG2S0", 0xBD, + SPINAND_INFO("TC58CYG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -167,33 +174,13 @@ static const struct spinand_info toshiba_spinand_table[] = { tc58cxgxsx_ecc_get_status)), }; -static int toshiba_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Toshiba SPI NAND read ID needs a dummy byte, - * so the first byte in id is garbage. - */ - if (id[1] != SPINAND_MFR_TOSHIBA) - return 0; - - ret = spinand_match_and_init(spinand, toshiba_spinand_table, - ARRAY_SIZE(toshiba_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { - .detect = toshiba_spinand_detect, }; const struct spinand_manufacturer toshiba_spinand_manufacturer = { .id = SPINAND_MFR_TOSHIBA, .name = "Toshiba", + .chips = toshiba_spinand_table, + .nchips = ARRAY_SIZE(toshiba_spinand_table), .ops = &toshiba_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index a6c17e0cace8..76684428354e 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -75,7 +75,8 @@ static int w25m02gv_select_target(struct spinand_device *spinand, } static const struct spinand_info winbond_spinand_table[] = { - SPINAND_INFO("W25M02GV", 0xAB, + SPINAND_INFO("W25M02GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -84,7 +85,8 @@ static const struct spinand_info winbond_spinand_table[] = { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), - SPINAND_INFO("W25N01GV", 0xAA, + SPINAND_INFO("W25N01GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -94,31 +96,6 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), }; -/** - * winbond_spinand_detect - initialize device related part in spinand_device - * struct if it is a Winbond device. - * @spinand: SPI NAND device structure - */ -static int winbond_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Winbond SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_WINBOND) - return 0; - - ret = spinand_match_and_init(spinand, winbond_spinand_table, - ARRAY_SIZE(winbond_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static int winbond_spinand_init(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); @@ -138,12 +115,13 @@ static int winbond_spinand_init(struct spinand_device *spinand) } static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { - .detect = winbond_spinand_detect, .init = winbond_spinand_init, }; const struct spinand_manufacturer winbond_spinand_manufacturer = { .id = SPINAND_MFR_WINBOND, .name = "Winbond", + .chips = winbond_spinand_table, + .nchips = ARRAY_SIZE(winbond_spinand_table), .ops = &winbond_spinand_manuf_ops, }; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 4ea558bd3c46..f4c4ae87181b 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -32,9 +32,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPINAND_READID_OP(ndummy, buf, len) \ +#define SPINAND_READID_OP(naddr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ - SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_ADDR(naddr, 0, 1), \ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 1)) @@ -176,37 +176,46 @@ struct spinand_device; * @data: buffer containing the id bytes. Currently 4 bytes large, but can * be extended if required * @len: ID length - * - * struct_spinand_id->data contains all bytes returned after a READ_ID command, - * including dummy bytes if the chip does not emit ID bytes right after the - * READ_ID command. The responsibility to extract real ID bytes is left to - * struct_manufacurer_ops->detect(). */ struct spinand_id { u8 data[SPINAND_MAX_ID_LEN]; int len; }; +enum spinand_readid_method { + SPINAND_READID_METHOD_OPCODE, + SPINAND_READID_METHOD_OPCODE_ADDR, + SPINAND_READID_METHOD_OPCODE_DUMMY, +}; + +/** + * struct spinand_devid - SPI NAND device id structure + * @id: device id of current chip + * @len: number of bytes in device id + * @method: method to read chip id + * There are 3 possible variants: + * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately + * after read_id opcode. + * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after + * read_id opcode + 1-byte address. + * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after + * read_id opcode + 1 dummy byte. + */ +struct spinand_devid { + const u8 *id; + const u8 len; + const enum spinand_readid_method method; +}; + /** * struct manufacurer_ops - SPI NAND manufacturer specific operations - * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed - * the core calls the struct_manufacurer_ops->detect() hook of each - * registered manufacturer until one of them return 1. Note that - * the first thing to check in this hook is that the manufacturer ID - * in struct_spinand_device->id matches the manufacturer whose - * ->detect() hook has been called. Should return 1 if there's a - * match, 0 if the manufacturer ID does not match and a negative - * error code otherwise. When true is returned, the core assumes - * that properties of the NAND chip (spinand->base.memorg and - * spinand->base.eccreq) have been filled * @init: initialize a SPI NAND device * @cleanup: cleanup a SPI NAND device * * Each SPI NAND manufacturer driver should implement this interface so that - * NAND chips coming from this vendor can be detected and initialized properly. + * NAND chips coming from this vendor can be initialized properly. */ struct spinand_manufacturer_ops { - int (*detect)(struct spinand_device *spinand); int (*init)(struct spinand_device *spinand); void (*cleanup)(struct spinand_device *spinand); }; @@ -215,11 +224,16 @@ struct spinand_manufacturer_ops { * struct spinand_manufacturer - SPI NAND manufacturer instance * @id: manufacturer ID * @name: manufacturer name + * @devid_len: number of bytes in device ID + * @chips: supported SPI NANDs under current manufacturer + * @nchips: number of SPI NANDs available in chips array * @ops: manufacturer operations */ struct spinand_manufacturer { u8 id; char *name; + const struct spinand_info *chips; + const size_t nchips; const struct spinand_manufacturer_ops *ops; }; @@ -291,7 +305,7 @@ struct spinand_ecc_info { */ struct spinand_info { const char *model; - u16 devid; + struct spinand_devid devid; u32 flags; struct nand_memory_organization memorg; struct nand_ecc_req eccreq; @@ -305,6 +319,13 @@ struct spinand_info { unsigned int target); }; +#define SPINAND_ID(__method, ...) \ + { \ + .id = (const u8[]){ __VA_ARGS__ }, \ + .len = sizeof((u8[]){ __VA_ARGS__ }), \ + .method = __method, \ + } + #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ { \ .read_cache = __read, \ @@ -451,9 +472,10 @@ static inline void spinand_set_of_node(struct spinand_device *spinand, nanddev_set_of_node(&spinand->base, np); } -int spinand_match_and_init(struct spinand_device *dev, +int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid); + unsigned int table_size, + enum spinand_readid_method rdid_method); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); From c4b7dd35d35936964c71db42e1d94994ee6ea411 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 11 Feb 2020 14:31:51 -0300 Subject: [PATCH 06/55] mtd: rawnand: ingenic: Use devm_platform_ioremap_resource() Use devm_platform_ioremap_resource() instead of platform_get_resource() + devm_ioremap_resource(). Signed-off-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200211173151.27587-1-paul@crapouillou.net --- drivers/mtd/nand/raw/ingenic/ingenic_ecc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index c954189606f6..8e22cd6ec71f 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -124,7 +124,6 @@ int ingenic_ecc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ingenic_ecc *ecc; - struct resource *res; ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); if (!ecc) @@ -134,8 +133,7 @@ int ingenic_ecc_probe(struct platform_device *pdev) if (!ecc->ops) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ecc->base = devm_ioremap_resource(dev, res); + ecc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ecc->base)) return PTR_ERR(ecc->base); From 91a1abfb752357fe5d0783bd69db0d91f358e3eb Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:16 +0100 Subject: [PATCH 07/55] mtd: rawnand: ams-delta: Write protect device during probe Initialise NWP GPIO pin as asserted to protect the device from hazard during setup of other GPIO pins. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-2-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 8312182088c1..2501cfe00f43 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -251,8 +251,8 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - /* Set chip enabled, but */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); + /* Set chip enabled but write protected */ + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); @@ -309,6 +309,17 @@ static int ams_delta_init(struct platform_device *pdev) nand_controller_init(&priv->base); this->controller = &priv->base; + /* + * FIXME: We should release write protection only after nand_scan() to + * be on the safe side but we can't do that until we have a generic way + * to assert/deassert WP from the core. Even if the core shouldn't + * write things in the nand_scan() path, it should have control on this + * pin just in case we ever need to disable write protection during + * chip detection/initialization. + */ + /* Release write protection */ + gpiod_set_value(priv->gpiod_nwp, 1); + /* Scan to find existence of the device */ err = nand_scan(this, 1); if (err) @@ -336,6 +347,9 @@ static int ams_delta_cleanup(struct platform_device *pdev) struct ams_delta_nand *priv = platform_get_drvdata(pdev); struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); + /* Apply write protection */ + gpiod_set_value(priv->gpiod_nwp, 0); + /* Unregister device */ nand_release(mtd_to_nand(mtd)); From 1698ea32133a884d6def357f90265e59799242e9 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:17 +0100 Subject: [PATCH 08/55] mtd: rawnand: ams-delta: Use struct gpio_nand_platdata In order to be able to move the hardcoded Amstrad Delta partition info from the driver code to the board file, reuse gpio_nand_platdata structure owned by "gpio-nand" driver and try to obtain information on device partitions from device platform data. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-3-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 2501cfe00f43..fbab7cc14607 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -220,12 +221,20 @@ static const struct nand_controller_ops ams_delta_ops = { */ static int ams_delta_init(struct platform_device *pdev) { + struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); + const struct mtd_partition *partitions = partition_info; + int num_partitions = ARRAY_SIZE(partition_info); struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; struct gpio_descs *data_gpiods; int err = 0; + if (pdata) { + partitions = pdata->parts; + num_partitions = pdata->num_parts; + } + /* Allocate memory for MTD device structure and private data */ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), GFP_KERNEL); @@ -326,8 +335,7 @@ static int ams_delta_init(struct platform_device *pdev) return err; /* Register the partitions */ - err = mtd_device_register(mtd, partition_info, - ARRAY_SIZE(partition_info)); + err = mtd_device_register(mtd, partitions, num_partitions); if (err) goto err_nand_cleanup; From 38c30b3c96a572926346b7c5221fb3953bfb0d5e Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:18 +0100 Subject: [PATCH 09/55] ARM: OMAP1: ams-delta: Provide board specific partition info Now as the Amstrad Delta NAND driver supports fetching information on MTD partitions from device platform data, add partition info to the NAND device configuration. Signed-off-by: Janusz Krzysztofik Acked-by: Tony Lindgren Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-4-jmkrzyszt@gmail.com --- arch/arm/mach-omap1/board-ams-delta.c | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index a2aa7a12b374..f4d2ef97099e 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -294,9 +296,42 @@ struct modem_private_data { static struct modem_private_data modem_priv; +/* + * Define partitions for flash device + */ + +static struct mtd_partition partition_info[] = { + { .name = "Kernel", + .offset = 0, + .size = 3 * SZ_1M + SZ_512K }, + { .name = "u-boot", + .offset = 3 * SZ_1M + SZ_512K, + .size = SZ_256K }, + { .name = "u-boot params", + .offset = 3 * SZ_1M + SZ_512K + SZ_256K, + .size = SZ_256K }, + { .name = "Amstrad LDR", + .offset = 4 * SZ_1M, + .size = SZ_256K }, + { .name = "File system", + .offset = 4 * SZ_1M + 1 * SZ_256K, + .size = 27 * SZ_1M }, + { .name = "PBL reserved", + .offset = 32 * SZ_1M - 3 * SZ_256K, + .size = 3 * SZ_256K }, +}; + +static struct gpio_nand_platdata nand_platdata = { + .parts = partition_info, + .num_parts = ARRAY_SIZE(partition_info), +}; + static struct platform_device ams_delta_nand_device = { .name = "ams-delta-nand", .id = -1, + .dev = { + .platform_data = &nand_platdata, + }, }; #define OMAP_GPIO_LABEL "gpio-0-15" From d7ffe387cc12a5dbfdeeabae85b168dc407ac285 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:19 +0100 Subject: [PATCH 10/55] mtd: rawnand: ams-delta: Drop board specific partition info Now as we support fetching partition info from device platform data and the Amstrad Delta board file provides that info, drop it from the driver code. v2: rebase on top of gpio_nand_platdata extension Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-5-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fbab7cc14607..25f121adea6f 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -42,31 +42,6 @@ struct ams_delta_nand { bool data_in; }; -/* - * Define partitions for flash devices - */ - -static const struct mtd_partition partition_info[] = { - { .name = "Kernel", - .offset = 0, - .size = 3 * SZ_1M + SZ_512K }, - { .name = "u-boot", - .offset = 3 * SZ_1M + SZ_512K, - .size = SZ_256K }, - { .name = "u-boot params", - .offset = 3 * SZ_1M + SZ_512K + SZ_256K, - .size = SZ_256K }, - { .name = "Amstrad LDR", - .offset = 4 * SZ_1M, - .size = SZ_256K }, - { .name = "File system", - .offset = 4 * SZ_1M + 1 * SZ_256K, - .size = 27 * SZ_1M }, - { .name = "PBL reserved", - .offset = 32 * SZ_1M - 3 * SZ_256K, - .size = 3 * SZ_256K }, -}; - static void ams_delta_write_commit(struct ams_delta_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 0); @@ -222,8 +197,8 @@ static const struct nand_controller_ops ams_delta_ops = { static int ams_delta_init(struct platform_device *pdev) { struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); - const struct mtd_partition *partitions = partition_info; - int num_partitions = ARRAY_SIZE(partition_info); + const struct mtd_partition *partitions = NULL; + int num_partitions = 0; struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; From 2cef3d4cf4498e260f5bc2d5fab850e4a52be382 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:20 +0100 Subject: [PATCH 11/55] mtd: rawnand: ams-delta: Enable OF partition info support Provide MTD layer with device OF node info required by OF partition parser. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-6-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 25f121adea6f..fb96f6a3b0b3 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -222,6 +222,7 @@ static int ams_delta_init(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; nand_set_controller_data(this, priv); + nand_set_flash_node(this, pdev->dev.of_node); priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); if (IS_ERR(priv->gpiod_rdy)) { From 241008ed0bb5955e52e06d7270e87c974bbaadd6 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:21 +0100 Subject: [PATCH 12/55] mtd: rawnand: ams-delta: Push inversion handling to gpiolib Let platforms take care of declaring correct GPIO pin polarity so we can just ask a GPIO line to be asserted or deasserted and gpiolib deals with the rest depending on how the platform is configured. Inspired by similar changes to regulator drivers by Linus Walleij , thanks! Signed-off-by: Janusz Krzysztofik Acked-by: Tony Lindgren Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-7-jmkrzyszt@gmail.com --- arch/arm/mach-omap1/board-ams-delta.c | 12 ++++++++---- drivers/mtd/nand/raw/ams-delta.c | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index f4d2ef97099e..8d32894ecd2e 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -341,10 +341,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = { .table = { GPIO_LOOKUP(OMAP_GPIO_LABEL, AMS_DELTA_GPIO_PIN_NAND_RB, "rdy", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", + GPIO_ACTIVE_LOW), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0), GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0), diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fb96f6a3b0b3..c7aeb940accd 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -44,9 +44,9 @@ struct ams_delta_nand { static void ams_delta_write_commit(struct ams_delta_nand *priv) { - gpiod_set_value(priv->gpiod_nwe, 0); - ndelay(40); gpiod_set_value(priv->gpiod_nwe, 1); + ndelay(40); + gpiod_set_value(priv->gpiod_nwe, 0); } static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) @@ -81,13 +81,13 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; - gpiod_set_value(priv->gpiod_nre, 0); + gpiod_set_value(priv->gpiod_nre, 1); ndelay(40); gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); - gpiod_set_value(priv->gpiod_nre, 1); + gpiod_set_value(priv->gpiod_nre, 0); res = values[0]; return res; @@ -129,7 +129,7 @@ static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) { - gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1); + gpiod_set_value(priv->gpiod_nce, assert); } static int ams_delta_exec_op(struct nand_chip *this, @@ -237,28 +237,28 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); /* Set chip enabled but write protected */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW); + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH); + priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nce)) { err = PTR_ERR(priv->gpiod_nce); dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH); + priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nre)) { err = PTR_ERR(priv->gpiod_nre); dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH); + priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwe)) { err = PTR_ERR(priv->gpiod_nwe); dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); @@ -303,7 +303,7 @@ static int ams_delta_init(struct platform_device *pdev) * chip detection/initialization. */ /* Release write protection */ - gpiod_set_value(priv->gpiod_nwp, 1); + gpiod_set_value(priv->gpiod_nwp, 0); /* Scan to find existence of the device */ err = nand_scan(this, 1); @@ -332,7 +332,7 @@ static int ams_delta_cleanup(struct platform_device *pdev) struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); /* Apply write protection */ - gpiod_set_value(priv->gpiod_nwp, 0); + gpiod_set_value(priv->gpiod_nwp, 1); /* Unregister device */ nand_release(mtd_to_nand(mtd)); From ccada49b050f44df9499859f09b822b8aebc3a4d Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:22 +0100 Subject: [PATCH 13/55] mtd: rawnand: ams-delta: Don't hardcode read/write pulse widths Instead of forcing Amstrad Delta specific read/write pulse widths, use variables initialised from respective fields of chip SDR timings. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-8-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index c7aeb940accd..11689218d23a 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -40,12 +40,14 @@ struct ams_delta_nand { struct gpio_desc *gpiod_cle; struct gpio_descs *data_gpiods; bool data_in; + unsigned int tRP; + unsigned int tWP; }; static void ams_delta_write_commit(struct ams_delta_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 1); - ndelay(40); + ndelay(priv->tWP); gpiod_set_value(priv->gpiod_nwe, 0); } @@ -82,7 +84,7 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; gpiod_set_value(priv->gpiod_nre, 1); - ndelay(40); + ndelay(priv->tRP); gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); @@ -187,8 +189,31 @@ static int ams_delta_exec_op(struct nand_chip *this, return ret; } +static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, + const struct nand_data_interface *cf) +{ + struct ams_delta_nand *priv = nand_get_controller_data(this); + const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); + struct device *dev = &nand_to_mtd(this)->dev; + + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); + dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + + priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000); + dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP); + + return 0; +} + static const struct nand_controller_ops ams_delta_ops = { .exec_op = ams_delta_exec_op, + .setup_data_interface = ams_delta_setup_data_interface, }; /* From 586a746b326c35d2c0b2773dba61ffa1d4818711 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:23 +0100 Subject: [PATCH 14/55] mtd: rawnand: ams-delta: Make read pulses optional Allow platforms to omit NRE pin from device configuration by requesting that pin as optional. In that case, also don't apply read pulse width from chip SDR timings. There should be no need for further code adjustments as gpiolib can handle NULL GPIO descriptor pointers. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-9-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 11689218d23a..c481d73e3dcb 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -202,8 +202,10 @@ static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, if (csline == NAND_DATA_IFACE_CHECK_ONLY) return 0; - priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); - dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + if (priv->gpiod_nre) { + priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); + dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + } priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000); dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP); @@ -276,7 +278,8 @@ static int ams_delta_init(struct platform_device *pdev) return err; } - priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_LOW); + priv->gpiod_nre = devm_gpiod_get_optional(&pdev->dev, "nre", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nre)) { err = PTR_ERR(priv->gpiod_nre); dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); From ea5ea9fa6db23ff5b194e84fad32e3bae5c7f357 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:24 +0100 Subject: [PATCH 15/55] mtd: rawnand: ams-delta: Handle more GPIO pins as optional In order to make the driver more useful on platforms other than Amstrad Delta, allow GPIO descriptor pointers of possibly non-critical NWP and NCE pins to be initialised as NULL. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-10-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index c481d73e3dcb..0c88e94e9b71 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -264,14 +264,16 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); /* Set chip enabled but write protected */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); + priv->gpiod_nwp = devm_gpiod_get_optional(&pdev->dev, "nwp", + GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_LOW); + priv->gpiod_nce = devm_gpiod_get_optional(&pdev->dev, "nce", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nce)) { err = PTR_ERR(priv->gpiod_nce); dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); From 7c2f66a960fccc165d0b6c594f40f0ad3edfc61f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:25 +0100 Subject: [PATCH 16/55] mtd: rawnand: ams-delta: Add module device tables In preparation for merging the driver with "gpio-nand", introduce module device tables where new device models can be accommodated as soon as respective support is added. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-11-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 0c88e94e9b71..a493f1dc6677 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -370,11 +370,29 @@ static int ams_delta_cleanup(struct platform_device *pdev) return 0; } +static const struct of_device_id gpio_nand_of_id_table[] = { + { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, gpio_nand_of_id_table); + +static const struct platform_device_id gpio_nand_plat_id_table[] = { + { + .name = "ams-delta-nand", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table); + static struct platform_driver ams_delta_nand_driver = { .probe = ams_delta_init, .remove = ams_delta_cleanup, + .id_table = gpio_nand_plat_id_table, .driver = { .name = "ams-delta-nand", + .of_match_table = of_match_ptr(gpio_nand_of_id_table), }, }; From d1b1a8f73a21c7807b23144f41174b71a5a60a40 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:26 +0100 Subject: [PATCH 17/55] mtd: rawnand: ams-delta: Support custom driver initialisation In preparation for extending the driver with custom I/O support, try to obtain device specific initialisation routine from a matching device table entry and run it as an additional step of device probe. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-12-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index a493f1dc6677..60502edfbeab 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -230,6 +231,7 @@ static int ams_delta_init(struct platform_device *pdev) struct nand_chip *this; struct mtd_info *mtd; struct gpio_descs *data_gpiods; + int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); int err = 0; if (pdata) { @@ -319,6 +321,15 @@ static int ams_delta_init(struct platform_device *pdev) priv->data_gpiods = data_gpiods; priv->data_in = true; + if (pdev->id_entry) + probe = (void *) pdev->id_entry->driver_data; + else + probe = of_device_get_match_data(&pdev->dev); + if (probe) + err = probe(pdev, priv); + if (err) + return err; + /* Initialize the NAND controller object embedded in ams_delta_nand. */ priv->base.ops = &ams_delta_ops; nand_controller_init(&priv->base); From edfd8d9c763f5f91cda663e89f34dccacd8eb586 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:27 +0100 Subject: [PATCH 18/55] mtd: rawnand: ams-delta: Drop useless local variable For consistency with adjacent code patterns used in the driver probe function, store data GPIO array pointer directly in a respective field of the driver private structure instead of storing it intermediately in a local variable for error checking. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-13-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 60502edfbeab..d8eef3dffa66 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -230,7 +230,6 @@ static int ams_delta_init(struct platform_device *pdev) struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; - struct gpio_descs *data_gpiods; int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); int err = 0; @@ -312,13 +311,12 @@ static int ams_delta_init(struct platform_device *pdev) } /* Request array of data pins, initialize them as input */ - data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); - if (IS_ERR(data_gpiods)) { - err = PTR_ERR(data_gpiods); + priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); + if (IS_ERR(priv->data_gpiods)) { + err = PTR_ERR(priv->data_gpiods); dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); return err; } - priv->data_gpiods = data_gpiods; priv->data_in = true; if (pdev->id_entry) From 2b1dcee304b67f3c4e7e2e910be90e36eef46050 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:28 +0100 Subject: [PATCH 19/55] mtd: rawnand: ams-delta: Make the driver custom I/O ready In order to be merged with "gpio-nand", the driver must support custom (non-GPIO) I/O accessors. Allow platforms to omit data GPIO port as well as NWE pin info from device setup. For the driver to still work on such platform, custom I/O accessors as well as a custom probe function which initialises the driver private structure with those accessors must be added to the driver. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-14-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index d8eef3dffa66..5a27170b2808 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -43,6 +43,9 @@ struct ams_delta_nand { bool data_in; unsigned int tRP; unsigned int tWP; + u8 (*io_read)(struct ams_delta_nand *this); + void (*io_write)(struct ams_delta_nand *this, + u8 byte); }; static void ams_delta_write_commit(struct ams_delta_nand *priv) @@ -116,18 +119,18 @@ static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, ams_delta_dir_output(priv, buf[i++]); while (i < len) - ams_delta_io_write(priv, buf[i++]); + priv->io_write(priv, buf[i++]); } static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) { int i; - if (!priv->data_in) + if (priv->data_gpiods && !priv->data_in) ams_delta_dir_input(priv); for (i = 0; i < len; i++) - buf[i] = ams_delta_io_read(priv); + buf[i] = priv->io_read(priv); } static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) @@ -289,7 +292,8 @@ static int ams_delta_init(struct platform_device *pdev) return err; } - priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_LOW); + priv->gpiod_nwe = devm_gpiod_get_optional(&pdev->dev, "nwe", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwe)) { err = PTR_ERR(priv->gpiod_nwe); dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); @@ -311,13 +315,24 @@ static int ams_delta_init(struct platform_device *pdev) } /* Request array of data pins, initialize them as input */ - priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); + priv->data_gpiods = devm_gpiod_get_array_optional(&pdev->dev, "data", + GPIOD_IN); if (IS_ERR(priv->data_gpiods)) { err = PTR_ERR(priv->data_gpiods); dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); return err; } - priv->data_in = true; + if (priv->data_gpiods) { + if (!priv->gpiod_nwe) { + dev_err(&pdev->dev, + "mandatory NWE pin not provided by platform\n"); + return -ENODEV; + } + + priv->io_read = ams_delta_io_read; + priv->io_write = ams_delta_io_write; + priv->data_in = true; + } if (pdev->id_entry) probe = (void *) pdev->id_entry->driver_data; @@ -328,6 +343,11 @@ static int ams_delta_init(struct platform_device *pdev) if (err) return err; + if (!priv->io_read || !priv->io_write) { + dev_err(&pdev->dev, "incomplete device configuration\n"); + return -ENODEV; + } + /* Initialize the NAND controller object embedded in ams_delta_nand. */ priv->base.ops = &ams_delta_ops; nand_controller_init(&priv->base); From 16d00cd612068965134a08c08a43355b4b4ac58f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:29 +0100 Subject: [PATCH 20/55] mtd: rawnand: ams-delta: Rename structures and functions to gpio_nand* Another step in preparation for merging the driver with "gpio-nand". Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-15-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 86 ++++++++++++++++---------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 5a27170b2808..d66dab25df20 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -29,7 +29,7 @@ /* * MTD structure for E3 (Delta) */ -struct ams_delta_nand { +struct gpio_nand { struct nand_controller base; struct nand_chip nand_chip; struct gpio_desc *gpiod_rdy; @@ -43,19 +43,18 @@ struct ams_delta_nand { bool data_in; unsigned int tRP; unsigned int tWP; - u8 (*io_read)(struct ams_delta_nand *this); - void (*io_write)(struct ams_delta_nand *this, - u8 byte); + u8 (*io_read)(struct gpio_nand *this); + void (*io_write)(struct gpio_nand *this, u8 byte); }; -static void ams_delta_write_commit(struct ams_delta_nand *priv) +static void gpio_nand_write_commit(struct gpio_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 1); ndelay(priv->tWP); gpiod_set_value(priv->gpiod_nwe, 0); } -static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) +static void gpio_nand_io_write(struct gpio_nand *priv, u8 byte) { struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; @@ -63,10 +62,10 @@ static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); - ams_delta_write_commit(priv); + gpio_nand_write_commit(priv); } -static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) +static void gpio_nand_dir_output(struct gpio_nand *priv, u8 byte) { struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; @@ -76,12 +75,12 @@ static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) gpiod_direction_output_raw(data_gpiods->desc[i], test_bit(i, values)); - ams_delta_write_commit(priv); + gpio_nand_write_commit(priv); priv->data_in = false; } -static u8 ams_delta_io_read(struct ams_delta_nand *priv) +static u8 gpio_nand_io_read(struct gpio_nand *priv) { u8 res; struct gpio_descs *data_gpiods = priv->data_gpiods; @@ -99,7 +98,7 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) return res; } -static void ams_delta_dir_input(struct ams_delta_nand *priv) +static void gpio_nand_dir_input(struct gpio_nand *priv) { struct gpio_descs *data_gpiods = priv->data_gpiods; int i; @@ -110,68 +109,67 @@ static void ams_delta_dir_input(struct ams_delta_nand *priv) priv->data_in = true; } -static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, - int len) +static void gpio_nand_write_buf(struct gpio_nand *priv, const u8 *buf, int len) { int i = 0; if (len > 0 && priv->data_in) - ams_delta_dir_output(priv, buf[i++]); + gpio_nand_dir_output(priv, buf[i++]); while (i < len) priv->io_write(priv, buf[i++]); } -static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) +static void gpio_nand_read_buf(struct gpio_nand *priv, u8 *buf, int len) { int i; if (priv->data_gpiods && !priv->data_in) - ams_delta_dir_input(priv); + gpio_nand_dir_input(priv); for (i = 0; i < len; i++) buf[i] = priv->io_read(priv); } -static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) +static void gpio_nand_ctrl_cs(struct gpio_nand *priv, bool assert) { gpiod_set_value(priv->gpiod_nce, assert); } -static int ams_delta_exec_op(struct nand_chip *this, +static int gpio_nand_exec_op(struct nand_chip *this, const struct nand_operation *op, bool check_only) { - struct ams_delta_nand *priv = nand_get_controller_data(this); + struct gpio_nand *priv = nand_get_controller_data(this); const struct nand_op_instr *instr; int ret = 0; if (check_only) return 0; - ams_delta_ctrl_cs(priv, 1); + gpio_nand_ctrl_cs(priv, 1); for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) { switch (instr->type) { case NAND_OP_CMD_INSTR: gpiod_set_value(priv->gpiod_cle, 1); - ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1); + gpio_nand_write_buf(priv, &instr->ctx.cmd.opcode, 1); gpiod_set_value(priv->gpiod_cle, 0); break; case NAND_OP_ADDR_INSTR: gpiod_set_value(priv->gpiod_ale, 1); - ams_delta_write_buf(priv, instr->ctx.addr.addrs, + gpio_nand_write_buf(priv, instr->ctx.addr.addrs, instr->ctx.addr.naddrs); gpiod_set_value(priv->gpiod_ale, 0); break; case NAND_OP_DATA_IN_INSTR: - ams_delta_read_buf(priv, instr->ctx.data.buf.in, + gpio_nand_read_buf(priv, instr->ctx.data.buf.in, instr->ctx.data.len); break; case NAND_OP_DATA_OUT_INSTR: - ams_delta_write_buf(priv, instr->ctx.data.buf.out, + gpio_nand_write_buf(priv, instr->ctx.data.buf.out, instr->ctx.data.len); break; @@ -188,15 +186,15 @@ static int ams_delta_exec_op(struct nand_chip *this, break; } - ams_delta_ctrl_cs(priv, 0); + gpio_nand_ctrl_cs(priv, 0); return ret; } -static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, +static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline, const struct nand_data_interface *cf) { - struct ams_delta_nand *priv = nand_get_controller_data(this); + struct gpio_nand *priv = nand_get_controller_data(this); const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); struct device *dev = &nand_to_mtd(this)->dev; @@ -217,23 +215,23 @@ static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, return 0; } -static const struct nand_controller_ops ams_delta_ops = { - .exec_op = ams_delta_exec_op, - .setup_data_interface = ams_delta_setup_data_interface, +static const struct nand_controller_ops gpio_nand_ops = { + .exec_op = gpio_nand_exec_op, + .setup_data_interface = gpio_nand_setup_data_interface, }; /* * Main initialization routine */ -static int ams_delta_init(struct platform_device *pdev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); const struct mtd_partition *partitions = NULL; int num_partitions = 0; - struct ams_delta_nand *priv; + struct gpio_nand *priv; struct nand_chip *this; struct mtd_info *mtd; - int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); + int (*probe)(struct platform_device *pdev, struct gpio_nand *priv); int err = 0; if (pdata) { @@ -242,7 +240,7 @@ static int ams_delta_init(struct platform_device *pdev) } /* Allocate memory for MTD device structure and private data */ - priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), + priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -329,8 +327,8 @@ static int ams_delta_init(struct platform_device *pdev) return -ENODEV; } - priv->io_read = ams_delta_io_read; - priv->io_write = ams_delta_io_write; + priv->io_read = gpio_nand_io_read; + priv->io_write = gpio_nand_io_write; priv->data_in = true; } @@ -348,8 +346,8 @@ static int ams_delta_init(struct platform_device *pdev) return -ENODEV; } - /* Initialize the NAND controller object embedded in ams_delta_nand. */ - priv->base.ops = &ams_delta_ops; + /* Initialize the NAND controller object embedded in gpio_nand. */ + priv->base.ops = &gpio_nand_ops; nand_controller_init(&priv->base); this->controller = &priv->base; @@ -385,9 +383,9 @@ static int ams_delta_init(struct platform_device *pdev) /* * Clean up routine */ -static int ams_delta_cleanup(struct platform_device *pdev) +static int gpio_nand_remove(struct platform_device *pdev) { - struct ams_delta_nand *priv = platform_get_drvdata(pdev); + struct gpio_nand *priv = platform_get_drvdata(pdev); struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); /* Apply write protection */ @@ -415,9 +413,9 @@ static const struct platform_device_id gpio_nand_plat_id_table[] = { }; MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table); -static struct platform_driver ams_delta_nand_driver = { - .probe = ams_delta_init, - .remove = ams_delta_cleanup, +static struct platform_driver gpio_nand_driver = { + .probe = gpio_nand_probe, + .remove = gpio_nand_remove, .id_table = gpio_nand_plat_id_table, .driver = { .name = "ams-delta-nand", @@ -425,7 +423,7 @@ static struct platform_driver ams_delta_nand_driver = { }, }; -module_platform_driver(ams_delta_nand_driver); +module_platform_driver(gpio_nand_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan McDowell "); From 84234652595f8dc29a92a33a6ae504c7df2e11c8 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Mon, 17 Feb 2020 14:56:39 +0800 Subject: [PATCH 21/55] mtd: rawnand: Add support for Macronix NAND randomizer Macronix NANDs support randomizer operation for user data scrambled, which can be enabled with a SET_FEATURE. User data written to the NAND device without randomizer is still readable after randomizer function enabled. The penalty of randomizer are subpage accesses prohibited and more time period is needed in program operation and entering deep power-down mode. i.e., tPROG 300us to 340us(randomizer enabled) For more high-reliability concern, if subpage write not available with hardware ECC and then to enable randomizer is recommended by default. Driver checks byte 167 of Vendor Blocks in ONFI parameter page table to see if this high-reliability function is supported. By adding a new specific DT property in children nodes to enable randomizer function. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581922600-25461-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_macronix.c | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 3ff7ce00cbdb..0a2fe25f2639 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -11,6 +11,19 @@ #define MACRONIX_READ_RETRY_BIT BIT(0) #define MACRONIX_NUM_READ_RETRY_MODES 6 +#define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 +#define MACRONIX_RANDOMIZER_BIT BIT(1) +#define MACRONIX_RANDOMIZER_ENPGM BIT(0) +#define MACRONIX_RANDOMIZER_RANDEN BIT(1) +#define MACRONIX_RANDOMIZER_RANDOPT BIT(2) +#define MACRONIX_RANDOMIZER_MODE_ENTER \ + (MACRONIX_RANDOMIZER_ENPGM | \ + MACRONIX_RANDOMIZER_RANDEN | \ + MACRONIX_RANDOMIZER_RANDOPT) +#define MACRONIX_RANDOMIZER_MODE_EXIT \ + (MACRONIX_RANDOMIZER_RANDEN | \ + MACRONIX_RANDOMIZER_RANDOPT) + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -29,15 +42,83 @@ static int macronix_nand_setup_read_retry(struct nand_chip *chip, int mode) return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); } +static int macronix_nand_randomizer_check_enable(struct nand_chip *chip) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + if (feature[0]) + return feature[0]; + + feature[0] = MACRONIX_RANDOMIZER_MODE_ENTER; + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + /* RANDEN and RANDOPT OTP bits are programmed */ + feature[0] = 0x0; + ret = nand_prog_page_op(chip, 0, 0, feature, 1); + if (ret < 0) + return ret; + + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + feature[0] &= MACRONIX_RANDOMIZER_MODE_EXIT; + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + return 0; +} + static void macronix_nand_onfi_init(struct nand_chip *chip) { struct nand_parameters *p = &chip->parameters; struct nand_onfi_vendor_macronix *mxic; + struct device_node *dn = nand_get_flash_node(chip); + int rand_otp = 0; + int ret; if (!p->onfi) return; + if (of_find_property(dn, "mxic,enable-randomizer-otp", NULL)) + rand_otp = 1; + mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor; + /* Subpage write is prohibited in randomizer operatoin */ + if (rand_otp && chip->options & NAND_NO_SUBPAGE_WRITE && + mxic->reliability_func & MACRONIX_RANDOMIZER_BIT) { + if (p->supports_set_get_features) { + bitmap_set(p->set_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); + bitmap_set(p->get_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); + ret = macronix_nand_randomizer_check_enable(chip); + if (ret < 0) { + bitmap_clear(p->set_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + 1); + bitmap_clear(p->get_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + 1); + pr_info("Macronix NAND randomizer failed\n"); + } else { + pr_info("Macronix NAND randomizer enabled\n"); + } + } + } + if ((mxic->reliability_func & MACRONIX_READ_RETRY_BIT) == 0) return; From 7f274f411c76ecf87c6eef7ce263ddfe0962ba46 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Mon, 17 Feb 2020 14:56:40 +0800 Subject: [PATCH 22/55] dt-bindings: mtd: Document Macronix NAND device bindings Document the bindings used by the Macronix NAND device. Signed-off-by: Mason Yang Reviewed-by: Rob Herring [ Link: https://lore.kernel.org/linux-mtd/1581922600-25461-3-git-send-email-masonccyang@mxic.com.tw --- .../devicetree/bindings/mtd/nand-macronix.txt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/nand-macronix.txt diff --git a/Documentation/devicetree/bindings/mtd/nand-macronix.txt b/Documentation/devicetree/bindings/mtd/nand-macronix.txt new file mode 100644 index 000000000000..ffab28a2c4d1 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nand-macronix.txt @@ -0,0 +1,27 @@ +Macronix NANDs Device Tree Bindings +----------------------------------- + +Macronix NANDs support randomizer operation for scrambling user data, +which can be enabled with a SET_FEATURE. The penalty when using the +randomizer are subpage accesses prohibited and more time period needed +for program operation, i.e., tPROG 300us to 340us (randomizer enabled). +Enabling the randomizer is a one time persistent and non reversible +operation. + +For more high-reliability concern, if subpage write is not available +with hardware ECC and not enabled at UBI level, then enabling the +randomizer is recommended by default by adding a new specific property +in children nodes. + +Required NAND chip properties in children mode: +- randomizer enable: should be "mxic,enable-randomizer-otp" + +Example: + + nand: nand-controller@unit-address { + + nand@0 { + reg = <0>; + mxic,enable-randomizer-otp; + }; + }; From 2148937501ee3d663e0010e519a553fea67ad103 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:14 +0000 Subject: [PATCH 23/55] mtd: spinand: Stop using spinand->oobbuf for buffering bad block markers For reading and writing the bad block markers, spinand->oobbuf is currently used as a buffer for the marker bytes. During the underlying read and write operations to actually get/set the content of the OOB area, the content of spinand->oobbuf is reused and changed by accessing it through spinand->oobbuf and/or spinand->databuf. This is a flaw in the original design of the SPI NAND core and at the latest from 13c15e07eedf ("mtd: spinand: Handle the case where PROGRAM LOAD does not reset the cache") on, it results in not having the bad block marker written at all, as the spinand->oobbuf is cleared to 0xff after setting the marker bytes to zero. To fix it, we now just store the two bytes for the marker on the stack and let the read/write operations copy it from/to the page buffer later. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-2-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index a9e9cbad942f..137d31dae3ce 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -570,18 +570,18 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) { struct spinand_device *spinand = nand_to_spinand(nand); + u8 marker[2] = { }; struct nand_page_io_req req = { .pos = *pos, - .ooblen = 2, + .ooblen = sizeof(marker), .ooboffs = 0, - .oobbuf.in = spinand->oobbuf, + .oobbuf.in = marker, .mode = MTD_OPS_RAW, }; - memset(spinand->oobbuf, 0, 2); spinand_select_target(spinand, pos->target); spinand_read_page(spinand, &req, false); - if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff) + if (marker[0] != 0xff || marker[1] != 0xff) return true; return false; @@ -605,11 +605,12 @@ static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) { struct spinand_device *spinand = nand_to_spinand(nand); + u8 marker[2] = { }; struct nand_page_io_req req = { .pos = *pos, .ooboffs = 0, - .ooblen = 2, - .oobbuf.out = spinand->oobbuf, + .ooblen = sizeof(marker), + .oobbuf.out = marker, }; int ret; @@ -624,7 +625,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) spinand_erase_op(spinand, pos); - memset(spinand->oobbuf, 0, 2); return spinand_write_page(spinand, &req); } From 621a7b780bd8b7054647d53d5071961f2c9e0873 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:25 +0000 Subject: [PATCH 24/55] mtd: spinand: Explicitly use MTD_OPS_RAW to write the bad block marker to OOB When writing the bad block marker to the OOB area the access mode should be set to MTD_OPS_RAW as it is done for reading the marker. Currently this only works because req.mode is initialized to MTD_OPS_PLACE_OOB (0) and spinand_write_to_cache_op() checks for req.mode != MTD_OPS_AUTO_OOB. Fix this by explicitly setting req.mode to MTD_OPS_RAW. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-3-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 137d31dae3ce..ee1eea857367 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -611,6 +611,7 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) .ooboffs = 0, .ooblen = sizeof(marker), .oobbuf.out = marker, + .mode = MTD_OPS_RAW, }; int ret; From b645ad39d56846618704e463b24bb994c9585c7f Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:35 +0000 Subject: [PATCH 25/55] mtd: spinand: Do not erase the block before writing a bad block marker Currently when marking a block, we use spinand_erase_op() to erase the block before writing the marker to the OOB area. Doing so without waiting for the operation to finish can lead to the marking failing silently and no bad block marker being written to the flash. In fact we don't need to do an erase at all before writing the BBM. The ECC is disabled for raw accesses to the OOB data and we don't need to work around any issues with chips reporting ECC errors as it is known to be the case for raw NAND. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-4-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ee1eea857367..b6bb358b96ce 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -615,7 +615,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) }; int ret; - /* Erase block before marking it bad. */ ret = spinand_select_target(spinand, pos->target); if (ret) return ret; @@ -624,8 +623,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) if (ret) return ret; - spinand_erase_op(spinand, pos); - return spinand_write_page(spinand, &req); } From c6fbcb70132ffc66696a94dd3d8e6215c750254f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 23 Feb 2020 19:06:33 +0100 Subject: [PATCH 26/55] mtd: rawnand: Fix a typo ("manufecturer") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Neuschäfer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200223180634.8736-1-j.neuschaefer@gmx.net --- include/linux/mtd/rawnand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 4ab9bccfcde0..3c7c15aadcee 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1215,7 +1215,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) * struct nand_flash_dev - NAND Flash Device ID Structure * @name: a human-readable name of the NAND chip * @dev_id: the device ID (the second byte of the full chip ID array) - * @mfr_id: manufecturer ID part of the full chip ID array (refers the same + * @mfr_id: manufacturer ID part of the full chip ID array (refers the same * memory address as ``id[0]``) * @dev_id: device ID part of the full chip ID array (refers the same memory * address as ``id[1]``) From 49f1c33076ca56871ffddc4800b04524204ea889 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 16:27:22 -0600 Subject: [PATCH 27/55] mtd: rawnand: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Acked-by: Masahiro Yamada Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200226222722.GA18020@embeddedor --- drivers/mtd/nand/raw/denali.h | 2 +- drivers/mtd/nand/raw/marvell_nand.c | 2 +- drivers/mtd/nand/raw/meson_nand.c | 2 +- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/nand_hynix.c | 2 +- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h index e5cdcda56d14..ac46eb7956ce 100644 --- a/drivers/mtd/nand/raw/denali.h +++ b/drivers/mtd/nand/raw/denali.h @@ -328,7 +328,7 @@ struct denali_chip { struct nand_chip chip; struct list_head node; unsigned int nsels; - struct denali_chip_sel sels[0]; + struct denali_chip_sel sels[]; }; /** diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index fb5abdcfb007..7082bef1c8a7 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -334,7 +334,7 @@ struct marvell_nand_chip { int addr_cyc; int selected_die; unsigned int nsels; - struct marvell_nand_chip_sel sels[0]; + struct marvell_nand_chip_sel sels[]; }; static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 9f17b5b8efbf..f6fb5c0e6255 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -118,7 +118,7 @@ struct meson_nfc_nand_chip { u8 *data_buf; __le64 *info_buf; u32 nsels; - u8 sels[0]; + u8 sels[]; }; struct meson_nand_ecc { diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index b8305e39ab51..ef149e8b26d0 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -131,7 +131,7 @@ struct mtk_nfc_nand_chip { u32 spare_per_sector; int nsels; - u8 sels[0]; + u8 sels[]; /* nothing after this field */ }; diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 194e4227aefe..7caedaa5b9e5 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -26,7 +26,7 @@ struct hynix_read_retry { int nregs; const u8 *regs; - u8 values[0]; + u8 values[]; }; /** diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 37a4ac0dd85b..6ede3934a5f4 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -195,7 +195,7 @@ struct sunxi_nand_chip { u32 timing_cfg; u32 timing_ctl; int nsels; - struct sunxi_nand_chip_sel sels[0]; + struct sunxi_nand_chip_sel sels[]; }; static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) From 7cd8c0adb489fde5cec2690a85517df74fa8abbb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:43 +0200 Subject: [PATCH 28/55] mtd: rawnand: gpmi: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-2-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index b9d5d55a5edb..53b00c841aec 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1148,20 +1148,21 @@ static int acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; struct dma_chan *dma_chan; + int ret = 0; /* request dma channel */ - dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); - if (!dma_chan) { - dev_err(this->dev, "Failed to request DMA channel.\n"); - goto acquire_err; + dma_chan = dma_request_chan(&pdev->dev, "rx-tx"); + if (IS_ERR(dma_chan)) { + ret = PTR_ERR(dma_chan); + if (ret != -EPROBE_DEFER) + dev_err(this->dev, "DMA channel request failed: %d\n", + ret); + release_dma_channels(this); + } else { + this->dma_chans[0] = dma_chan; } - this->dma_chans[0] = dma_chan; - return 0; - -acquire_err: - release_dma_channels(this); - return -EINVAL; + return ret; } static int gpmi_get_clks(struct gpmi_nand_data *this) From aafe30baf4ad131c22a0dc730298360a6e695ce3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:44 +0200 Subject: [PATCH 29/55] mtd: rawnand: marvell: Release DMA channel on error Release the DMA channel on errors after the channel has been successfully requested. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-3-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/marvell_nand.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 7082bef1c8a7..f60d885f38bf 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2751,8 +2751,10 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENXIO; + if (!r) { + ret = -ENXIO; + goto release_channel; + } config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -2763,7 +2765,7 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) ret = dmaengine_slave_config(nfc->dma_chan, &config); if (ret < 0) { dev_err(nfc->dev, "Failed to configure DMA channel\n"); - return ret; + goto release_channel; } /* @@ -2773,12 +2775,20 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) * the provided buffer. */ nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); - if (!nfc->dma_buf) - return -ENOMEM; + if (!nfc->dma_buf) { + ret = -ENOMEM; + goto release_channel; + } nfc->use_dma = true; return 0; + +release_channel: + dma_release_channel(nfc->dma_chan); + nfc->dma_chan = NULL; + + return ret; } static void marvell_nfc_reset(struct marvell_nfc *nfc) @@ -2920,10 +2930,13 @@ static int marvell_nfc_probe(struct platform_device *pdev) ret = marvell_nand_chips_init(dev, nfc); if (ret) - goto unprepare_reg_clk; + goto release_dma; return 0; +release_dma: + if (nfc->use_dma) + dma_release_channel(nfc->dma_chan); unprepare_reg_clk: clk_disable_unprepare(nfc->reg_clk); unprepare_core_clk: From cf9e2389482179db6bfab41512a9c0b722540be8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:45 +0200 Subject: [PATCH 30/55] mtd: rawnand: marvell: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-4-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/marvell_nand.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index f60d885f38bf..179f0ca585f8 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2743,11 +2743,14 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) if (ret) return ret; - nfc->dma_chan = dma_request_slave_channel(nfc->dev, "data"); - if (!nfc->dma_chan) { - dev_err(nfc->dev, - "Unable to request data DMA channel\n"); - return -ENODEV; + nfc->dma_chan = dma_request_chan(nfc->dev, "data"); + if (IS_ERR(nfc->dma_chan)) { + ret = PTR_ERR(nfc->dma_chan); + nfc->dma_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nfc->dev, "DMA channel request failed: %d\n", + ret); + return ret; } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); From ac80c55b46754fbe0394cf404a9091b551f00e22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:46 +0200 Subject: [PATCH 31/55] mtd: rawnand: sunxi: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Acked-by: Maxime Ripard Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-5-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/sunxi_nand.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 6ede3934a5f4..5f3e40b79fb1 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2123,8 +2123,16 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (ret) goto out_ahb_reset_reassert; - nfc->dmac = dma_request_slave_channel(dev, "rxtx"); - if (nfc->dmac) { + nfc->dmac = dma_request_chan(dev, "rxtx"); + if (IS_ERR(nfc->dmac)) { + ret = PTR_ERR(nfc->dmac); + if (ret == -EPROBE_DEFER) + goto out_ahb_reset_reassert; + + /* Ignore errors to fall back to PIO mode */ + dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret); + nfc->dmac = NULL; + } else { struct dma_slave_config dmac_cfg = { }; dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; @@ -2138,9 +2146,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (nfc->caps->extra_mbus_conf) writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); - - } else { - dev_warn(dev, "failed to request rxtx DMA channel\n"); } platform_set_drvdata(pdev, nfc); From 80c3012e127cf8f4c0601868b2c23b1caab20b44 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:47 +0200 Subject: [PATCH 32/55] mtd: rawnand: qcom: Release resources on failure within qcom_nandc_alloc() In case when DMA channel request or alloc_bam_transaction() fails, dma_unmap_single() and any channels already requested should be released. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-6-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/qcom_nandc.c | 61 +++++++++++++++++-------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 7bb9a7e8e1e7..ca21cb3836dc 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2628,6 +2628,29 @@ static const struct nand_controller_ops qcom_nandc_ops = { .attach_chip = qcom_nand_attach_chip, }; +static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) +{ + if (nandc->props->is_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + + if (nandc->tx_chan) + dma_release_channel(nandc->tx_chan); + + if (nandc->rx_chan) + dma_release_channel(nandc->rx_chan); + + if (nandc->cmd_chan) + dma_release_channel(nandc->cmd_chan); + } else { + if (nandc->chan) + dma_release_channel(nandc->chan); + } +} + static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) { int ret; @@ -2676,19 +2699,22 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); if (!nandc->tx_chan) { dev_err(nandc->dev, "failed to request tx channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); if (!nandc->rx_chan) { dev_err(nandc->dev, "failed to request rx channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); if (!nandc->cmd_chan) { dev_err(nandc->dev, "failed to request cmd channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } /* @@ -2702,7 +2728,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (!nandc->bam_txn) { dev_err(nandc->dev, "failed to allocate bam transaction\n"); - return -ENOMEM; + ret = -ENOMEM; + goto unalloc; } } else { nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); @@ -2720,29 +2747,9 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) nandc->controller.ops = &qcom_nandc_ops; return 0; -} - -static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) -{ - if (nandc->props->is_bam) { - if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) - dma_unmap_single(nandc->dev, nandc->reg_read_dma, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); - - if (nandc->tx_chan) - dma_release_channel(nandc->tx_chan); - - if (nandc->rx_chan) - dma_release_channel(nandc->rx_chan); - - if (nandc->cmd_chan) - dma_release_channel(nandc->cmd_chan); - } else { - if (nandc->chan) - dma_release_channel(nandc->chan); - } +unalloc: + qcom_nandc_unalloc(nandc); + return ret; } /* one time setup of a few nand controller registers */ From 92f0f8efbd4ad8fac2e3e902eff3c67da65f08cc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:48 +0200 Subject: [PATCH 33/55] mtd: rawnand: qcom: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-7-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/qcom_nandc.c | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ca21cb3836dc..5b11c7061497 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2696,24 +2696,36 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) return -EIO; } - nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); - if (!nandc->tx_chan) { - dev_err(nandc->dev, "failed to request tx channel\n"); - ret = -ENODEV; + nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); + if (IS_ERR(nandc->tx_chan)) { + ret = PTR_ERR(nandc->tx_chan); + nandc->tx_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "tx DMA channel request failed: %d\n", + ret); goto unalloc; } - nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); - if (!nandc->rx_chan) { - dev_err(nandc->dev, "failed to request rx channel\n"); - ret = -ENODEV; + nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); + if (IS_ERR(nandc->rx_chan)) { + ret = PTR_ERR(nandc->rx_chan); + nandc->rx_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "rx DMA channel request failed: %d\n", + ret); goto unalloc; } - nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); - if (!nandc->cmd_chan) { - dev_err(nandc->dev, "failed to request cmd channel\n"); - ret = -ENODEV; + nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); + if (IS_ERR(nandc->cmd_chan)) { + ret = PTR_ERR(nandc->cmd_chan); + nandc->cmd_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "cmd DMA channel request failed: %d\n", + ret); goto unalloc; } @@ -2732,11 +2744,15 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) goto unalloc; } } else { - nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); - if (!nandc->chan) { - dev_err(nandc->dev, - "failed to request slave channel\n"); - return -ENODEV; + nandc->chan = dma_request_chan(nandc->dev, "rxtx"); + if (IS_ERR(nandc->chan)) { + ret = PTR_ERR(nandc->chan); + nandc->chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "rxtx DMA channel request failed: %d\n", + ret); + return ret; } } From b35f79aa461e965514e7d437c6704679552f4818 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:49 +0200 Subject: [PATCH 34/55] mtd: rawnand: stm32_fmc2: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly and inform user of error in case the DMA request failed. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-8-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 44 ++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 3ba73f18841f..b6d45cd911ae 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1606,15 +1606,36 @@ static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr, /* DMA configuration */ static int stm32_fmc2_dma_setup(struct stm32_fmc2_nfc *fmc2) { - int ret; + int ret = 0; - fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx"); - fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx"); - fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc"); + fmc2->dma_tx_ch = dma_request_chan(fmc2->dev, "tx"); + if (IS_ERR(fmc2->dma_tx_ch)) { + ret = PTR_ERR(fmc2->dma_tx_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request tx DMA channel: %d\n", ret); + fmc2->dma_tx_ch = NULL; + goto err_dma; + } - if (!fmc2->dma_tx_ch || !fmc2->dma_rx_ch || !fmc2->dma_ecc_ch) { - dev_warn(fmc2->dev, "DMAs not defined in the device tree, polling mode is used\n"); - return 0; + fmc2->dma_rx_ch = dma_request_chan(fmc2->dev, "rx"); + if (IS_ERR(fmc2->dma_rx_ch)) { + ret = PTR_ERR(fmc2->dma_rx_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request rx DMA channel: %d\n", ret); + fmc2->dma_rx_ch = NULL; + goto err_dma; + } + + fmc2->dma_ecc_ch = dma_request_chan(fmc2->dev, "ecc"); + if (IS_ERR(fmc2->dma_ecc_ch)) { + ret = PTR_ERR(fmc2->dma_ecc_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request ecc DMA channel: %d\n", ret); + fmc2->dma_ecc_ch = NULL; + goto err_dma; } ret = sg_alloc_table(&fmc2->dma_ecc_sg, FMC2_MAX_SG, GFP_KERNEL); @@ -1635,6 +1656,15 @@ static int stm32_fmc2_dma_setup(struct stm32_fmc2_nfc *fmc2) init_completion(&fmc2->dma_ecc_complete); return 0; + +err_dma: + if (ret == -ENODEV) { + dev_warn(fmc2->dev, + "DMAs not defined in the DT, polling mode is used\n"); + ret = 0; + } + + return ret; } /* NAND callbacks setup */ From e015d72f321e8b54aa3aef680e85dcd87f912703 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 2 Mar 2020 15:45:09 -0300 Subject: [PATCH 35/55] mtd: rawnand: ingenic: Add dependency on MIPS || COMPILE_TEST This driver has no arch-specific instructions but is only ever useful on MIPS; so disable this driver if we're not compiling for MIPS, unless the driver is compile-tested. Signed-off-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200302184509.10666-1-paul@crapouillou.net --- drivers/mtd/nand/raw/ingenic/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig index e30feb56b650..96c5ae8b1bbc 100644 --- a/drivers/mtd/nand/raw/ingenic/Kconfig +++ b/drivers/mtd/nand/raw/ingenic/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config MTD_NAND_JZ4780 tristate "JZ4780 NAND controller" + depends on MIPS || COMPILE_TEST depends on JZ4780_NEMC help Enables support for NAND Flash connected to the NEMC on JZ4780 SoC From 92270086b7e5ada7ab381c06cc3da2e95ed17088 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Tue, 3 Mar 2020 15:21:21 +0800 Subject: [PATCH 36/55] mtd: rawnand: Add support for manufacturer specific lock/unlock operation Add nand_lock() & nand_unlock() for manufacturer specific lock & unlock operation while the device supports Block Portection function. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1583220084-10890-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_base.c | 36 ++++++++++++++++++++++++++++++-- include/linux/mtd/rawnand.h | 5 +++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a3ed6c54963e..a13b91aa3780 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4365,6 +4365,38 @@ static void nand_shutdown(struct mtd_info *mtd) nand_suspend(mtd); } +/** + * nand_lock - [MTD Interface] Lock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to lock (must be a multiple of block/page size) + */ +static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->lock_area) + return -ENOTSUPP; + + return chip->lock_area(chip, ofs, len); +} + +/** + * nand_unlock - [MTD Interface] Unlock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to unlock (must be a multiple of block/page size) + */ +static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->unlock_area) + return -ENOTSUPP; + + return chip->unlock_area(chip, ofs, len); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { @@ -5791,8 +5823,8 @@ static int nand_scan_tail(struct nand_chip *chip) mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; mtd->_sync = nand_sync; - mtd->_lock = NULL; - mtd->_unlock = NULL; + mtd->_lock = nand_lock; + mtd->_unlock = nand_unlock; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; mtd->_reboot = nand_shutdown; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3c7c15aadcee..49ed50fb44ab 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1077,6 +1077,8 @@ struct nand_legacy { * @manufacturer: [INTERN] Contains manufacturer information * @manufacturer.desc: [INTERN] Contains manufacturer's description * @manufacturer.priv: [INTERN] Contains manufacturer private information + * @lock_area: [REPLACEABLE] specific NAND chip lock operation + * @unlock_area: [REPLACEABLE] specific NAND chip unlock operation */ struct nand_chip { @@ -1136,6 +1138,9 @@ struct nand_chip { const struct nand_manufacturer *desc; void *priv; } manufacturer; + + int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); + int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); }; extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; From 03a539c7a118427a6609a26461358c56ac8f3a06 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Tue, 3 Mar 2020 15:21:22 +0800 Subject: [PATCH 37/55] mtd: rawnand: Macronix: Add support for block protection Macronix AC/AD series support using SET_FEATURES to change block protection and unprotection. Block protection support can be checked with GET_FEATURES. Signed-off-by: Mason Yang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1583220084-10890-3-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_macronix.c | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 0a2fe25f2639..fbe2fff0cf1b 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -11,6 +11,10 @@ #define MACRONIX_READ_RETRY_BIT BIT(0) #define MACRONIX_NUM_READ_RETRY_MODES 6 +#define ONFI_FEATURE_ADDR_MXIC_PROTECTION 0xA0 +#define MXIC_BLOCK_PROTECTION_ALL_LOCK 0x38 +#define MXIC_BLOCK_PROTECTION_ALL_UNLOCK 0x0 + #define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 #define MACRONIX_RANDOMIZER_BIT BIT(1) #define MACRONIX_RANDOMIZER_ENPGM BIT(0) @@ -172,6 +176,73 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip) ONFI_FEATURE_ADDR_TIMING_MODE, 1); } +/* + * Macronix NAND supports Block Protection by Protectoin(PT) pin; + * active high at power-on which protects the entire chip even the #WP is + * disabled. Lock/unlock protection area can be partition according to + * protection bits, i.e. upper 1/2 locked, upper 1/4 locked and so on. + */ +static int mxic_nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_LOCK; + nand_select_target(chip, 0); + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret) + pr_err("%s all blocks failed\n", __func__); + + return ret; +} + +static int mxic_nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; + nand_select_target(chip, 0); + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret) + pr_err("%s all blocks failed\n", __func__); + + return ret; +} + +static void macronix_nand_block_protection_support(struct nand_chip *chip) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; + nand_select_target(chip, 0); + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret || feature[0] != MXIC_BLOCK_PROTECTION_ALL_LOCK) { + if (ret) + pr_err("Block protection check failed\n"); + + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + return; + } + + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + + chip->lock_area = mxic_nand_lock; + chip->unlock_area = mxic_nand_unlock; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -179,6 +250,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_fix_broken_get_timings(chip); macronix_nand_onfi_init(chip); + macronix_nand_block_protection_support(chip); return 0; } From 397deafc02e1b91668fa1ee95fdd52d14fa82135 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:25 +0100 Subject: [PATCH 38/55] mtd: rawnand: cadence: get meta data size from registers Add checking size of BCH meta data size in capabilities registers instead of using fixed value. BCH meta data is used to keep data from NAND flash OOB area. Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-1-git-send-email-piotrs@cadence.com --- .../mtd/nand/raw/cadence-nand-controller.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index f6c7102a1e32..5063a8b493a4 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -30,7 +30,6 @@ * Generic mode is used for executing rest of commands. */ -#define MAX_OOB_SIZE_PER_SECTOR 32 #define MAX_ADDRESS_CYC 6 #define MAX_ERASE_ADDRESS_CYC 3 #define MAX_DATA_SIZE 0xFFFC @@ -190,6 +189,7 @@ /* BCH Engine identification register 3. */ #define BCH_CFG_3 0x844 +#define BCH_CFG_3_METADATA_SIZE GENMASK(23, 16) /* Ready/Busy# line status. */ #define RBN_SETINGS 0x1004 @@ -499,6 +499,7 @@ struct cdns_nand_ctrl { unsigned long assigned_cs; struct list_head chips; + u8 bch_metadata_size; }; struct cdns_nand_chip { @@ -1077,6 +1078,14 @@ static int cadence_nand_read_bch_caps(struct cdns_nand_ctrl *cdns_ctrl) int max_step_size = 0, nstrengths, i; u32 reg; + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_3); + cdns_ctrl->bch_metadata_size = FIELD_GET(BCH_CFG_3_METADATA_SIZE, reg); + if (cdns_ctrl->bch_metadata_size < 4) { + dev_err(cdns_ctrl->dev, + "Driver needs at least 4 bytes of BCH meta data\n"); + return -EIO; + } + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_0); cdns_ctrl->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg); cdns_ctrl->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg); @@ -1170,7 +1179,8 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl) writel_relaxed(0xFFFFFFFF, cdns_ctrl->reg + INTR_STATUS); cadence_nand_get_caps(cdns_ctrl); - cadence_nand_read_bch_caps(cdns_ctrl); + if (cadence_nand_read_bch_caps(cdns_ctrl)) + return -EIO; /* * Set IO width access to 8. @@ -2587,7 +2597,6 @@ int cadence_nand_attach_chip(struct nand_chip *chip) struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); u32 ecc_size = cdns_chip->sector_count * chip->ecc.bytes; struct mtd_info *mtd = nand_to_mtd(chip); - u32 max_oob_data_size; int ret; if (chip->options & NAND_BUSWIDTH_16) { @@ -2628,10 +2637,8 @@ int cadence_nand_attach_chip(struct nand_chip *chip) cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; - max_oob_data_size = MAX_OOB_SIZE_PER_SECTOR; - - if (cdns_chip->avail_oob_size > max_oob_data_size) - cdns_chip->avail_oob_size = max_oob_data_size; + if (cdns_chip->avail_oob_size > cdns_ctrl->bch_metadata_size) + cdns_chip->avail_oob_size = cdns_ctrl->bch_metadata_size; if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size) > mtd->oobsize) From e4578af0354176ff6b4ae78b9998b4f479f7c31c Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:26 +0100 Subject: [PATCH 39/55] mtd: rawnand: cadence: fix the calculation of the avaialble OOB size The value of cdns_chip->sector_count is not known at the moment of the derivation of ecc_size, leading to a zero value. Fix this by assigning ecc_size later in the code. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-2-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 5063a8b493a4..2ebfd0934739 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2595,7 +2595,7 @@ int cadence_nand_attach_chip(struct nand_chip *chip) { struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); - u32 ecc_size = cdns_chip->sector_count * chip->ecc.bytes; + u32 ecc_size; struct mtd_info *mtd = nand_to_mtd(chip); int ret; @@ -2634,6 +2634,7 @@ int cadence_nand_attach_chip(struct nand_chip *chip) /* Error correction configuration. */ cdns_chip->sector_size = chip->ecc.size; cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size; + ecc_size = cdns_chip->sector_count * chip->ecc.bytes; cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; From 9bf1903bed7a2e84f5a8deedb38f7e0ac5e8bfc6 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:27 +0100 Subject: [PATCH 40/55] mtd: rawnand: cadence: change bad block marker size Increase bad block marker size from one byte to two bytes. Bad block marker is handled by skip bytes feature of HPNFC. Controller expects this value to be an even number. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-3-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 2ebfd0934739..5c1bbb05ab51 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2612,12 +2612,9 @@ int cadence_nand_attach_chip(struct nand_chip *chip) chip->options |= NAND_NO_SUBPAGE_WRITE; cdns_chip->bbm_offs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - cdns_chip->bbm_offs &= ~0x01; - cdns_chip->bbm_len = 2; - } else { - cdns_chip->bbm_len = 1; - } + cdns_chip->bbm_offs &= ~0x01; + /* this value should be even number */ + cdns_chip->bbm_len = 2; ret = nand_ecc_choose_conf(chip, &cdns_ctrl->ecc_caps, From 0d7d6c8183aadb1dcc13f415941404a7913b46b3 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:28 +0100 Subject: [PATCH 41/55] mtd: rawnand: cadence: reinit completion before executing a new command Reing the completion object before executing CDMA command to make sure the 'done' flag is OK. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-4-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 5c1bbb05ab51..efddc5c68afb 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -998,6 +998,7 @@ static int cadence_nand_cdma_send(struct cdns_nand_ctrl *cdns_ctrl, return status; cadence_nand_reset_irq(cdns_ctrl); + reinit_completion(&cdns_ctrl->complete); writel_relaxed((u32)cdns_ctrl->dma_cdma_desc, cdns_ctrl->reg + CMD_REG2); From cdc6aba6719b9d7d85c6d411a43345ee12223268 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:11 -0500 Subject: [PATCH 42/55] dt: bindings: brcmnand: Add support for flash-edu Adding support for EBI DMA unit (EDU). Signed-off-by: Kamal Dasu Reviewed-by: Rob Herring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-2-kdasu.kdev@gmail.com --- .../devicetree/bindings/mtd/brcm,brcmnand.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt index 82156dc8f304..05651a654c66 100644 --- a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt +++ b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt @@ -35,11 +35,11 @@ Required properties: (optional) NAND flash cache range (if at non-standard offset) - reg-names : a list of the names corresponding to the previous register ranges. Should contain "nand" and (optionally) - "flash-dma" and/or "nand-cache". -- interrupts : The NAND CTLRDY interrupt and (if Flash DMA is available) - FLASH_DMA_DONE -- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done", if broken out as - individual interrupts. + "flash-dma" or "flash-edu" and/or "nand-cache". +- interrupts : The NAND CTLRDY interrupt, (if Flash DMA is available) + FLASH_DMA_DONE and if EDU is avaialble and used FLASH_EDU_DONE +- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done" or "flash_edu_done", + if broken out as individual interrupts. May be "nand", if the SoC has the individual NAND interrupts multiplexed behind another custom piece of hardware From 634088e2621310d2473e4ec3b69843e32d5cee20 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:12 -0500 Subject: [PATCH 43/55] arch: mips: brcm: Add 7425 flash-edu support Nand controller v5.0 and v6.0 have nand edu blocks that enable dma nand flash transfers. This allows for faster read and write access. Signed-off-by: Kamal Dasu Acked-by: Paul Burton Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-3-kdasu.kdev@gmail.com --- arch/mips/boot/dts/brcm/bcm7425.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/boot/dts/brcm/bcm7425.dtsi b/arch/mips/boot/dts/brcm/bcm7425.dtsi index 410e61ebaf9e..aa0b2d39c902 100644 --- a/arch/mips/boot/dts/brcm/bcm7425.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7425.dtsi @@ -403,8 +403,8 @@ nand: nand@41b800 { compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand"; #address-cells = <1>; #size-cells = <0>; - reg-names = "nand"; - reg = <0x41b800 0x400>; + reg-names = "nand", "flash-edu"; + reg = <0x41b800 0x400>, <0x41bc00 0x24>; interrupt-parent = <&hif_l2_intc>; interrupts = <24>; status = "disabled"; From a5d53ad26a8b441325eb9de8e9bb816584ebca7c Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:13 -0500 Subject: [PATCH 44/55] mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers Legacy mips soc platforms that have controller v5.0 and 6.0 use flash-edu block for dma transfers. This change adds support for nand dma transfers using the EDU block. Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-4-kdasu.kdev@gmail.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 293 ++++++++++++++++++++++- 1 file changed, 287 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 44518dada75b..e4e3ceeac38f 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -102,6 +102,45 @@ struct brcm_nand_dma_desc { #define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) #define NAND_POLL_STATUS_TIMEOUT_MS 100 +#define EDU_CMD_WRITE 0x00 +#define EDU_CMD_READ 0x01 +#define EDU_STATUS_ACTIVE BIT(0) +#define EDU_ERR_STATUS_ERRACK BIT(0) +#define EDU_DONE_MASK GENMASK(1, 0) + +#define EDU_CONFIG_MODE_NAND BIT(0) +#define EDU_CONFIG_SWAP_BYTE BIT(1) +#ifdef CONFIG_CPU_BIG_ENDIAN +#define EDU_CONFIG_SWAP_CFG EDU_CONFIG_SWAP_BYTE +#else +#define EDU_CONFIG_SWAP_CFG 0 +#endif + +/* edu registers */ +enum edu_reg { + EDU_CONFIG = 0, + EDU_DRAM_ADDR, + EDU_EXT_ADDR, + EDU_LENGTH, + EDU_CMD, + EDU_STOP, + EDU_STATUS, + EDU_DONE, + EDU_ERR_STATUS, +}; + +static const u16 edu_regs[] = { + [EDU_CONFIG] = 0x00, + [EDU_DRAM_ADDR] = 0x04, + [EDU_EXT_ADDR] = 0x08, + [EDU_LENGTH] = 0x0c, + [EDU_CMD] = 0x10, + [EDU_STOP] = 0x14, + [EDU_STATUS] = 0x18, + [EDU_DONE] = 0x1c, + [EDU_ERR_STATUS] = 0x20, +}; + /* flash_dma registers */ enum flash_dma_reg { FLASH_DMA_REVISION = 0, @@ -167,6 +206,8 @@ enum { BRCMNAND_HAS_WP = BIT(3), }; +struct brcmnand_host; + struct brcmnand_controller { struct device *dev; struct nand_controller controller; @@ -185,17 +226,32 @@ struct brcmnand_controller { int cmd_pending; bool dma_pending; + bool edu_pending; struct completion done; struct completion dma_done; + struct completion edu_done; /* List of NAND hosts (one for each chip-select) */ struct list_head host_list; + /* EDU info, per-transaction */ + const u16 *edu_offsets; + void __iomem *edu_base; + int edu_irq; + int edu_count; + u64 edu_dram_addr; + u32 edu_ext_addr; + u32 edu_cmd; + u32 edu_config; + /* flash_dma reg */ const u16 *flash_dma_offsets; struct brcm_nand_dma_desc *dma_desc; dma_addr_t dma_pa; + int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd); + /* in-memory cache of the FLASH_CACHE, used only for some commands */ u8 flash_cache[FC_BYTES]; @@ -216,6 +272,7 @@ struct brcmnand_controller { u32 nand_cs_nand_xor; u32 corr_stat_threshold; u32 flash_dma_mode; + u32 flash_edu_mode; bool pio_poll_mode; }; @@ -657,6 +714,22 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, __raw_writel(val, ctrl->nand_fc + word * 4); } +static inline void edu_writel(struct brcmnand_controller *ctrl, + enum edu_reg reg, u32 val) +{ + u16 offs = ctrl->edu_offsets[reg]; + + brcmnand_writel(val, ctrl->edu_base + offs); +} + +static inline u32 edu_readl(struct brcmnand_controller *ctrl, + enum edu_reg reg) +{ + u16 offs = ctrl->edu_offsets[reg]; + + return brcmnand_readl(ctrl->edu_base + offs); +} + static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl) { @@ -926,6 +999,16 @@ static inline bool has_flash_dma(struct brcmnand_controller *ctrl) return ctrl->flash_dma_base; } +static inline bool has_edu(struct brcmnand_controller *ctrl) +{ + return ctrl->edu_base; +} + +static inline bool use_dma(struct brcmnand_controller *ctrl) +{ + return has_flash_dma(ctrl) || has_edu(ctrl); +} + static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl) { if (ctrl->pio_poll_mode) @@ -1299,6 +1382,52 @@ static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, return tbytes; } +static void brcmnand_edu_init(struct brcmnand_controller *ctrl) +{ + /* initialize edu */ + edu_writel(ctrl, EDU_ERR_STATUS, 0); + edu_readl(ctrl, EDU_ERR_STATUS); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_readl(ctrl, EDU_DONE); +} + +/* edu irq */ +static irqreturn_t brcmnand_edu_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->edu_count) { + ctrl->edu_count--; + while (!(edu_readl(ctrl, EDU_DONE) & EDU_DONE_MASK)) + udelay(1); + edu_writel(ctrl, EDU_DONE, 0); + edu_readl(ctrl, EDU_DONE); + } + + if (ctrl->edu_count) { + ctrl->edu_dram_addr += FC_BYTES; + ctrl->edu_ext_addr += FC_BYTES; + + edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr); + edu_readl(ctrl, EDU_DRAM_ADDR); + edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr); + edu_readl(ctrl, EDU_EXT_ADDR); + + mb(); /* flush previous writes */ + edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); + edu_readl(ctrl, EDU_CMD); + + return IRQ_HANDLED; + } + + complete(&ctrl->edu_done); + + return IRQ_HANDLED; +} + static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) { struct brcmnand_controller *ctrl = data; @@ -1307,6 +1436,16 @@ static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) if (ctrl->dma_pending) return IRQ_HANDLED; + /* check if you need to piggy back on the ctrlrdy irq */ + if (ctrl->edu_pending) { + if (irq == ctrl->irq && ((int)ctrl->edu_irq >= 0)) + /* Discard interrupts while using dedicated edu irq */ + return IRQ_HANDLED; + + /* no registered edu irq, call handler */ + return brcmnand_edu_irq(irq, data); + } + complete(&ctrl->done); return IRQ_HANDLED; } @@ -1644,6 +1783,81 @@ static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf, } } +/** + * Kick EDU engine + */ +static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(200); + int ret = 0; + int dir = (cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + u8 edu_cmd = (cmd == CMD_PAGE_READ ? EDU_CMD_READ : EDU_CMD_WRITE); + unsigned int trans = len >> FC_SHIFT; + dma_addr_t pa; + + pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, pa)) { + dev_err(ctrl->dev, "unable to map buffer for EDU DMA\n"); + return -ENOMEM; + } + + ctrl->edu_pending = true; + ctrl->edu_dram_addr = pa; + ctrl->edu_ext_addr = addr; + ctrl->edu_cmd = edu_cmd; + ctrl->edu_count = trans; + + edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr); + edu_readl(ctrl, EDU_DRAM_ADDR); + edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr); + edu_readl(ctrl, EDU_EXT_ADDR); + edu_writel(ctrl, EDU_LENGTH, FC_BYTES); + edu_readl(ctrl, EDU_LENGTH); + + /* Start edu engine */ + mb(); /* flush previous writes */ + edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); + edu_readl(ctrl, EDU_CMD); + + if (wait_for_completion_timeout(&ctrl->edu_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for EDU; status %#x, error status %#x\n", + edu_readl(ctrl, EDU_STATUS), + edu_readl(ctrl, EDU_ERR_STATUS)); + } + + dma_unmap_single(ctrl->dev, pa, len, dir); + + /* for program page check NAND status */ + if (((brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS) & NAND_STATUS_FAIL) && + edu_cmd == EDU_CMD_WRITE) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + } + + /* Make sure the EDU status is clean */ + if (edu_readl(ctrl, EDU_STATUS) & EDU_STATUS_ACTIVE) + dev_warn(ctrl->dev, "EDU still active: %#x\n", + edu_readl(ctrl, EDU_STATUS)); + + if (unlikely(edu_readl(ctrl, EDU_ERR_STATUS) & EDU_ERR_STATUS_ERRACK)) { + dev_warn(ctrl->dev, "EDU RBUS error at addr %llx\n", + (unsigned long long)addr); + ret = -EIO; + } + + ctrl->edu_pending = false; + brcmnand_edu_init(ctrl); + edu_writel(ctrl, EDU_STOP, 0); /* force stop */ + edu_readl(ctrl, EDU_STOP); + + return ret; +} + /** * Construct a FLASH_DMA descriptor as part of a linked list. You must know the * following ahead of time: @@ -1850,9 +2064,11 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, try_dmaread: brcmnand_clear_ecc_addr(ctrl); - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, - CMD_PAGE_READ); + if (ctrl->dma_trans && !oob && flash_dma_buf_ok(buf)) { + err = ctrl->dma_trans(host, addr, buf, + trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { if (mtd_is_bitflip_or_eccerr(err)) err_addr = addr; @@ -1988,10 +2204,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < ctrl->max_oob; i += 4) oob_reg_write(ctrl, i, 0xffffffff); - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - if (brcmnand_dma_trans(host, addr, (u32 *)buf, - mtd->writesize, CMD_PROGRAM_PAGE)) + if (use_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (ctrl->dma_trans(host, addr, (u32 *)buf, mtd->writesize, + CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; } @@ -2494,6 +2712,8 @@ static int brcmnand_suspend(struct device *dev) if (has_flash_dma(ctrl)) ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + else if (has_edu(ctrl)) + ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG); return 0; } @@ -2508,6 +2728,14 @@ static int brcmnand_resume(struct device *dev) flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); } + if (has_edu(ctrl)) + ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG); + else { + edu_writel(ctrl, EDU_CONFIG, ctrl->edu_config); + edu_readl(ctrl, EDU_CONFIG); + brcmnand_edu_init(ctrl); + } + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, @@ -2553,6 +2781,49 @@ MODULE_DEVICE_TABLE(of, brcmnand_of_match); /*********************************************************************** * Platform driver setup (per controller) ***********************************************************************/ +static int brcmnand_edu_setup(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-edu"); + if (res) { + ctrl->edu_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->edu_base)) + return PTR_ERR(ctrl->edu_base); + + ctrl->edu_offsets = edu_regs; + + edu_writel(ctrl, EDU_CONFIG, EDU_CONFIG_MODE_NAND | + EDU_CONFIG_SWAP_CFG); + edu_readl(ctrl, EDU_CONFIG); + + /* initialize edu */ + brcmnand_edu_init(ctrl); + + ctrl->edu_irq = platform_get_irq_optional(pdev, 1); + if (ctrl->edu_irq < 0) { + dev_warn(dev, + "FLASH EDU enabled, using ctlrdy irq\n"); + } else { + ret = devm_request_irq(dev, ctrl->edu_irq, + brcmnand_edu_irq, 0, + "brcmnand-edu", ctrl); + if (ret < 0) { + dev_err(ctrl->dev, "can't allocate IRQ %d: error %d\n", + ctrl->edu_irq, ret); + return ret; + } + + dev_info(dev, "FLASH EDU enabled using irq %u\n", + ctrl->edu_irq); + } + } + + return 0; +} int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) { @@ -2578,6 +2849,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) init_completion(&ctrl->done); init_completion(&ctrl->dma_done); + init_completion(&ctrl->edu_done); nand_controller_init(&ctrl->controller); ctrl->controller.ops = &brcmnand_controller_ops; INIT_LIST_HEAD(&ctrl->host_list); @@ -2675,6 +2947,15 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) } dev_info(dev, "enabling FLASH_DMA\n"); + /* set flash dma transfer function to call */ + ctrl->dma_trans = brcmnand_dma_trans; + } else { + ret = brcmnand_edu_setup(pdev); + if (ret < 0) + goto err; + + /* set edu transfer function to call */ + ctrl->dma_trans = brcmnand_edu_trans; } /* Disable automatic device ID config, direct addressing */ From d3137043440fb1faaaf2481184f35b9ed0c1f2c2 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:30 +0100 Subject: [PATCH 45/55] mtd: spinand: micron: Generalize the OOB layout structure and function names In order to add new Micron SPI NAND devices, we generalized the OOB layout structure and function names. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-2-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index f56f81325e10..cc1ee68421c8 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -34,38 +34,38 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section) return -ERANGE; - region->offset = 64; - region->length = 64; + region->offset = mtd->oobsize / 2; + region->length = mtd->oobsize / 2; return 0; } -static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int micron_8_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section) return -ERANGE; /* Reserve 2 bytes for the BBM. */ region->offset = 2; - region->length = 62; + region->length = (mtd->oobsize / 2) - 2; return 0; } -static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { - .ecc = mt29f2g01abagd_ooblayout_ecc, - .free = mt29f2g01abagd_ooblayout_free, +static const struct mtd_ooblayout_ops micron_8_ooblayout = { + .ecc = micron_8_ooblayout_ecc, + .free = micron_8_ooblayout_free, }; -static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int micron_8_ecc_get_status(struct spinand_device *spinand, + u8 status) { switch (status & MICRON_STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: @@ -99,8 +99,8 @@ static const struct spinand_info micron_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, - mt29f2g01abagd_ecc_get_status)), + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { From 8511a3a9937e30949b34bea46c3dc3f65d11034b Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:31 +0100 Subject: [PATCH 46/55] mtd: spinand: micron: Describe the SPI NAND device MT29F2G01ABAGD Add the SPI NAND device MT29F2G01ABAGD series number, size and voltage details as a comment. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-3-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index cc1ee68421c8..4727933c894b 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -91,6 +91,7 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info micron_spinand_table[] = { + /* M79A 2Gb 3.3V */ SPINAND_INFO("MT29F2G01ABAGD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), From a15335a17f4abf48ed9739c3b119232f9392cb60 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:32 +0100 Subject: [PATCH 47/55] mtd: spinand: micron: Add new Micron SPI NAND devices Add device table for M79A and M78A series Micron SPI NAND devices. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-4-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 4727933c894b..26925714a9fb 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -102,6 +102,39 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M79A 2Gb 1.8V */ + SPINAND_INFO("MT29F2G01ABBGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 3.3V */ + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 1.8V */ + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { From 0bc68af9137dc3f30b161de4ce546c7799f88d1e Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:33 +0100 Subject: [PATCH 48/55] mtd: spinand: micron: identify SPI NAND device with Continuous Read mode Add SPINAND_HAS_CR_FEAT_BIT flag to identify the SPI NAND device with the Continuous Read mode. Some of the Micron SPI NAND devices have the "Continuous Read" feature enabled by default, which does not fit the subsystem needs. In this mode, the READ CACHE command doesn't require the starting column address. The device always output the data starting from the first column of the cache register, and once the end of the cache register reached, the data output continues through the next page. With the continuous read mode, it is possible to read out the entire block using a single READ command, and once the end of the block reached, the output pins become High-Z state. However, during this mode the read command doesn't output the OOB area. Hence, we disable the feature at probe time. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-5-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 16 ++++++++++++++++ include/linux/mtd/spinand.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 26925714a9fb..956f7710aca2 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -18,6 +18,8 @@ #define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) #define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) +#define MICRON_CFG_CR BIT(0) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -137,7 +139,21 @@ static const struct spinand_info micron_spinand_table[] = { micron_8_ecc_get_status)), }; +static int micron_spinand_init(struct spinand_device *spinand) +{ + /* + * M70A device series enable Continuous Read feature at Power-up, + * which is not supported. Disable this bit to avoid any possible + * failure. + */ + if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT) + return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0); + + return 0; +} + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .init = micron_spinand_init, }; const struct spinand_manufacturer micron_spinand_manufacturer = { diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index f4c4ae87181b..1077c45721ff 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -284,6 +284,7 @@ struct spinand_ecc_info { }; #define SPINAND_HAS_QE_BIT BIT(0) +#define SPINAND_HAS_CR_FEAT_BIT BIT(1) /** * struct spinand_info - Structure used to describe SPI NAND chips From a7e5daccc310c3b892ae5e598cadb7a9274c2547 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:34 +0100 Subject: [PATCH 49/55] mtd: spinand: micron: Add M70A series Micron SPI NAND devices Add device table for M70A series Micron SPI NAND devices. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-6-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 956f7710aca2..d6fd63008782 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -137,6 +137,28 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M70A 4Gb 3.3V */ + SPINAND_INFO("MT29F4G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M70A 4Gb 1.8V */ + SPINAND_INFO("MT29F4G01ABBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static int micron_spinand_init(struct spinand_device *spinand) From 9f9ae0c253c1e058fbc845e26c4a32a7d777f0dc Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:35 +0100 Subject: [PATCH 50/55] mtd: spinand: micron: Add new Micron SPI NAND devices with multiple dies Add device table for new Micron SPI NAND devices, which have multiple dies. Also, enable support to select the dies. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-7-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index d6fd63008782..5d370cfcdaaa 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -20,6 +20,14 @@ #define MICRON_CFG_CR BIT(0) +/* + * As per datasheet, die selection is done by the 6th bit of Die + * Select Register (Address 0xD0). + */ +#define MICRON_DIE_SELECT_REG 0xD0 + +#define MICRON_SELECT_DIE(x) ((x) << 6) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -66,6 +74,20 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { .free = micron_8_ooblayout_free, }; +static int micron_select_target(struct spinand_device *spinand, + unsigned int target) +{ + struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG, + spinand->scratchbuf); + + if (target > 1) + return -EINVAL; + + *spinand->scratchbuf = MICRON_SELECT_DIE(target); + + return spi_mem_exec_op(spinand->spimem, &op); +} + static int micron_8_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -137,6 +159,18 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M79A 4Gb 3.3V */ + SPINAND_INFO("MT29F4G01ADAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), + NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), /* M70A 4Gb 3.3V */ SPINAND_INFO("MT29F4G01ABAFD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), @@ -159,6 +193,30 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M70A 8Gb 3.3V */ + SPINAND_INFO("MT29F8G01ADAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), + /* M70A 8Gb 1.8V */ + SPINAND_INFO("MT29F8G01ADBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), }; static int micron_spinand_init(struct spinand_device *spinand) From adc6162b9a0c60a81cf6a107196924526cd186f6 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 18 Mar 2020 15:42:27 +0800 Subject: [PATCH 51/55] mtd: rawnand: Add support for manufacturer specific suspend/resume operation Patch nand_suspend() & nand_resume() to let manufacturers overwrite suspend/resume operations. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1584517348-14486-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_base.c | 17 +++++++++++++---- include/linux/mtd/rawnand.h | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a13b91aa3780..985a15a735af 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4326,16 +4326,22 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) /** * nand_suspend - [MTD Interface] Suspend the NAND flash * @mtd: MTD device structure + * + * Returns 0 for success or negative error code otherwise. */ static int nand_suspend(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; mutex_lock(&chip->lock); - chip->suspended = 1; + if (chip->suspend) + ret = chip->suspend(chip); + if (!ret) + chip->suspended = 1; mutex_unlock(&chip->lock); - return 0; + return ret; } /** @@ -4347,11 +4353,14 @@ static void nand_resume(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); mutex_lock(&chip->lock); - if (chip->suspended) + if (chip->suspended) { + if (chip->resume) + chip->resume(chip); chip->suspended = 0; - else + } else { pr_err("%s called for a chip which is not in suspended state\n", __func__); + } mutex_unlock(&chip->lock); } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 49ed50fb44ab..1e76196f9829 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1064,6 +1064,8 @@ struct nand_legacy { * @lock: lock protecting the suspended field. Also used to * serialize accesses to the NAND device. * @suspended: set to 1 when the device is suspended, 0 when it's not. + * @suspend: [REPLACEABLE] specific NAND device suspend operation + * @resume: [REPLACEABLE] specific NAND device resume operation * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -1119,6 +1121,8 @@ struct nand_chip { struct mutex lock; unsigned int suspended : 1; + int (*suspend)(struct nand_chip *chip); + void (*resume)(struct nand_chip *chip); uint8_t *oob_poi; struct nand_controller *controller; From 19301d54997df35e9829b4f707c59f3284530d71 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 18 Mar 2020 15:42:28 +0800 Subject: [PATCH 52/55] mtd: rawnand: macronix: Add support for deep power down mode Macronix AD series support deep power down mode for a minimum power consumption state. Overload nand_suspend() & nand_resume() in Macronix specific code to support deep power down mode. Signed-off-by: Mason Yang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_macronix.c | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index fbe2fff0cf1b..09c254c97b5c 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -6,6 +6,7 @@ * Author: Boris Brezillon */ +#include "linux/delay.h" #include "internals.h" #define MACRONIX_READ_RETRY_BIT BIT(0) @@ -28,6 +29,8 @@ (MACRONIX_RANDOMIZER_RANDEN | \ MACRONIX_RANDOMIZER_RANDOPT) +#define MXIC_CMD_POWER_DOWN 0xB9 + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -243,6 +246,76 @@ static void macronix_nand_block_protection_support(struct nand_chip *chip) chip->unlock_area = mxic_nand_unlock; } +static int nand_power_down_op(struct nand_chip *chip) +{ + int ret; + + if (nand_has_exec_op(chip)) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(MXIC_CMD_POWER_DOWN, 0), + }; + + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + } else { + chip->legacy.cmdfunc(chip, MXIC_CMD_POWER_DOWN, -1, -1); + } + + return 0; +} + +static int mxic_nand_suspend(struct nand_chip *chip) +{ + int ret; + + nand_select_target(chip, 0); + ret = nand_power_down_op(chip); + if (ret < 0) + pr_err("Suspending MXIC NAND chip failed (%d)\n", ret); + nand_deselect_target(chip); + + return ret; +} + +static void mxic_nand_resume(struct nand_chip *chip) +{ + /* + * Toggle #CS pin to resume NAND device and don't care + * of the others CLE, #WE, #RE pins status. + * A NAND controller ensure it is able to assert/de-assert #CS + * by sending any byte over the NAND bus. + * i.e., + * NAND power down command or reset command w/o R/B# status checking. + */ + nand_select_target(chip, 0); + nand_power_down_op(chip); + /* The minimum of a recovery time tRDP is 35 us */ + usleep_range(35, 100); + nand_deselect_target(chip); +} + +static void macronix_nand_deep_power_down_support(struct nand_chip *chip) +{ + int i; + static const char * const deep_power_down_dev[] = { + "MX30UF1G28AD", + "MX30UF2G28AD", + "MX30UF4G28AD", + }; + + i = match_string(deep_power_down_dev, ARRAY_SIZE(deep_power_down_dev), + chip->parameters.model); + if (i < 0) + return; + + chip->suspend = mxic_nand_suspend; + chip->resume = mxic_nand_resume; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -251,6 +324,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_fix_broken_get_timings(chip); macronix_nand_onfi_init(chip); macronix_nand_block_protection_support(chip); + macronix_nand_deep_power_down_support(chip); return 0; } From 6b49e58d6d9dab031a16af2af5439f28a37c4cd9 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Tue, 24 Mar 2020 15:49:44 +0900 Subject: [PATCH 53/55] mtd: spinand: toshiba: Rename function name to change suffix and prefix (8Gbit) The suffix was changed from "G" to "J" to classify between 1st generation and 2nd generation serial NAND devices (which now belong to the Kioxia brand). As reference that's 1st generation device of 1Gbit product is "TC58CVG0S3HRAIG" 2nd generation device of 1Gbit product is "TC58CVG0S3HRAIJ". The 8Gbit type "TH58CxG3S0HRAIJ" is new to Kioxia's serial NAND lineup and the prefix was changed from "TC58" to "TH58". Thus the functions were renamed from tc58cxgxsx_*() to tx58cxgxsxraix_*(). Signed-off-by: Yoshio Furuyama Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/0dedd9869569a17625822dba87878254d253ba0e.1584949601.git.ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/spi/toshiba.c | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index d34773191700..5d217dd4b253 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -26,8 +26,8 @@ static SPINAND_OP_VARIANTS(write_cache_variants, static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -38,8 +38,8 @@ static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int tx58cxgxsxraix_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -51,13 +51,13 @@ static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = { - .ecc = tc58cxgxsx_ooblayout_ecc, - .free = tc58cxgxsx_ooblayout_free, +static const struct mtd_ooblayout_ops tx58cxgxsxraix_ooblayout = { + .ecc = tx58cxgxsxraix_ooblayout_ecc, + .free = tx58cxgxsxraix_ooblayout_free, }; -static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, + u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; @@ -96,7 +96,7 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb */ - SPINAND_INFO("TC58CVG0S3", + SPINAND_INFO("TC58CVG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -104,10 +104,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 2Gb */ - SPINAND_INFO("TC58CVG1S3", + SPINAND_INFO("TC58CVG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -115,10 +115,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", + SPINAND_INFO("TC58CVG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -126,10 +126,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", + SPINAND_INFO("TC58CVG2S0HRAIJ", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -137,10 +137,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 1Gb */ - SPINAND_INFO("TC58CYG0S3", + SPINAND_INFO("TC58CYG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -148,10 +148,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 2Gb */ - SPINAND_INFO("TC58CYG1S3", + SPINAND_INFO("TC58CYG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -159,10 +159,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 4Gb */ - SPINAND_INFO("TC58CYG2S0", + SPINAND_INFO("TC58CYG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -170,8 +170,8 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), }; static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { From 798fcdd010006e87b3154d6454c657af7b033002 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Tue, 24 Mar 2020 15:49:55 +0900 Subject: [PATCH 54/55] mtd: spinand: toshiba: Support for new Kioxia Serial NAND Add support for new Kioxia products. The new Kioxia products support program load x4 command, and have HOLD_D bit which is equivalent to QE bit. Signed-off-by: Yoshio Furuyama Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/aa69e455beedc5ce0d7141359b9364ed8aec9e65.1584949601.git.ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/spi/toshiba.c | 128 ++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 5d217dd4b253..bc801d83343e 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -20,6 +20,18 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); +static SPINAND_OP_VARIANTS(write_cache_x4_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_x4_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +/** + * Backward compatibility for 1st generation Serial NAND devices + * which don't support Quad Program Load operation. + */ static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -95,7 +107,7 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info toshiba_spinand_table[] = { - /* 3.3V 1Gb */ + /* 3.3V 1Gb (1st generation) */ SPINAND_INFO("TC58CVG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -106,7 +118,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 2Gb */ + /* 3.3V 2Gb (1st generation) */ SPINAND_INFO("TC58CVG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -117,7 +129,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 4Gb */ + /* 3.3V 4Gb (1st generation) */ SPINAND_INFO("TC58CVG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -128,18 +140,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0HRAIJ", - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), - NAND_MEMORG(1, 4096, 256, 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(&tx58cxgxsxraix_ooblayout, - tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 1Gb */ + /* 1.8V 1Gb (1st generation) */ SPINAND_INFO("TC58CYG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -150,7 +151,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 2Gb */ + /* 1.8V 2Gb (1st generation) */ SPINAND_INFO("TC58CYG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -161,7 +162,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 4Gb */ + /* 1.8V 4Gb (1st generation) */ SPINAND_INFO("TC58CYG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -172,6 +173,99 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), + + /* + * 2nd generation serial nand has HOLD_D which is equivalent to + * QE_BIT. + */ + /* 3.3V 1Gb (2nd generation) */ + SPINAND_INFO("TC58CVG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 2Gb (2nd generation) */ + SPINAND_INFO("TC58CVG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 4Gb (2nd generation) */ + SPINAND_INFO("TC58CVG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 8Gb (2nd generation) */ + SPINAND_INFO("TH58CVG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb (2nd generation) */ + SPINAND_INFO("TC58CYG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 2Gb (2nd generation) */ + SPINAND_INFO("TC58CYG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb (2nd generation) */ + SPINAND_INFO("TC58CYG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 8Gb (2nd generation) */ + SPINAND_INFO("TH58CYG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), }; static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { From fca88925d76978b7f20de42d8ead34fb91500003 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Wed, 25 Mar 2020 17:22:52 +0900 Subject: [PATCH 55/55] mtd: rawnand: toshiba: Support reading the number of bitflips for BENAND (Built-in ECC NAND) Add support vendor specific commands for KIOXIA CORPORATION BENAND. The actual bitflips number can be retrieved by this command. Signed-off-by: Yoshio Furuyama Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1585124572-4693-1-git-send-email-ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/raw/nand_toshiba.c | 58 ++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index 9c03fbb1f47d..f3dcd695b5db 100644 --- a/drivers/mtd/nand/raw/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -14,14 +14,68 @@ /* Recommended to rewrite for BENAND */ #define TOSHIBA_NAND_STATUS_REWRITE_RECOMMENDED BIT(3) +/* ECC Status Read Command for BENAND */ +#define TOSHIBA_NAND_CMD_ECC_STATUS_READ 0x7A + +/* ECC Status Mask for BENAND */ +#define TOSHIBA_NAND_ECC_STATUS_MASK 0x0F + +/* Uncorrectable Error for BENAND */ +#define TOSHIBA_NAND_ECC_STATUS_UNCORR 0x0F + +/* Max ECC Steps for BENAND */ +#define TOSHIBA_NAND_MAX_ECC_STEPS 8 + +static int toshiba_nand_benand_read_eccstatus_op(struct nand_chip *chip, + u8 *buf) +{ + u8 *ecc_status = buf; + + if (nand_has_exec_op(chip)) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ, + PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(chip->ecc.steps, ecc_status, 0), + }; + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + + return nand_exec_op(chip, &op); + } + + return -ENOTSUPP; +} + static int toshiba_nand_benand_eccstatus(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); int ret; unsigned int max_bitflips = 0; - u8 status; + u8 status, ecc_status[TOSHIBA_NAND_MAX_ECC_STEPS]; /* Check Status */ + ret = toshiba_nand_benand_read_eccstatus_op(chip, ecc_status); + if (!ret) { + unsigned int i, bitflips = 0; + + for (i = 0; i < chip->ecc.steps; i++) { + bitflips = ecc_status[i] & TOSHIBA_NAND_ECC_STATUS_MASK; + if (bitflips == TOSHIBA_NAND_ECC_STATUS_UNCORR) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += bitflips; + max_bitflips = max(max_bitflips, bitflips); + } + } + + return max_bitflips; + } + + /* + * Fallback to regular status check if + * toshiba_nand_benand_read_eccstatus_op() failed. + */ ret = nand_status_op(chip, &status); if (ret) return ret; @@ -108,7 +162,7 @@ static void toshiba_nand_decode_id(struct nand_chip *chip) */ if (chip->id.len >= 6 && nand_is_slc(chip) && (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && - !(chip->id.data[4] & 0x80) /* !BENAND */) { + !(chip->id.data[4] & TOSHIBA_NAND_ID4_IS_BENAND) /* !BENAND */) { memorg->oobsize = 32 * memorg->pagesize >> 9; mtd->oobsize = memorg->oobsize; }