1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Authors: 4 * Andrey Zolotarev <andrey.zolotarev@keenetic.com> - the main driver logic 5 * Aleksei Sviridkin <f@lex.la> - adaptation to the mainline Linux kernel 6 * 7 * Based on: 8 * https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7 9 */ 10 11 #include <linux/device.h> 12 #include <linux/kernel.h> 13 #include <linux/mtd/spinand.h> 14 15 #define SPINAND_MFR_HEYANGTEK 0xc9 16 17 #define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS (3 << 4) 18 19 static SPINAND_OP_VARIANTS(read_cache_variants, 20 SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), 21 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), 22 SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), 23 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), 24 SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), 25 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); 26 27 static SPINAND_OP_VARIANTS(write_cache_variants, 28 SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), 29 SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); 30 31 static SPINAND_OP_VARIANTS(update_cache_variants, 32 SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), 33 SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); 34 35 /* 36 * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken 37 * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each 38 * 16-byte section, the bad block marker sits in byte 0 and the remaining 39 * bytes are exposed as free. 40 */ 41 static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section, 42 struct mtd_oob_region *region) 43 { 44 if (section > 3) 45 return -ERANGE; 46 47 region->offset = (16 * section) + 8; 48 region->length = 8; 49 50 return 0; 51 } 52 53 static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section, 54 struct mtd_oob_region *region) 55 { 56 if (section > 3) 57 return -ERANGE; 58 59 if (section) { 60 region->offset = 16 * section; 61 region->length = 8; 62 } else { 63 /* section 0 has one byte reserved for the bad block marker */ 64 region->offset = 1; 65 region->length = 7; 66 } 67 68 return 0; 69 } 70 71 static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = { 72 .ecc = hyf1gq4_ooblayout_ecc, 73 .free = hyf1gq4_ooblayout_free, 74 }; 75 76 static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status) 77 { 78 struct nand_device *nand = spinand_to_nand(spinand); 79 80 switch (status & STATUS_ECC_MASK) { 81 case STATUS_ECC_NO_BITFLIPS: 82 return 0; 83 84 case STATUS_ECC_UNCOR_ERROR: 85 return -EBADMSG; 86 87 case STATUS_ECC_HAS_BITFLIPS: 88 /* 89 * The die exposes only a coarse 2-bit ECC status and has no 90 * register for the exact bitflip count. This code means 91 * "corrected, below the refresh threshold", so report half of 92 * the ECC strength as a representative value. 93 */ 94 return nanddev_get_ecc_conf(nand)->strength / 2; 95 96 case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS: 97 /* 98 * "Corrected, refresh recommended": report the full ECC 99 * strength so the upper layers relocate the data. 100 */ 101 return nanddev_get_ecc_conf(nand)->strength; 102 103 default: 104 break; 105 } 106 107 return -EINVAL; 108 } 109 110 static const struct spinand_info heyangtek_spinand_table[] = { 111 SPINAND_INFO("HYF1GQ4UDACAE", 112 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21), 113 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 114 NAND_ECCREQ(4, 512), 115 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 116 &write_cache_variants, 117 &update_cache_variants), 118 SPINAND_HAS_QE_BIT, 119 SPINAND_ECCINFO(&hyf1gq4_ooblayout, 120 hyf1gq4_ecc_get_status)), 121 }; 122 123 static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = { 124 }; 125 126 const struct spinand_manufacturer heyangtek_spinand_manufacturer = { 127 .id = SPINAND_MFR_HEYANGTEK, 128 .name = "HeYangTek", 129 .chips = heyangtek_spinand_table, 130 .nchips = ARRAY_SIZE(heyangtek_spinand_table), 131 .ops = &heyangtek_spinand_manuf_ops, 132 }; 133