1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Author: 4 * Chuanhong Guo <gch981213@gmail.com> - the main driver logic 5 * Martin Kurbanov <mmkurbanov@sberdevices.ru> - OOB layout 6 */ 7 8 #include <linux/device.h> 9 #include <linux/kernel.h> 10 #include <linux/mtd/spinand.h> 11 #include <linux/spi/spi-mem.h> 12 13 /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ 14 #define SPINAND_MFR_ESMT_C8 0xc8 15 #define SPINAND_MFR_ESMT_8C 0x8c 16 17 #define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7) 18 #define ESMT_F50L1G41LB_CFG_OTP_LOCK \ 19 (CFG_OTP_ENABLE | ESMT_F50L1G41LB_CFG_OTP_PROTECT) 20 21 static SPINAND_OP_VARIANTS(read_cache_variants, 22 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_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 * OOB spare area map (64 bytes) 37 * 38 * Bad Block Markers 39 * filled by HW and kernel Reserved 40 * | +-----------------------+-----------------------+ 41 * | | | | 42 * | | OOB free data Area |non ECC protected | 43 * | +-------------|-----+-----------------|-----+-----------------|-----+ 44 * | | | | | | | | 45 * +-|---|----------+--|-----|--------------+--|-----|--------------+--|-----|--------------+ 46 * | | | section0 | | | section1 | | | section2 | | | section3 | 47 * +-v-+-v-+---+----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+ 48 * | | | | | | | | | | | | | | | | | 49 * |0:1|2:3|4:7|8:15|16:17|18:19|20:23|24:31|32:33|34:35|36:39|40:47|48:49|50:51|52:55|56:63| 50 * | | | | | | | | | | | | | | | | | 51 * +---+---+-^-+--^-+-----+-----+--^--+--^--+-----+-----+--^--+--^--+-----+-----+--^--+--^--+ 52 * | | | | | | | | 53 * | +----------------|-----+-----------------|-----+-----------------|-----+ 54 * | ECC Area|(Main + Spare) - filled|by ESMT NAND HW | 55 * | | | | 56 * +---------------------+-----------------------+-----------------------+ 57 * OOB ECC protected Area - not used due to 58 * partial programming from some filesystems 59 * (like JFFS2 with cleanmarkers) 60 */ 61 62 #define ESMT_OOB_SECTION_COUNT 4 63 #define ESMT_OOB_SECTION_SIZE(nand) \ 64 (nanddev_per_page_oobsize(nand) / ESMT_OOB_SECTION_COUNT) 65 #define ESMT_OOB_FREE_SIZE(nand) \ 66 (ESMT_OOB_SECTION_SIZE(nand) / 2) 67 #define ESMT_OOB_ECC_SIZE(nand) \ 68 (ESMT_OOB_SECTION_SIZE(nand) - ESMT_OOB_FREE_SIZE(nand)) 69 #define ESMT_OOB_BBM_SIZE 2 70 71 static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section, 72 struct mtd_oob_region *region) 73 { 74 struct nand_device *nand = mtd_to_nanddev(mtd); 75 76 if (section >= ESMT_OOB_SECTION_COUNT) 77 return -ERANGE; 78 79 region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + 80 ESMT_OOB_FREE_SIZE(nand); 81 region->length = ESMT_OOB_ECC_SIZE(nand); 82 83 return 0; 84 } 85 86 static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section, 87 struct mtd_oob_region *region) 88 { 89 struct nand_device *nand = mtd_to_nanddev(mtd); 90 91 if (section >= ESMT_OOB_SECTION_COUNT) 92 return -ERANGE; 93 94 /* 95 * Reserve space for bad blocks markers (section0) and 96 * reserved bytes (sections 1-3) 97 */ 98 region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + 2; 99 100 /* Use only 2 non-protected ECC bytes per each OOB section */ 101 region->length = 2; 102 103 return 0; 104 } 105 106 static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { 107 .ecc = f50l1g41lb_ooblayout_ecc, 108 .free = f50l1g41lb_ooblayout_free, 109 }; 110 111 static int f50l1g41lb_otp_info(struct spinand_device *spinand, size_t len, 112 struct otp_info *buf, size_t *retlen, bool user) 113 { 114 if (len < sizeof(*buf)) 115 return -EINVAL; 116 117 buf->locked = 0; 118 buf->start = 0; 119 buf->length = user ? spinand_user_otp_size(spinand) : 120 spinand_fact_otp_size(spinand); 121 122 *retlen = sizeof(*buf); 123 return 0; 124 } 125 126 static int f50l1g41lb_fact_otp_info(struct spinand_device *spinand, size_t len, 127 struct otp_info *buf, size_t *retlen) 128 { 129 return f50l1g41lb_otp_info(spinand, len, buf, retlen, false); 130 } 131 132 static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len, 133 struct otp_info *buf, size_t *retlen) 134 { 135 return f50l1g41lb_otp_info(spinand, len, buf, retlen, true); 136 } 137 138 static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from, 139 size_t len) 140 { 141 struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true); 142 struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0); 143 u8 status; 144 int ret; 145 146 ret = spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, 147 ESMT_F50L1G41LB_CFG_OTP_LOCK); 148 if (!ret) 149 return ret; 150 151 ret = spi_mem_exec_op(spinand->spimem, &write_op); 152 if (!ret) 153 goto out; 154 155 ret = spi_mem_exec_op(spinand->spimem, &exec_op); 156 if (!ret) 157 goto out; 158 159 ret = spinand_wait(spinand, 160 SPINAND_WRITE_INITIAL_DELAY_US, 161 SPINAND_WRITE_POLL_DELAY_US, 162 &status); 163 if (!ret && (status & STATUS_PROG_FAILED)) 164 ret = -EIO; 165 166 out: 167 if (spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, 0)) { 168 dev_warn(&spinand_to_mtd(spinand)->dev, 169 "Can not disable OTP mode\n"); 170 ret = -EIO; 171 } 172 173 return ret; 174 } 175 176 static const struct spinand_user_otp_ops f50l1g41lb_user_otp_ops = { 177 .info = f50l1g41lb_user_otp_info, 178 .lock = f50l1g41lb_otp_lock, 179 .read = spinand_user_otp_read, 180 .write = spinand_user_otp_write, 181 }; 182 183 static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = { 184 .info = f50l1g41lb_fact_otp_info, 185 .read = spinand_fact_otp_read, 186 }; 187 188 189 static const struct spinand_info esmt_8c_spinand_table[] = { 190 SPINAND_INFO("F50L1G41LC", 191 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C), 192 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 193 NAND_ECCREQ(1, 512), 194 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 195 &write_cache_variants, 196 &update_cache_variants), 197 0, 198 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), 199 SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), 200 SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), 201 }; 202 203 static const struct spinand_info esmt_c8_spinand_table[] = { 204 SPINAND_INFO("F50L1G41LB", 205 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f, 206 0x7f, 0x7f), 207 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 208 NAND_ECCREQ(1, 512), 209 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 210 &write_cache_variants, 211 &update_cache_variants), 212 0, 213 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), 214 SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), 215 SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), 216 SPINAND_INFO("F50D1G41LB", 217 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f, 218 0x7f), 219 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 220 NAND_ECCREQ(1, 512), 221 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 222 &write_cache_variants, 223 &update_cache_variants), 224 0, 225 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), 226 SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), 227 SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), 228 SPINAND_INFO("F50D2G41KA", 229 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f, 230 0x7f, 0x7f), 231 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), 232 NAND_ECCREQ(8, 512), 233 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 234 &write_cache_variants, 235 &update_cache_variants), 236 0, 237 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), 238 }; 239 240 static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { 241 }; 242 243 const struct spinand_manufacturer esmt_8c_spinand_manufacturer = { 244 .id = SPINAND_MFR_ESMT_8C, 245 .name = "ESMT", 246 .chips = esmt_8c_spinand_table, 247 .nchips = ARRAY_SIZE(esmt_8c_spinand_table), 248 .ops = &esmt_spinand_manuf_ops, 249 }; 250 251 const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { 252 .id = SPINAND_MFR_ESMT_C8, 253 .name = "ESMT", 254 .chips = esmt_c8_spinand_table, 255 .nchips = ARRAY_SIZE(esmt_c8_spinand_table), 256 .ops = &esmt_spinand_manuf_ops, 257 }; 258