1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2017 exceet electronics GmbH 4 * 5 * Authors: 6 * Frieder Schrempf <frieder.schrempf@exceet.de> 7 * Boris Brezillon <boris.brezillon@bootlin.com> 8 */ 9 10 #ifndef __UBOOT__ 11 #include <linux/device.h> 12 #include <linux/kernel.h> 13 #endif 14 #include <linux/mtd/spinand.h> 15 16 #define SPINAND_MFR_WINBOND 0xEF 17 18 #define WINBOND_CFG_BUF_READ BIT(3) 19 #define WINBOND_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) 20 21 static SPINAND_OP_VARIANTS(read_cache_variants, 22 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 23 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 24 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 25 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 26 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 27 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 28 29 static SPINAND_OP_VARIANTS(write_cache_variants, 30 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 31 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 32 33 static SPINAND_OP_VARIANTS(update_cache_variants, 34 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 35 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 36 37 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, 38 struct mtd_oob_region *region) 39 { 40 if (section > 3) 41 return -ERANGE; 42 43 region->offset = (16 * section) + 8; 44 region->length = 8; 45 46 return 0; 47 } 48 49 static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, 50 struct mtd_oob_region *region) 51 { 52 if (section > 3) 53 return -ERANGE; 54 55 region->offset = (16 * section) + 2; 56 region->length = 6; 57 58 return 0; 59 } 60 61 static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { 62 .ecc = w25m02gv_ooblayout_ecc, 63 .rfree = w25m02gv_ooblayout_free, 64 }; 65 66 static int w25m02gv_select_target(struct spinand_device *spinand, 67 unsigned int target) 68 { 69 struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), 70 SPI_MEM_OP_NO_ADDR, 71 SPI_MEM_OP_NO_DUMMY, 72 SPI_MEM_OP_DATA_OUT(1, 73 spinand->scratchbuf, 74 1)); 75 76 *spinand->scratchbuf = target; 77 return spi_mem_exec_op(spinand->slave, &op); 78 } 79 80 static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, 81 struct mtd_oob_region *region) 82 { 83 if (section) 84 return -ERANGE; 85 86 region->offset = 64; 87 region->length = 64; 88 89 return 0; 90 } 91 92 static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, 93 struct mtd_oob_region *region) 94 { 95 if (section) 96 return -ERANGE; 97 98 /* Reserve 2 bytes for the BBM. */ 99 region->offset = 2; 100 region->length = 62; 101 102 return 0; 103 } 104 105 static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { 106 .ecc = w25n02kv_ooblayout_ecc, 107 .rfree = w25n02kv_ooblayout_free, 108 }; 109 110 static int w25n02kv_ecc_get_status(struct spinand_device *spinand, 111 u8 status) 112 { 113 struct nand_device *nand = spinand_to_nand(spinand); 114 u8 mbf = 0; 115 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); 116 117 switch (status & STATUS_ECC_MASK) { 118 case STATUS_ECC_NO_BITFLIPS: 119 return 0; 120 121 case STATUS_ECC_UNCOR_ERROR: 122 return -EBADMSG; 123 124 case STATUS_ECC_HAS_BITFLIPS: 125 case WINBOND_STATUS_ECC_HAS_BITFLIPS_T: 126 /* 127 * Let's try to retrieve the real maximum number of bitflips 128 * in order to avoid forcing the wear-leveling layer to move 129 * data around if it's not necessary. 130 */ 131 if (spi_mem_exec_op(spinand->slave, &op)) 132 return nand->eccreq.strength; 133 134 mbf >>= 4; 135 136 if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) 137 return nand->eccreq.strength; 138 139 return mbf; 140 141 default: 142 break; 143 } 144 145 return -EINVAL; 146 } 147 148 /* Another set for the same id[2] devices in one series */ 149 static const struct spinand_info winbond_spinand_table[] = { 150 SPINAND_INFO("W25M02GV", 151 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAB), 152 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), 153 NAND_ECCREQ(1, 512), 154 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 155 &write_cache_variants, 156 &update_cache_variants), 157 0, 158 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 159 SPINAND_SELECT_TARGET(w25m02gv_select_target)), 160 SPINAND_INFO("W25N512GV", 161 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x20), 162 NAND_MEMORG(1, 2048, 64, 64, 512, 1, 1, 1), 163 NAND_ECCREQ(1, 512), 164 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 165 &write_cache_variants, 166 &update_cache_variants), 167 0, 168 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 169 SPINAND_SELECT_TARGET(w25m02gv_select_target)), 170 SPINAND_INFO("W25N01GV", 171 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x21), 172 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 173 NAND_ECCREQ(1, 512), 174 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 175 &write_cache_variants, 176 &update_cache_variants), 177 0, 178 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 179 SPINAND_SELECT_TARGET(w25m02gv_select_target)), 180 SPINAND_INFO("W25N02KV", 181 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x22), 182 NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), 183 NAND_ECCREQ(8, 512), 184 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 185 &write_cache_variants, 186 &update_cache_variants), 187 0, 188 SPINAND_ECCINFO(&w25n02kv_ooblayout, 189 w25n02kv_ecc_get_status)), 190 SPINAND_INFO("W25N04KV", 191 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x23), 192 NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), 193 NAND_ECCREQ(8, 512), 194 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 195 &write_cache_variants, 196 &update_cache_variants), 197 0, 198 SPINAND_ECCINFO(&w25n02kv_ooblayout, 199 w25n02kv_ecc_get_status)), 200 SPINAND_INFO("W25N01GW", 201 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x21), 202 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 203 NAND_ECCREQ(1, 512), 204 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 205 &write_cache_variants, 206 &update_cache_variants), 207 0, 208 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 209 SPINAND_SELECT_TARGET(w25m02gv_select_target)), 210 SPINAND_INFO("W25N02KW", 211 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x22), 212 NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), 213 NAND_ECCREQ(8, 512), 214 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 215 &write_cache_variants, 216 &update_cache_variants), 217 0, 218 SPINAND_ECCINFO(&w25n02kv_ooblayout, 219 w25n02kv_ecc_get_status)), 220 }; 221 222 static int winbond_spinand_init(struct spinand_device *spinand) 223 { 224 struct nand_device *nand = spinand_to_nand(spinand); 225 unsigned int i; 226 227 /* 228 * Make sure all dies are in buffer read mode and not continuous read 229 * mode. 230 */ 231 for (i = 0; i < nand->memorg.ntargets; i++) { 232 spinand_select_target(spinand, i); 233 spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, 234 WINBOND_CFG_BUF_READ); 235 } 236 237 return 0; 238 } 239 240 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { 241 .init = winbond_spinand_init, 242 }; 243 244 const struct spinand_manufacturer winbond_spinand_manufacturer = { 245 .id = SPINAND_MFR_WINBOND, 246 .name = "Winbond", 247 .chips = winbond_spinand_table, 248 .nchips = ARRAY_SIZE(winbond_spinand_table), 249 .ops = &winbond_spinand_manuf_ops, 250 }; 251