From 9471c6ebf176284108115f60e22a176e70b97c0b Mon Sep 17 00:00:00 2001 From: Chen Minqiang Date: Thu, 12 Oct 2023 06:51:40 +0800 Subject: [PATCH] mtd: spinand: winbond: Support for W25MxxGV W25NxxKV series --- drivers/mtd/nand/spi/winbond.c | 115 +++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -15,6 +15,23 @@ #define WINBOND_CFG_BUF_READ BIT(3) +#define W25N02_N04KV_STATUS_ECC_MASK (3 << 4) +#define W25N02_N04KV_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS (1 << 4) +#define W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4) +#define W25N02_N04KV_STATUS_ECC_UNCOR_ERROR (2 << 4) + +#define W25N01_M02GV_STATUS_ECC_MASK (3 << 4) +#define W25N01_M02GV_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define W25N01_M02GV_STATUS_ECC_1_BITFLIPS (1 << 4) +#define W25N01_M02GV_STATUS_ECC_UNCOR_ERROR (2 << 4) + +#define W25N01KV_STATUS_ECC_MASK (3 << 4) +#define W25N01KV_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define W25N01KV_STATUS_ECC_1_3_BITFLIPS (1 << 4) +#define W25N01KV_STATUS_ECC_4_BITFLIPS (3 << 4) +#define W25N01KV_STATUS_ECC_UNCOR_ERROR (2 << 4) + 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), @@ -31,6 +48,29 @@ static SPINAND_OP_VARIANTS(update_cache_ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); +static int w25n02kv_n04kv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int w25n02kv_n04kv_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 2; + region->length = 14; + + return 0; +} + +static const struct mtd_ooblayout_ops w25n02kv_n04kv_ooblayout = { + .ecc = w25n02kv_n04kv_ooblayout_ecc, + .free = w25n02kv_n04kv_ooblayout_free, +}; + static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -140,6 +180,58 @@ static int w25n02kv_ecc_get_status(struc return -EINVAL; } +static int w25n01kv_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & W25N01KV_STATUS_ECC_MASK) { + case W25N01KV_STATUS_ECC_NO_BITFLIPS: + return 0; + + case W25N01KV_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case W25N01KV_STATUS_ECC_4_BITFLIPS: + return 4; + + case W25N01KV_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int w25n02kv_n04kv_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & W25N02_N04KV_STATUS_ECC_MASK) { + case W25N02_N04KV_STATUS_ECC_NO_BITFLIPS: + return 0; + + case W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS: + return 3; + + case W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS: + return 4; + + /* W25N02_N04KV_use internal 8bit ECC algorithm. + * But the ECC strength is 4 bit requried. + * Return 3 if the bit bit flip count less than 5. + * Return 4 if the bit bit flip count more than 5 to 8. + */ + + case W25N02_N04KV_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO("W25M02GV", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), @@ -151,6 +243,16 @@ static const struct spinand_info winbond 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), + SPINAND_INFO("W25N01KV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, + w25n01kv_ecc_get_status)), SPINAND_INFO("W25N01GV", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -169,6 +271,19 @@ static const struct spinand_info winbond &update_cache_variants), 0, SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + /* W25N04KV has 2-die(lun), however, it can select die automatically. + * Treat it as single die here and double block size. + */ + SPINAND_INFO("W25N04KV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23), + NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, + w25n02kv_n04kv_ecc_get_status)), }; static int winbond_spinand_init(struct spinand_device *spinand)