1c93c6132SChuanhong Guo // SPDX-License-Identifier: GPL-2.0 2c93c6132SChuanhong Guo /* 3c93c6132SChuanhong Guo * Author: 4c93c6132SChuanhong Guo * Chuanhong Guo <gch981213@gmail.com> 5c93c6132SChuanhong Guo */ 6c93c6132SChuanhong Guo 7c93c6132SChuanhong Guo #include <linux/device.h> 8c93c6132SChuanhong Guo #include <linux/kernel.h> 9c93c6132SChuanhong Guo #include <linux/mtd/spinand.h> 10c93c6132SChuanhong Guo 11c93c6132SChuanhong Guo #define SPINAND_MFR_GIGADEVICE 0xC8 12cfd93d7cSJeff Kletsky 13c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) 14c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) 15c93c6132SChuanhong Guo 16c40c7a99SStefan Roese #define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 17c40c7a99SStefan Roese 18cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) 19cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) 20cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) 21cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) 22cfd93d7cSJeff Kletsky 23c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(read_cache_variants, 24*6387ad9cSHauke Mehrtens SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), 25c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 26c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 27c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 28c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 29c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 30c93c6132SChuanhong Guo 31cfd93d7cSJeff Kletsky static SPINAND_OP_VARIANTS(read_cache_variants_f, 32*6387ad9cSHauke Mehrtens SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), 33cfd93d7cSJeff Kletsky SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), 34cfd93d7cSJeff Kletsky SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 35cfd93d7cSJeff Kletsky SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), 36cfd93d7cSJeff Kletsky SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), 37cfd93d7cSJeff Kletsky SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); 38cfd93d7cSJeff Kletsky 39c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(write_cache_variants, 40c93c6132SChuanhong Guo SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 41c93c6132SChuanhong Guo SPINAND_PROG_LOAD(true, 0, NULL, 0)); 42c93c6132SChuanhong Guo 43c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(update_cache_variants, 44c93c6132SChuanhong Guo SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 45c93c6132SChuanhong Guo SPINAND_PROG_LOAD(false, 0, NULL, 0)); 46c93c6132SChuanhong Guo 47c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, 48c93c6132SChuanhong Guo struct mtd_oob_region *region) 49c93c6132SChuanhong Guo { 50c93c6132SChuanhong Guo if (section > 3) 51c93c6132SChuanhong Guo return -ERANGE; 52c93c6132SChuanhong Guo 53c93c6132SChuanhong Guo region->offset = (16 * section) + 8; 54c93c6132SChuanhong Guo region->length = 8; 55c93c6132SChuanhong Guo 56c93c6132SChuanhong Guo return 0; 57c93c6132SChuanhong Guo } 58c93c6132SChuanhong Guo 59c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, 60c93c6132SChuanhong Guo struct mtd_oob_region *region) 61c93c6132SChuanhong Guo { 62c93c6132SChuanhong Guo if (section > 3) 63c93c6132SChuanhong Guo return -ERANGE; 64c93c6132SChuanhong Guo 65c93c6132SChuanhong Guo if (section) { 66c93c6132SChuanhong Guo region->offset = 16 * section; 67c93c6132SChuanhong Guo region->length = 8; 68c93c6132SChuanhong Guo } else { 69c93c6132SChuanhong Guo /* section 0 has one byte reserved for bad block mark */ 70c93c6132SChuanhong Guo region->offset = 1; 71c93c6132SChuanhong Guo region->length = 7; 72c93c6132SChuanhong Guo } 73c93c6132SChuanhong Guo return 0; 74c93c6132SChuanhong Guo } 75c93c6132SChuanhong Guo 76cfd93d7cSJeff Kletsky static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { 77cfd93d7cSJeff Kletsky .ecc = gd5fxgq4xa_ooblayout_ecc, 78cfd93d7cSJeff Kletsky .free = gd5fxgq4xa_ooblayout_free, 79cfd93d7cSJeff Kletsky }; 80cfd93d7cSJeff Kletsky 81c93c6132SChuanhong Guo static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, 82c93c6132SChuanhong Guo u8 status) 83c93c6132SChuanhong Guo { 84c93c6132SChuanhong Guo switch (status & STATUS_ECC_MASK) { 85c93c6132SChuanhong Guo case STATUS_ECC_NO_BITFLIPS: 86c93c6132SChuanhong Guo return 0; 87c93c6132SChuanhong Guo 88c93c6132SChuanhong Guo case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 89c93c6132SChuanhong Guo /* 1-7 bits are flipped. return the maximum. */ 90c93c6132SChuanhong Guo return 7; 91c93c6132SChuanhong Guo 92c93c6132SChuanhong Guo case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 93c93c6132SChuanhong Guo return 8; 94c93c6132SChuanhong Guo 95c93c6132SChuanhong Guo case STATUS_ECC_UNCOR_ERROR: 96c93c6132SChuanhong Guo return -EBADMSG; 97c93c6132SChuanhong Guo 98c93c6132SChuanhong Guo default: 99c93c6132SChuanhong Guo break; 100c93c6132SChuanhong Guo } 101c93c6132SChuanhong Guo 102c93c6132SChuanhong Guo return -EINVAL; 103c93c6132SChuanhong Guo } 104c93c6132SChuanhong Guo 105cfd93d7cSJeff Kletsky static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, 106c40c7a99SStefan Roese struct mtd_oob_region *region) 107c40c7a99SStefan Roese { 108c40c7a99SStefan Roese if (section) 109c40c7a99SStefan Roese return -ERANGE; 110c40c7a99SStefan Roese 111c40c7a99SStefan Roese region->offset = 64; 112c40c7a99SStefan Roese region->length = 64; 113c40c7a99SStefan Roese 114c40c7a99SStefan Roese return 0; 115c40c7a99SStefan Roese } 116c40c7a99SStefan Roese 117cfd93d7cSJeff Kletsky static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, 118c40c7a99SStefan Roese struct mtd_oob_region *region) 119c40c7a99SStefan Roese { 120c40c7a99SStefan Roese if (section) 121c40c7a99SStefan Roese return -ERANGE; 122c40c7a99SStefan Roese 123c40c7a99SStefan Roese /* Reserve 1 bytes for the BBM. */ 124c40c7a99SStefan Roese region->offset = 1; 125c40c7a99SStefan Roese region->length = 63; 126c40c7a99SStefan Roese 127c40c7a99SStefan Roese return 0; 128c40c7a99SStefan Roese } 129c40c7a99SStefan Roese 130cfd93d7cSJeff Kletsky static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { 131cfd93d7cSJeff Kletsky .ecc = gd5fxgq4_variant2_ooblayout_ecc, 132cfd93d7cSJeff Kletsky .free = gd5fxgq4_variant2_ooblayout_free, 133cfd93d7cSJeff Kletsky }; 134cfd93d7cSJeff Kletsky 135c40c7a99SStefan Roese static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, 136c40c7a99SStefan Roese u8 status) 137c40c7a99SStefan Roese { 138c40c7a99SStefan Roese u8 status2; 139c40c7a99SStefan Roese struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, 140c40c7a99SStefan Roese &status2); 141c40c7a99SStefan Roese int ret; 142c40c7a99SStefan Roese 143c40c7a99SStefan Roese switch (status & STATUS_ECC_MASK) { 144c40c7a99SStefan Roese case STATUS_ECC_NO_BITFLIPS: 145c40c7a99SStefan Roese return 0; 146c40c7a99SStefan Roese 147c40c7a99SStefan Roese case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 148c40c7a99SStefan Roese /* 149c40c7a99SStefan Roese * Read status2 register to determine a more fine grained 150c40c7a99SStefan Roese * bit error status 151c40c7a99SStefan Roese */ 152c40c7a99SStefan Roese ret = spi_mem_exec_op(spinand->spimem, &op); 153c40c7a99SStefan Roese if (ret) 154c40c7a99SStefan Roese return ret; 155c40c7a99SStefan Roese 156c40c7a99SStefan Roese /* 157c40c7a99SStefan Roese * 4 ... 7 bits are flipped (1..4 can't be detected, so 158c40c7a99SStefan Roese * report the maximum of 4 in this case 159c40c7a99SStefan Roese */ 160c40c7a99SStefan Roese /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */ 161c40c7a99SStefan Roese return ((status & STATUS_ECC_MASK) >> 2) | 162c40c7a99SStefan Roese ((status2 & STATUS_ECC_MASK) >> 4); 163c40c7a99SStefan Roese 164c40c7a99SStefan Roese case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 165c40c7a99SStefan Roese return 8; 166c40c7a99SStefan Roese 167c40c7a99SStefan Roese case STATUS_ECC_UNCOR_ERROR: 168c40c7a99SStefan Roese return -EBADMSG; 169c40c7a99SStefan Roese 170c40c7a99SStefan Roese default: 171c40c7a99SStefan Roese break; 172c40c7a99SStefan Roese } 173c40c7a99SStefan Roese 174c40c7a99SStefan Roese return -EINVAL; 175c40c7a99SStefan Roese } 176c40c7a99SStefan Roese 177cfd93d7cSJeff Kletsky static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, 178cfd93d7cSJeff Kletsky u8 status) 179cfd93d7cSJeff Kletsky { 180cfd93d7cSJeff Kletsky switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { 181cfd93d7cSJeff Kletsky case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: 182cfd93d7cSJeff Kletsky return 0; 183c93c6132SChuanhong Guo 184cfd93d7cSJeff Kletsky case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: 185cfd93d7cSJeff Kletsky return 3; 186cfd93d7cSJeff Kletsky 187cfd93d7cSJeff Kletsky case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: 188cfd93d7cSJeff Kletsky return -EBADMSG; 189cfd93d7cSJeff Kletsky 190cfd93d7cSJeff Kletsky default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ 191cfd93d7cSJeff Kletsky return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; 192cfd93d7cSJeff Kletsky } 193cfd93d7cSJeff Kletsky 194cfd93d7cSJeff Kletsky return -EINVAL; 195cfd93d7cSJeff Kletsky } 196c40c7a99SStefan Roese 197c93c6132SChuanhong Guo static const struct spinand_info gigadevice_spinand_table[] = { 198f1541773SChuanhong Guo SPINAND_INFO("GD5F1GQ4xA", 199f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), 200377e517bSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 201c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 202c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 203c93c6132SChuanhong Guo &write_cache_variants, 204c93c6132SChuanhong Guo &update_cache_variants), 205c93c6132SChuanhong Guo 0, 206c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 207c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 208f1541773SChuanhong Guo SPINAND_INFO("GD5F2GQ4xA", 209f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), 210377e517bSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), 211c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 212c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 213c93c6132SChuanhong Guo &write_cache_variants, 214c93c6132SChuanhong Guo &update_cache_variants), 215c93c6132SChuanhong Guo 0, 216c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 217c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 218f1541773SChuanhong Guo SPINAND_INFO("GD5F4GQ4xA", 219f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), 220a126483eSFrieder Schrempf NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), 221c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 222c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 223c93c6132SChuanhong Guo &write_cache_variants, 224c93c6132SChuanhong Guo &update_cache_variants), 225c93c6132SChuanhong Guo 0, 226c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 227c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 228f1541773SChuanhong Guo SPINAND_INFO("GD5F1GQ4UExxG", 229f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), 230377e517bSBoris Brezillon NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 231c40c7a99SStefan Roese NAND_ECCREQ(8, 512), 232c40c7a99SStefan Roese SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 233c40c7a99SStefan Roese &write_cache_variants, 234c40c7a99SStefan Roese &update_cache_variants), 235c40c7a99SStefan Roese 0, 236cfd93d7cSJeff Kletsky SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, 237c40c7a99SStefan Roese gd5fxgq4uexxg_ecc_get_status)), 238f1541773SChuanhong Guo SPINAND_INFO("GD5F1GQ4UFxxG", 239f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), 240cfd93d7cSJeff Kletsky NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 241cfd93d7cSJeff Kletsky NAND_ECCREQ(8, 512), 242cfd93d7cSJeff Kletsky SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, 243cfd93d7cSJeff Kletsky &write_cache_variants, 244cfd93d7cSJeff Kletsky &update_cache_variants), 245cfd93d7cSJeff Kletsky 0, 246cfd93d7cSJeff Kletsky SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, 247cfd93d7cSJeff Kletsky gd5fxgq4ufxxg_ecc_get_status)), 248c93c6132SChuanhong Guo }; 249c93c6132SChuanhong Guo 250c93c6132SChuanhong Guo static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { 251c93c6132SChuanhong Guo }; 252c93c6132SChuanhong Guo 253c93c6132SChuanhong Guo const struct spinand_manufacturer gigadevice_spinand_manufacturer = { 254c93c6132SChuanhong Guo .id = SPINAND_MFR_GIGADEVICE, 255c93c6132SChuanhong Guo .name = "GigaDevice", 256f1541773SChuanhong Guo .chips = gigadevice_spinand_table, 257f1541773SChuanhong Guo .nchips = ARRAY_SIZE(gigadevice_spinand_table), 258c93c6132SChuanhong Guo .ops = &gigadevice_spinand_manuf_ops, 259c93c6132SChuanhong Guo }; 260