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, &mbf); 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 >>= 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 }; 173 174 static int winbond_spinand_init(struct spinand_device *spinand) 175 { 176 struct nand_device *nand = spinand_to_nand(spinand); 177 unsigned int i; 178 179 /* 180 * Make sure all dies are in buffer read mode and not continuous read 181 * mode. 182 */ 183 for (i = 0; i < nand->memorg.ntargets; i++) { 184 spinand_select_target(spinand, i); 185 spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, 186 WINBOND_CFG_BUF_READ); 187 } 188 189 return 0; 190 } 191 192 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { 193 .init = winbond_spinand_init, 194 }; 195 196 const struct spinand_manufacturer winbond_spinand_manufacturer = { 197 .id = SPINAND_MFR_WINBOND, 198 .name = "Winbond", 199 .chips = winbond_spinand_table, 200 .nchips = ARRAY_SIZE(winbond_spinand_table), 201 .ops = &winbond_spinand_manuf_ops, 202 }; 203