1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Author: 4 * Chuanhong Guo <gch981213@gmail.com> 5 */ 6 7 #include <linux/device.h> 8 #include <linux/kernel.h> 9 #include <linux/mtd/spinand.h> 10 11 #define SPINAND_MFR_GIGADEVICE 0xC8 12 13 #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) 14 #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) 15 16 #define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4) 17 #define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4) 18 19 #define GD5FXGQXXEXXG_REG_STATUS2 0xf0 20 21 #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) 22 #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) 23 #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) 24 #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) 25 26 static SPINAND_OP_VARIANTS(read_cache_variants, 27 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), 28 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 29 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 30 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 31 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 32 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 33 34 static SPINAND_OP_VARIANTS(read_cache_variants_f, 35 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), 36 SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), 37 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 38 SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), 39 SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), 40 SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); 41 42 static SPINAND_OP_VARIANTS(write_cache_variants, 43 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 44 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 45 46 static SPINAND_OP_VARIANTS(update_cache_variants, 47 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 48 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 49 50 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, 51 struct mtd_oob_region *region) 52 { 53 if (section > 3) 54 return -ERANGE; 55 56 region->offset = (16 * section) + 8; 57 region->length = 8; 58 59 return 0; 60 } 61 62 static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, 63 struct mtd_oob_region *region) 64 { 65 if (section > 3) 66 return -ERANGE; 67 68 if (section) { 69 region->offset = 16 * section; 70 region->length = 8; 71 } else { 72 /* section 0 has one byte reserved for bad block mark */ 73 region->offset = 1; 74 region->length = 7; 75 } 76 return 0; 77 } 78 79 static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { 80 .ecc = gd5fxgq4xa_ooblayout_ecc, 81 .free = gd5fxgq4xa_ooblayout_free, 82 }; 83 84 static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, 85 u8 status) 86 { 87 switch (status & STATUS_ECC_MASK) { 88 case STATUS_ECC_NO_BITFLIPS: 89 return 0; 90 91 case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 92 /* 1-7 bits are flipped. return the maximum. */ 93 return 7; 94 95 case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 96 return 8; 97 98 case STATUS_ECC_UNCOR_ERROR: 99 return -EBADMSG; 100 101 default: 102 break; 103 } 104 105 return -EINVAL; 106 } 107 108 static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, 109 struct mtd_oob_region *region) 110 { 111 if (section) 112 return -ERANGE; 113 114 region->offset = 64; 115 region->length = 64; 116 117 return 0; 118 } 119 120 static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, 121 struct mtd_oob_region *region) 122 { 123 if (section) 124 return -ERANGE; 125 126 /* Reserve 1 bytes for the BBM. */ 127 region->offset = 1; 128 region->length = 63; 129 130 return 0; 131 } 132 133 /* Valid for Q4/Q5 and Q6 (untested) devices */ 134 static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { 135 .ecc = gd5fxgqx_variant2_ooblayout_ecc, 136 .free = gd5fxgqx_variant2_ooblayout_free, 137 }; 138 139 static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, 140 struct mtd_oob_region *oobregion) 141 { 142 if (section) 143 return -ERANGE; 144 145 oobregion->offset = 128; 146 oobregion->length = 128; 147 148 return 0; 149 } 150 151 static int gd5fxgq4xc_ooblayout_256_free(struct mtd_info *mtd, int section, 152 struct mtd_oob_region *oobregion) 153 { 154 if (section) 155 return -ERANGE; 156 157 oobregion->offset = 1; 158 oobregion->length = 127; 159 160 return 0; 161 } 162 163 static const struct mtd_ooblayout_ops gd5fxgq4xc_oob_256_ops = { 164 .ecc = gd5fxgq4xc_ooblayout_256_ecc, 165 .free = gd5fxgq4xc_ooblayout_256_free, 166 }; 167 168 static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, 169 u8 status) 170 { 171 u8 status2; 172 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, 173 &status2); 174 int ret; 175 176 switch (status & STATUS_ECC_MASK) { 177 case STATUS_ECC_NO_BITFLIPS: 178 return 0; 179 180 case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: 181 /* 182 * Read status2 register to determine a more fine grained 183 * bit error status 184 */ 185 ret = spi_mem_exec_op(spinand->spimem, &op); 186 if (ret) 187 return ret; 188 189 /* 190 * 4 ... 7 bits are flipped (1..4 can't be detected, so 191 * report the maximum of 4 in this case 192 */ 193 /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */ 194 return ((status & STATUS_ECC_MASK) >> 2) | 195 ((status2 & STATUS_ECC_MASK) >> 4); 196 197 case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: 198 return 8; 199 200 case STATUS_ECC_UNCOR_ERROR: 201 return -EBADMSG; 202 203 default: 204 break; 205 } 206 207 return -EINVAL; 208 } 209 210 static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, 211 u8 status) 212 { 213 u8 status2; 214 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, 215 &status2); 216 int ret; 217 218 switch (status & STATUS_ECC_MASK) { 219 case STATUS_ECC_NO_BITFLIPS: 220 return 0; 221 222 case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS: 223 /* 224 * Read status2 register to determine a more fine grained 225 * bit error status 226 */ 227 ret = spi_mem_exec_op(spinand->spimem, &op); 228 if (ret) 229 return ret; 230 231 /* 232 * 1 ... 4 bits are flipped (and corrected) 233 */ 234 /* bits sorted this way (1...0): ECCSE1, ECCSE0 */ 235 return ((status2 & STATUS_ECC_MASK) >> 4) + 1; 236 237 case STATUS_ECC_UNCOR_ERROR: 238 return -EBADMSG; 239 240 default: 241 break; 242 } 243 244 return -EINVAL; 245 } 246 247 static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, 248 u8 status) 249 { 250 switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { 251 case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: 252 return 0; 253 254 case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: 255 return 3; 256 257 case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: 258 return -EBADMSG; 259 260 default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ 261 return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; 262 } 263 264 return -EINVAL; 265 } 266 267 static const struct spinand_info gigadevice_spinand_table[] = { 268 SPINAND_INFO("GD5F1GQ4xA", 269 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), 270 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 271 NAND_ECCREQ(8, 512), 272 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 273 &write_cache_variants, 274 &update_cache_variants), 275 SPINAND_HAS_QE_BIT, 276 SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 277 gd5fxgq4xa_ecc_get_status)), 278 SPINAND_INFO("GD5F2GQ4xA", 279 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), 280 NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), 281 NAND_ECCREQ(8, 512), 282 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 283 &write_cache_variants, 284 &update_cache_variants), 285 SPINAND_HAS_QE_BIT, 286 SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 287 gd5fxgq4xa_ecc_get_status)), 288 SPINAND_INFO("GD5F4GQ4xA", 289 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), 290 NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), 291 NAND_ECCREQ(8, 512), 292 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 293 &write_cache_variants, 294 &update_cache_variants), 295 SPINAND_HAS_QE_BIT, 296 SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, 297 gd5fxgq4xa_ecc_get_status)), 298 SPINAND_INFO("GD5F4GQ4RC", 299 SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xa4, 0x68), 300 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), 301 NAND_ECCREQ(8, 512), 302 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, 303 &write_cache_variants, 304 &update_cache_variants), 305 SPINAND_HAS_QE_BIT, 306 SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, 307 gd5fxgq4ufxxg_ecc_get_status)), 308 SPINAND_INFO("GD5F4GQ4UC", 309 SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb4, 0x68), 310 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), 311 NAND_ECCREQ(8, 512), 312 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, 313 &write_cache_variants, 314 &update_cache_variants), 315 SPINAND_HAS_QE_BIT, 316 SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, 317 gd5fxgq4ufxxg_ecc_get_status)), 318 SPINAND_INFO("GD5F1GQ4UExxG", 319 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), 320 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 321 NAND_ECCREQ(8, 512), 322 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 323 &write_cache_variants, 324 &update_cache_variants), 325 SPINAND_HAS_QE_BIT, 326 SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, 327 gd5fxgq4uexxg_ecc_get_status)), 328 SPINAND_INFO("GD5F1GQ4UFxxG", 329 SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), 330 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 331 NAND_ECCREQ(8, 512), 332 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, 333 &write_cache_variants, 334 &update_cache_variants), 335 SPINAND_HAS_QE_BIT, 336 SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, 337 gd5fxgq4ufxxg_ecc_get_status)), 338 SPINAND_INFO("GD5F1GQ5UExxG", 339 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), 340 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 341 NAND_ECCREQ(4, 512), 342 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 343 &write_cache_variants, 344 &update_cache_variants), 345 SPINAND_HAS_QE_BIT, 346 SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, 347 gd5fxgq5xexxg_ecc_get_status)), 348 }; 349 350 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { 351 }; 352 353 const struct spinand_manufacturer gigadevice_spinand_manufacturer = { 354 .id = SPINAND_MFR_GIGADEVICE, 355 .name = "GigaDevice", 356 .chips = gigadevice_spinand_table, 357 .nchips = ARRAY_SIZE(gigadevice_spinand_table), 358 .ops = &gigadevice_spinand_manuf_ops, 359 }; 360