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