1 // SPDX-License-Identifier: GPL-2.0
7 #include <linux/device.h>
8 #include <linux/kernel.h>
9 #include <linux/mtd/spinand.h>
11 #define SPINAND_MFR_GIGADEVICE 0xC8
13 #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4)
14 #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4)
16 #define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4)
17 #define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4)
19 #define GD5FXGQXXEXXG_REG_STATUS2 0xf0
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)
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));
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));
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));
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));
50 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
51 struct mtd_oob_region *region)
56 region->offset = (16 * section) + 8;
62 static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
63 struct mtd_oob_region *region)
69 region->offset = 16 * section;
72 /* section 0 has one byte reserved for bad block mark */
79 static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
80 .ecc = gd5fxgq4xa_ooblayout_ecc,
81 .free = gd5fxgq4xa_ooblayout_free,
84 static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
87 switch (status & STATUS_ECC_MASK) {
88 case STATUS_ECC_NO_BITFLIPS:
91 case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
92 /* 1-7 bits are flipped. return the maximum. */
95 case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
98 case STATUS_ECC_UNCOR_ERROR:
108 static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
109 struct mtd_oob_region *region)
120 static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section,
121 struct mtd_oob_region *region)
126 /* Reserve 1 bytes for the BBM. */
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,
139 static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section,
140 struct mtd_oob_region *oobregion)
145 oobregion->offset = 128;
146 oobregion->length = 128;
151 static int gd5fxgq4xc_ooblayout_256_free(struct mtd_info *mtd, int section,
152 struct mtd_oob_region *oobregion)
157 oobregion->offset = 1;
158 oobregion->length = 127;
163 static const struct mtd_ooblayout_ops gd5fxgq4xc_oob_256_ops = {
164 .ecc = gd5fxgq4xc_ooblayout_256_ecc,
165 .free = gd5fxgq4xc_ooblayout_256_free,
168 static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
172 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2,
176 switch (status & STATUS_ECC_MASK) {
177 case STATUS_ECC_NO_BITFLIPS:
180 case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
182 * Read status2 register to determine a more fine grained
185 ret = spi_mem_exec_op(spinand->spimem, &op);
190 * 4 ... 7 bits are flipped (1..4 can't be detected, so
191 * report the maximum of 4 in this case
193 /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
194 return ((status & STATUS_ECC_MASK) >> 2) |
195 ((status2 & STATUS_ECC_MASK) >> 4);
197 case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
200 case STATUS_ECC_UNCOR_ERROR:
210 static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand,
214 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2,
218 switch (status & STATUS_ECC_MASK) {
219 case STATUS_ECC_NO_BITFLIPS:
222 case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS:
224 * Read status2 register to determine a more fine grained
227 ret = spi_mem_exec_op(spinand->spimem, &op);
232 * 1 ... 4 bits are flipped (and corrected)
234 /* bits sorted this way (1...0): ECCSE1, ECCSE0 */
235 return ((status2 & STATUS_ECC_MASK) >> 4) + 1;
237 case STATUS_ECC_UNCOR_ERROR:
247 static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
250 switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
251 case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
254 case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
257 case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
260 default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
261 return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
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),
272 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
273 &write_cache_variants,
274 &update_cache_variants),
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),
282 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
283 &write_cache_variants,
284 &update_cache_variants),
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),
292 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
293 &write_cache_variants,
294 &update_cache_variants),
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),
302 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
303 &write_cache_variants,
304 &update_cache_variants),
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),
312 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
313 &write_cache_variants,
314 &update_cache_variants),
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),
322 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
323 &write_cache_variants,
324 &update_cache_variants),
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),
332 SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
333 &write_cache_variants,
334 &update_cache_variants),
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),
342 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
343 &write_cache_variants,
344 &update_cache_variants),
346 SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
347 gd5fxgq5xexxg_ecc_get_status)),
350 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
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,