1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2018 exceet electronics GmbH 4 * Copyright (c) 2018 Kontron Electronics GmbH 5 * 6 * Author: Frieder Schrempf <frieder.schrempf@kontron.de> 7 */ 8 9 #ifndef __UBOOT__ 10 #include <malloc.h> 11 #include <linux/device.h> 12 #include <linux/kernel.h> 13 #endif 14 #include <linux/mtd/spinand.h> 15 16 #define SPINAND_MFR_TOSHIBA 0x98 17 #define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) 18 19 static SPINAND_OP_VARIANTS(read_cache_variants, 20 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 21 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 22 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 23 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 24 25 static SPINAND_OP_VARIANTS(write_cache_variants, 26 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 27 28 static SPINAND_OP_VARIANTS(update_cache_variants, 29 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 30 31 static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, 32 struct mtd_oob_region *region) 33 { 34 if (section > 0) 35 return -ERANGE; 36 37 region->offset = mtd->oobsize / 2; 38 region->length = mtd->oobsize / 2; 39 40 return 0; 41 } 42 43 static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, 44 struct mtd_oob_region *region) 45 { 46 if (section > 0) 47 return -ERANGE; 48 49 /* 2 bytes reserved for BBM */ 50 region->offset = 2; 51 region->length = (mtd->oobsize / 2) - 2; 52 53 return 0; 54 } 55 56 static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = { 57 .ecc = tc58cxgxsx_ooblayout_ecc, 58 .rfree = tc58cxgxsx_ooblayout_free, 59 }; 60 61 static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, 62 u8 status) 63 { 64 struct nand_device *nand = spinand_to_nand(spinand); 65 u8 mbf = 0; 66 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); 67 68 switch (status & STATUS_ECC_MASK) { 69 case STATUS_ECC_NO_BITFLIPS: 70 return 0; 71 72 case STATUS_ECC_UNCOR_ERROR: 73 return -EBADMSG; 74 75 case STATUS_ECC_HAS_BITFLIPS: 76 case TOSH_STATUS_ECC_HAS_BITFLIPS_T: 77 /* 78 * Let's try to retrieve the real maximum number of bitflips 79 * in order to avoid forcing the wear-leveling layer to move 80 * data around if it's not necessary. 81 */ 82 if (spi_mem_exec_op(spinand->slave, &op)) 83 return nand->eccreq.strength; 84 85 mbf >>= 4; 86 87 if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) 88 return nand->eccreq.strength; 89 90 return mbf; 91 92 default: 93 break; 94 } 95 96 return -EINVAL; 97 } 98 99 static const struct spinand_info toshiba_spinand_table[] = { 100 /* 3.3V 1Gb */ 101 SPINAND_INFO("TC58CVG0S3", 102 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), 103 NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), 104 NAND_ECCREQ(8, 512), 105 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 106 &write_cache_variants, 107 &update_cache_variants), 108 0, 109 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 110 tc58cxgxsx_ecc_get_status)), 111 /* 3.3V 2Gb */ 112 SPINAND_INFO("TC58CVG1S3", 113 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), 114 NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), 115 NAND_ECCREQ(8, 512), 116 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 117 &write_cache_variants, 118 &update_cache_variants), 119 0, 120 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 121 tc58cxgxsx_ecc_get_status)), 122 /* 3.3V 4Gb */ 123 SPINAND_INFO("TC58CVG2S0", 124 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), 125 NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), 126 NAND_ECCREQ(8, 512), 127 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 128 &write_cache_variants, 129 &update_cache_variants), 130 0, 131 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 132 tc58cxgxsx_ecc_get_status)), 133 /* 1.8V 1Gb */ 134 SPINAND_INFO("TC58CYG0S3", 135 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), 136 NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), 137 NAND_ECCREQ(8, 512), 138 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 139 &write_cache_variants, 140 &update_cache_variants), 141 0, 142 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 143 tc58cxgxsx_ecc_get_status)), 144 /* 1.8V 2Gb */ 145 SPINAND_INFO("TC58CYG1S3", 146 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), 147 NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), 148 NAND_ECCREQ(8, 512), 149 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 150 &write_cache_variants, 151 &update_cache_variants), 152 0, 153 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 154 tc58cxgxsx_ecc_get_status)), 155 /* 1.8V 4Gb */ 156 SPINAND_INFO("TC58CYG2S0", 157 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), 158 NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), 159 NAND_ECCREQ(8, 512), 160 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 161 &write_cache_variants, 162 &update_cache_variants), 163 0, 164 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 165 tc58cxgxsx_ecc_get_status)), 166 /* 3.3V 1Gb */ 167 SPINAND_INFO("TC58CVG0S3HRAIJ", 168 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), 169 NAND_MEMORG(1, 2048, 128, 64, 1024, 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 0, 175 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 176 tc58cxgxsx_ecc_get_status)), 177 /* 3.3V 2Gb */ 178 SPINAND_INFO("TC58CVG1S3HRAIJ", 179 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), 180 NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), 181 NAND_ECCREQ(8, 512), 182 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 183 &write_cache_variants, 184 &update_cache_variants), 185 0, 186 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 187 tc58cxgxsx_ecc_get_status)), 188 /* 3.3V 4Gb */ 189 SPINAND_INFO("TC58CVG2S0HRAIJ", 190 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), 191 NAND_MEMORG(1, 4096, 128, 64, 2048, 1, 1, 1), 192 NAND_ECCREQ(8, 512), 193 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 194 &write_cache_variants, 195 &update_cache_variants), 196 0, 197 SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, 198 tc58cxgxsx_ecc_get_status)), 199 }; 200 201 static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { 202 }; 203 204 const struct spinand_manufacturer toshiba_spinand_manufacturer = { 205 .id = SPINAND_MFR_TOSHIBA, 206 .name = "Toshiba", 207 .chips = toshiba_spinand_table, 208 .nchips = ARRAY_SIZE(toshiba_spinand_table), 209 .ops = &toshiba_spinand_manuf_ops, 210 }; 211