| --- a/drivers/mtd/nand/spi/winbond.c |
| +++ b/drivers/mtd/nand/spi/winbond.c |
| @@ -15,6 +15,25 @@ |
| |
| #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) |
| + |
| +#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) |
| +#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) |
| +#endif |
| + |
| 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 +50,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) |
| { |
| @@ -74,9 +116,63 @@ static int w25m02gv_select_target(struct |
| return spi_mem_exec_op(spinand->spimem, &op); |
| } |
| |
| +#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) |
| +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; |
| +} |
| +#endif |
| + |
| +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), |
| + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), |
| NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), |
| NAND_ECCREQ(1, 512), |
| SPINAND_INFO_OP_VARIANTS(&read_cache_variants, |
| @@ -85,8 +181,20 @@ static const struct spinand_info winbond |
| 0, |
| SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), |
| SPINAND_SELECT_TARGET(w25m02gv_select_target)), |
| +#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) |
| + SPINAND_INFO("W25N01KV", |
| + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), |
| + NAND_MEMORG(1, 2048, 96, 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)), |
| +#else |
| SPINAND_INFO("W25N01GV", |
| - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), |
| + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), |
| NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), |
| NAND_ECCREQ(1, 512), |
| SPINAND_INFO_OP_VARIANTS(&read_cache_variants, |
| @@ -94,6 +202,30 @@ static const struct spinand_info winbond |
| &update_cache_variants), |
| 0, |
| SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), |
| +#endif |
| + SPINAND_INFO("W25N02KV", |
| + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), |
| + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 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, |
| + w25n02kv_n04kv_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(4, 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) |
| --- a/drivers/mtd/nand/spi/Kconfig |
| +++ b/drivers/mtd/nand/spi/Kconfig |
| @@ -6,3 +6,12 @@ menuconfig MTD_SPI_NAND |
| select SPI_MEM |
| help |
| This is the framework for the SPI NAND device drivers. |
| + |
| +config MTD_SPI_NAND_W25N01KV |
| + tristate "Winbond W25N01KV Support" |
| + select MTD_SPI_NAND |
| + default n |
| + help |
| + Winbond W25N01KV share the same ID with W25N01GV. However, they have |
| + different attributes. |
| + |