1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2020 Rockchip Electronics Co., Ltd 4 * 5 * Authors: 6 * Dingqiang Lin <jon.lin@rock-chips.com> 7 */ 8 9 #ifndef __UBOOT__ 10 #include <linux/device.h> 11 #include <linux/kernel.h> 12 #endif 13 #include <linux/mtd/spinand.h> 14 15 #define SPINAND_MFR_XTX 0x0B 16 17 static SPINAND_OP_VARIANTS(read_cache_variants, 18 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 19 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 20 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 21 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 22 23 static SPINAND_OP_VARIANTS(write_cache_variants, 24 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 25 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 26 27 static SPINAND_OP_VARIANTS(update_cache_variants, 28 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 29 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 30 31 static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, 32 struct mtd_oob_region *region) 33 { 34 if (section) 35 return -ERANGE; 36 37 region->offset = 48; 38 region->length = 16; 39 40 return 0; 41 } 42 43 static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, 44 struct mtd_oob_region *region) 45 { 46 if (section) 47 return -ERANGE; 48 49 region->offset = 2; 50 region->length = mtd->oobsize - 18; 51 52 return 0; 53 } 54 55 static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { 56 .ecc = xt26g0xa_ooblayout_ecc, 57 .rfree = xt26g0xa_ooblayout_free, 58 }; 59 60 static int xt26g01b_ooblayout_ecc(struct mtd_info *mtd, int section, 61 struct mtd_oob_region *region) 62 { 63 return -ERANGE; 64 } 65 66 static int xt26g01b_ooblayout_free(struct mtd_info *mtd, int section, 67 struct mtd_oob_region *region) 68 { 69 if (section) 70 return -ERANGE; 71 72 region->offset = 2; 73 region->length = mtd->oobsize - 2; 74 75 return 0; 76 } 77 78 static const struct mtd_ooblayout_ops xt26g01b_ooblayout = { 79 .ecc = xt26g01b_ooblayout_ecc, 80 .rfree = xt26g01b_ooblayout_free, 81 }; 82 83 static int xt26g02b_ooblayout_ecc(struct mtd_info *mtd, int section, 84 struct mtd_oob_region *region) 85 { 86 if (section > 3) 87 return -ERANGE; 88 89 region->offset = (16 * section) + 8; 90 region->length = 8; 91 92 return 0; 93 } 94 95 static int xt26g02b_ooblayout_free(struct mtd_info *mtd, int section, 96 struct mtd_oob_region *region) 97 { 98 if (section > 3) 99 return -ERANGE; 100 101 region->offset = (16 * section) + 2; 102 region->length = 6; 103 104 return 0; 105 } 106 107 static const struct mtd_ooblayout_ops xt26g02b_ooblayout = { 108 .ecc = xt26g02b_ooblayout_ecc, 109 .rfree = xt26g02b_ooblayout_free, 110 }; 111 112 /* 113 * ecc bits: 0xC0[2,5] 114 * [0x0000], No bit errors were detected; 115 * [0x0001, 0x0111], Bit errors were detected and corrected. Not 116 * reach Flipping Bits; 117 * [0x1000], Multiple bit errors were detected and 118 * not corrected. 119 * [0x1100], Bit error count equals the bit flip 120 * detectionthreshold 121 * else, reserved 122 */ 123 static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, 124 u8 status) 125 { 126 u8 eccsr = (status & GENMASK(5, 2)) >> 2; 127 128 if (eccsr <= 7) 129 return eccsr; 130 else if (eccsr == 12) 131 return 8; 132 else 133 return -EBADMSG; 134 } 135 136 /* 137 * ecc bits: 0xC0[4,6] 138 * [0x0], No bit errors were detected; 139 * [0x001, 0x011], Bit errors were detected and corrected. Not 140 * reach Flipping Bits; 141 * [0x100], Bit error count equals the bit flip 142 * detectionthreshold 143 * [0x101, 0x110], Reserved; 144 * [0x111], Multiple bit errors were detected and 145 * not corrected. 146 */ 147 static int xt26g02b_ecc_get_status(struct spinand_device *spinand, 148 u8 status) 149 { 150 u8 eccsr = (status & GENMASK(6, 4)) >> 4; 151 152 if (eccsr <= 4) 153 return eccsr; 154 else 155 return -EBADMSG; 156 } 157 158 static const struct spinand_info xtx_spinand_table[] = { 159 SPINAND_INFO("XT26G01A", 0xE1, 160 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 161 NAND_ECCREQ(8, 512), 162 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 163 &write_cache_variants, 164 &update_cache_variants), 165 SPINAND_HAS_QE_BIT, 166 SPINAND_ECCINFO(&xt26g0xa_ooblayout, 167 xt26g0xa_ecc_get_status)), 168 SPINAND_INFO("XT26G02A", 0xE2, 169 NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), 170 NAND_ECCREQ(8, 512), 171 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 172 &write_cache_variants, 173 &update_cache_variants), 174 SPINAND_HAS_QE_BIT, 175 SPINAND_ECCINFO(&xt26g0xa_ooblayout, 176 xt26g0xa_ecc_get_status)), 177 SPINAND_INFO("XT26G04A", 0xE3, 178 NAND_MEMORG(1, 2048, 64, 128, 2048, 1, 1, 1), 179 NAND_ECCREQ(8, 512), 180 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 181 &write_cache_variants, 182 &update_cache_variants), 183 SPINAND_HAS_QE_BIT, 184 SPINAND_ECCINFO(&xt26g0xa_ooblayout, 185 xt26g0xa_ecc_get_status)), 186 SPINAND_INFO("XT26G01B", 0xF1, 187 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 188 NAND_ECCREQ(8, 512), 189 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 190 &write_cache_variants, 191 &update_cache_variants), 192 SPINAND_HAS_QE_BIT, 193 SPINAND_ECCINFO(&xt26g01b_ooblayout, 194 xt26g0xa_ecc_get_status)), 195 SPINAND_INFO("XT26G02B", 0xF2, 196 NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), 197 NAND_ECCREQ(4, 512), 198 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 199 &write_cache_variants, 200 &update_cache_variants), 201 SPINAND_HAS_QE_BIT, 202 SPINAND_ECCINFO(&xt26g02b_ooblayout, 203 xt26g02b_ecc_get_status)), 204 }; 205 206 static int xtx_spinand_detect(struct spinand_device *spinand) 207 { 208 u8 *id = spinand->id.data; 209 int ret; 210 211 /* 212 * XTX SPI NAND read ID needs a dummy byte, so the first byte in 213 * raw_id is garbage. 214 */ 215 if (id[1] != SPINAND_MFR_XTX) 216 return 0; 217 218 ret = spinand_match_and_init(spinand, xtx_spinand_table, 219 ARRAY_SIZE(xtx_spinand_table), 220 id[2]); 221 if (ret) 222 return ret; 223 224 return 1; 225 } 226 227 static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { 228 .detect = xtx_spinand_detect, 229 }; 230 231 const struct spinand_manufacturer xtx_spinand_manufacturer = { 232 .id = SPINAND_MFR_XTX, 233 .name = "xtx", 234 .ops = &xtx_spinand_manuf_ops, 235 }; 236