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 12c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) 13c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) 14c93c6132SChuanhong Guo 15*c40c7a99SStefan Roese #define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 16*c40c7a99SStefan Roese 17c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(read_cache_variants, 18c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 19c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 20c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 21c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 22c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 23c93c6132SChuanhong Guo SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 24c93c6132SChuanhong Guo 25c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(write_cache_variants, 26c93c6132SChuanhong Guo SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 27c93c6132SChuanhong Guo SPINAND_PROG_LOAD(true, 0, NULL, 0)); 28c93c6132SChuanhong Guo 29c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(update_cache_variants, 30c93c6132SChuanhong Guo SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 31c93c6132SChuanhong Guo SPINAND_PROG_LOAD(false, 0, NULL, 0)); 32c93c6132SChuanhong Guo 33c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, 34c93c6132SChuanhong Guo struct mtd_oob_region *region) 35c93c6132SChuanhong Guo { 36c93c6132SChuanhong Guo if (section > 3) 37c93c6132SChuanhong Guo return -ERANGE; 38c93c6132SChuanhong Guo 39c93c6132SChuanhong Guo region->offset = (16 * section) + 8; 40c93c6132SChuanhong Guo region->length = 8; 41c93c6132SChuanhong Guo 42c93c6132SChuanhong Guo return 0; 43c93c6132SChuanhong Guo } 44c93c6132SChuanhong Guo 45c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, 46c93c6132SChuanhong Guo struct mtd_oob_region *region) 47c93c6132SChuanhong Guo { 48c93c6132SChuanhong Guo if (section > 3) 49c93c6132SChuanhong Guo return -ERANGE; 50c93c6132SChuanhong Guo 51c93c6132SChuanhong Guo if (section) { 52c93c6132SChuanhong Guo region->offset = 16 * section; 53c93c6132SChuanhong Guo region->length = 8; 54c93c6132SChuanhong Guo } else { 55c93c6132SChuanhong Guo /* section 0 has one byte reserved for bad block mark */ 56c93c6132SChuanhong Guo region->offset = 1; 57c93c6132SChuanhong Guo region->length = 7; 58c93c6132SChuanhong Guo } 59c93c6132SChuanhong Guo return 0; 60c93c6132SChuanhong Guo } 61c93c6132SChuanhong Guo 62c93c6132SChuanhong Guo static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, 63c93c6132SChuanhong Guo u8 status) 64c93c6132SChuanhong Guo { 65c93c6132SChuanhong Guo switch (status & STATUS_ECC_MASK) { 66c93c6132SChuanhong Guo case STATUS_ECC_NO_BITFLIPS: 67c93c6132SChuanhong Guo return 0; 68c93c6132SChuanhong Guo 69c93c6132SChuanhong Guo case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 70c93c6132SChuanhong Guo /* 1-7 bits are flipped. return the maximum. */ 71c93c6132SChuanhong Guo return 7; 72c93c6132SChuanhong Guo 73c93c6132SChuanhong Guo case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 74c93c6132SChuanhong Guo return 8; 75c93c6132SChuanhong Guo 76c93c6132SChuanhong Guo case STATUS_ECC_UNCOR_ERROR: 77c93c6132SChuanhong Guo return -EBADMSG; 78c93c6132SChuanhong Guo 79c93c6132SChuanhong Guo default: 80c93c6132SChuanhong Guo break; 81c93c6132SChuanhong Guo } 82c93c6132SChuanhong Guo 83c93c6132SChuanhong Guo return -EINVAL; 84c93c6132SChuanhong Guo } 85c93c6132SChuanhong Guo 86*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, 87*c40c7a99SStefan Roese struct mtd_oob_region *region) 88*c40c7a99SStefan Roese { 89*c40c7a99SStefan Roese if (section) 90*c40c7a99SStefan Roese return -ERANGE; 91*c40c7a99SStefan Roese 92*c40c7a99SStefan Roese region->offset = 64; 93*c40c7a99SStefan Roese region->length = 64; 94*c40c7a99SStefan Roese 95*c40c7a99SStefan Roese return 0; 96*c40c7a99SStefan Roese } 97*c40c7a99SStefan Roese 98*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, 99*c40c7a99SStefan Roese struct mtd_oob_region *region) 100*c40c7a99SStefan Roese { 101*c40c7a99SStefan Roese if (section) 102*c40c7a99SStefan Roese return -ERANGE; 103*c40c7a99SStefan Roese 104*c40c7a99SStefan Roese /* Reserve 1 bytes for the BBM. */ 105*c40c7a99SStefan Roese region->offset = 1; 106*c40c7a99SStefan Roese region->length = 63; 107*c40c7a99SStefan Roese 108*c40c7a99SStefan Roese return 0; 109*c40c7a99SStefan Roese } 110*c40c7a99SStefan Roese 111*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, 112*c40c7a99SStefan Roese u8 status) 113*c40c7a99SStefan Roese { 114*c40c7a99SStefan Roese u8 status2; 115*c40c7a99SStefan Roese struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, 116*c40c7a99SStefan Roese &status2); 117*c40c7a99SStefan Roese int ret; 118*c40c7a99SStefan Roese 119*c40c7a99SStefan Roese switch (status & STATUS_ECC_MASK) { 120*c40c7a99SStefan Roese case STATUS_ECC_NO_BITFLIPS: 121*c40c7a99SStefan Roese return 0; 122*c40c7a99SStefan Roese 123*c40c7a99SStefan Roese case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 124*c40c7a99SStefan Roese /* 125*c40c7a99SStefan Roese * Read status2 register to determine a more fine grained 126*c40c7a99SStefan Roese * bit error status 127*c40c7a99SStefan Roese */ 128*c40c7a99SStefan Roese ret = spi_mem_exec_op(spinand->spimem, &op); 129*c40c7a99SStefan Roese if (ret) 130*c40c7a99SStefan Roese return ret; 131*c40c7a99SStefan Roese 132*c40c7a99SStefan Roese /* 133*c40c7a99SStefan Roese * 4 ... 7 bits are flipped (1..4 can't be detected, so 134*c40c7a99SStefan Roese * report the maximum of 4 in this case 135*c40c7a99SStefan Roese */ 136*c40c7a99SStefan Roese /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */ 137*c40c7a99SStefan Roese return ((status & STATUS_ECC_MASK) >> 2) | 138*c40c7a99SStefan Roese ((status2 & STATUS_ECC_MASK) >> 4); 139*c40c7a99SStefan Roese 140*c40c7a99SStefan Roese case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 141*c40c7a99SStefan Roese return 8; 142*c40c7a99SStefan Roese 143*c40c7a99SStefan Roese case STATUS_ECC_UNCOR_ERROR: 144*c40c7a99SStefan Roese return -EBADMSG; 145*c40c7a99SStefan Roese 146*c40c7a99SStefan Roese default: 147*c40c7a99SStefan Roese break; 148*c40c7a99SStefan Roese } 149*c40c7a99SStefan Roese 150*c40c7a99SStefan Roese return -EINVAL; 151*c40c7a99SStefan Roese } 152*c40c7a99SStefan Roese 153c93c6132SChuanhong Guo static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { 154c93c6132SChuanhong Guo .ecc = gd5fxgq4xa_ooblayout_ecc, 155c93c6132SChuanhong Guo .free = gd5fxgq4xa_ooblayout_free, 156c93c6132SChuanhong Guo }; 157c93c6132SChuanhong Guo 158*c40c7a99SStefan Roese static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = { 159*c40c7a99SStefan Roese .ecc = gd5fxgq4uexxg_ooblayout_ecc, 160*c40c7a99SStefan Roese .free = gd5fxgq4uexxg_ooblayout_free, 161*c40c7a99SStefan Roese }; 162*c40c7a99SStefan Roese 163c93c6132SChuanhong Guo static const struct spinand_info gigadevice_spinand_table[] = { 164c93c6132SChuanhong Guo SPINAND_INFO("GD5F1GQ4xA", 0xF1, 165c93c6132SChuanhong Guo NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 166c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 167c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 168c93c6132SChuanhong Guo &write_cache_variants, 169c93c6132SChuanhong Guo &update_cache_variants), 170c93c6132SChuanhong Guo 0, 171c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 172c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 173c93c6132SChuanhong Guo SPINAND_INFO("GD5F2GQ4xA", 0xF2, 174c93c6132SChuanhong Guo NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), 175c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 176c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 177c93c6132SChuanhong Guo &write_cache_variants, 178c93c6132SChuanhong Guo &update_cache_variants), 179c93c6132SChuanhong Guo 0, 180c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 181c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 182c93c6132SChuanhong Guo SPINAND_INFO("GD5F4GQ4xA", 0xF4, 183c93c6132SChuanhong Guo NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), 184c93c6132SChuanhong Guo NAND_ECCREQ(8, 512), 185c93c6132SChuanhong Guo SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 186c93c6132SChuanhong Guo &write_cache_variants, 187c93c6132SChuanhong Guo &update_cache_variants), 188c93c6132SChuanhong Guo 0, 189c93c6132SChuanhong Guo SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 190c93c6132SChuanhong Guo gd5fxgq4xa_ecc_get_status)), 191*c40c7a99SStefan Roese SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, 192*c40c7a99SStefan Roese NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), 193*c40c7a99SStefan Roese NAND_ECCREQ(8, 512), 194*c40c7a99SStefan Roese SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 195*c40c7a99SStefan Roese &write_cache_variants, 196*c40c7a99SStefan Roese &update_cache_variants), 197*c40c7a99SStefan Roese 0, 198*c40c7a99SStefan Roese SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout, 199*c40c7a99SStefan Roese gd5fxgq4uexxg_ecc_get_status)), 200c93c6132SChuanhong Guo }; 201c93c6132SChuanhong Guo 202c93c6132SChuanhong Guo static int gigadevice_spinand_detect(struct spinand_device *spinand) 203c93c6132SChuanhong Guo { 204c93c6132SChuanhong Guo u8 *id = spinand->id.data; 205c93c6132SChuanhong Guo int ret; 206c93c6132SChuanhong Guo 207c93c6132SChuanhong Guo /* 208c93c6132SChuanhong Guo * For GD NANDs, There is an address byte needed to shift in before IDs 209c93c6132SChuanhong Guo * are read out, so the first byte in raw_id is dummy. 210c93c6132SChuanhong Guo */ 211c93c6132SChuanhong Guo if (id[1] != SPINAND_MFR_GIGADEVICE) 212c93c6132SChuanhong Guo return 0; 213c93c6132SChuanhong Guo 214c93c6132SChuanhong Guo ret = spinand_match_and_init(spinand, gigadevice_spinand_table, 215c93c6132SChuanhong Guo ARRAY_SIZE(gigadevice_spinand_table), 216c93c6132SChuanhong Guo id[2]); 217c93c6132SChuanhong Guo if (ret) 218c93c6132SChuanhong Guo return ret; 219c93c6132SChuanhong Guo 220c93c6132SChuanhong Guo return 1; 221c93c6132SChuanhong Guo } 222c93c6132SChuanhong Guo 223c93c6132SChuanhong Guo static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { 224c93c6132SChuanhong Guo .detect = gigadevice_spinand_detect, 225c93c6132SChuanhong Guo }; 226c93c6132SChuanhong Guo 227c93c6132SChuanhong Guo const struct spinand_manufacturer gigadevice_spinand_manufacturer = { 228c93c6132SChuanhong Guo .id = SPINAND_MFR_GIGADEVICE, 229c93c6132SChuanhong Guo .name = "GigaDevice", 230c93c6132SChuanhong Guo .ops = &gigadevice_spinand_manuf_ops, 231c93c6132SChuanhong Guo }; 232