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