1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2005, Intec Automation Inc. 4 * Copyright (C) 2014, Freescale Semiconductor, Inc. 5 */ 6 7 #include <linux/mtd/spi-nor.h> 8 9 #include "core.h" 10 11 #define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ 12 #define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */ 13 14 #define WINBOND_NOR_WREAR_OP(buf) \ 15 SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \ 16 SPI_MEM_OP_NO_ADDR, \ 17 SPI_MEM_OP_NO_DUMMY, \ 18 SPI_MEM_OP_DATA_OUT(1, buf, 0)) 19 20 static int 21 w25q128_post_bfpt_fixups(struct spi_nor *nor, 22 const struct sfdp_parameter_header *bfpt_header, 23 const struct sfdp_bfpt *bfpt) 24 { 25 /* 26 * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded 27 * size is really wrong. It seems that they confused Mbit with MiB. 28 * Thus the flash is discovered as a 2MiB device. 29 */ 30 if (bfpt_header->major == SFDP_JESD216_MAJOR && 31 bfpt_header->minor == SFDP_JESD216_MINOR && 32 nor->params->size == SZ_2M && 33 nor->params->erase_map.regions[0].size == SZ_2M) { 34 nor->params->size = SZ_16M; 35 nor->params->erase_map.regions[0].size = SZ_16M; 36 } 37 38 return 0; 39 } 40 41 static const struct spi_nor_fixups w25q128_fixups = { 42 .post_bfpt = w25q128_post_bfpt_fixups, 43 }; 44 45 static int 46 w25q256_post_bfpt_fixups(struct spi_nor *nor, 47 const struct sfdp_parameter_header *bfpt_header, 48 const struct sfdp_bfpt *bfpt) 49 { 50 /* 51 * W25Q256JV supports 4B opcodes but W25Q256FV does not. 52 * Unfortunately, Winbond has re-used the same JEDEC ID for both 53 * variants which prevents us from defining a new entry in the parts 54 * table. 55 * To differentiate between W25Q256JV and W25Q256FV check SFDP header 56 * version: only JV has JESD216A compliant structure (version 5). 57 */ 58 if (bfpt_header->major == SFDP_JESD216_MAJOR && 59 bfpt_header->minor == SFDP_JESD216A_MINOR) 60 nor->flags |= SNOR_F_4B_OPCODES; 61 62 return 0; 63 } 64 65 static const struct spi_nor_fixups w25q256_fixups = { 66 .post_bfpt = w25q256_post_bfpt_fixups, 67 }; 68 69 static const struct flash_info winbond_nor_parts[] = { 70 { 71 .id = SNOR_ID(0xef, 0x30, 0x10), 72 .name = "w25x05", 73 .size = SZ_64K, 74 .no_sfdp_flags = SECT_4K, 75 }, { 76 .id = SNOR_ID(0xef, 0x30, 0x11), 77 .name = "w25x10", 78 .size = SZ_128K, 79 .no_sfdp_flags = SECT_4K, 80 }, { 81 .id = SNOR_ID(0xef, 0x30, 0x12), 82 .name = "w25x20", 83 .size = SZ_256K, 84 .no_sfdp_flags = SECT_4K, 85 }, { 86 .id = SNOR_ID(0xef, 0x30, 0x13), 87 .name = "w25x40", 88 .size = SZ_512K, 89 .no_sfdp_flags = SECT_4K, 90 }, { 91 .id = SNOR_ID(0xef, 0x30, 0x14), 92 .name = "w25x80", 93 .size = SZ_1M, 94 .no_sfdp_flags = SECT_4K, 95 }, { 96 .id = SNOR_ID(0xef, 0x30, 0x15), 97 .name = "w25x16", 98 .size = SZ_2M, 99 .no_sfdp_flags = SECT_4K, 100 }, { 101 .id = SNOR_ID(0xef, 0x30, 0x16), 102 .name = "w25x32", 103 .size = SZ_4M, 104 .no_sfdp_flags = SECT_4K, 105 }, { 106 .id = SNOR_ID(0xef, 0x30, 0x17), 107 .name = "w25x64", 108 .size = SZ_8M, 109 .no_sfdp_flags = SECT_4K, 110 }, { 111 .id = SNOR_ID(0xef, 0x40, 0x12), 112 .name = "w25q20cl", 113 .size = SZ_256K, 114 .no_sfdp_flags = SECT_4K, 115 }, { 116 .id = SNOR_ID(0xef, 0x40, 0x14), 117 .name = "w25q80bl", 118 .size = SZ_1M, 119 .no_sfdp_flags = SECT_4K, 120 }, { 121 .id = SNOR_ID(0xef, 0x40, 0x16), 122 .name = "w25q32", 123 .size = SZ_4M, 124 .no_sfdp_flags = SECT_4K, 125 }, { 126 .id = SNOR_ID(0xef, 0x40, 0x17), 127 .name = "w25q64", 128 .size = SZ_8M, 129 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 130 }, { 131 .id = SNOR_ID(0xef, 0x40, 0x18), 132 /* Flavors w/ and w/o SFDP. */ 133 .name = "w25q128", 134 .size = SZ_16M, 135 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 136 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 137 .fixups = &w25q128_fixups, 138 }, { 139 .id = SNOR_ID(0xef, 0x40, 0x19), 140 .name = "w25q256", 141 .size = SZ_32M, 142 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 143 .fixups = &w25q256_fixups, 144 }, { 145 .id = SNOR_ID(0xef, 0x40, 0x20), 146 .name = "w25q512jvq", 147 .size = SZ_64M, 148 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 149 }, { 150 .id = SNOR_ID(0xef, 0x50, 0x12), 151 .name = "w25q20bw", 152 .size = SZ_256K, 153 .no_sfdp_flags = SECT_4K, 154 }, { 155 .id = SNOR_ID(0xef, 0x50, 0x14), 156 .name = "w25q80", 157 .size = SZ_1M, 158 .no_sfdp_flags = SECT_4K, 159 }, { 160 .id = SNOR_ID(0xef, 0x60, 0x12), 161 .name = "w25q20ew", 162 .size = SZ_256K, 163 .no_sfdp_flags = SECT_4K, 164 }, { 165 .id = SNOR_ID(0xef, 0x60, 0x15), 166 .name = "w25q16dw", 167 .size = SZ_2M, 168 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 169 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 170 }, { 171 .id = SNOR_ID(0xef, 0x60, 0x16), 172 .name = "w25q32dw", 173 .size = SZ_4M, 174 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 175 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 176 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 177 }, { 178 .id = SNOR_ID(0xef, 0x60, 0x17), 179 .name = "w25q64dw", 180 .size = SZ_8M, 181 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 182 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 183 }, { 184 .id = SNOR_ID(0xef, 0x60, 0x18), 185 .name = "w25q128fw", 186 .size = SZ_16M, 187 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 188 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 189 }, { 190 .id = SNOR_ID(0xef, 0x60, 0x19), 191 .name = "w25q256jw", 192 .size = SZ_32M, 193 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 194 }, { 195 .id = SNOR_ID(0xef, 0x60, 0x20), 196 .name = "w25q512nwq", 197 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 198 }, { 199 .id = SNOR_ID(0xef, 0x70, 0x15), 200 .name = "w25q16jv-im/jm", 201 .size = SZ_2M, 202 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 203 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 204 }, { 205 .id = SNOR_ID(0xef, 0x70, 0x16), 206 .name = "w25q32jv", 207 .size = SZ_4M, 208 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 209 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 210 }, { 211 .id = SNOR_ID(0xef, 0x70, 0x17), 212 .name = "w25q64jvm", 213 .size = SZ_8M, 214 .no_sfdp_flags = SECT_4K, 215 }, { 216 .id = SNOR_ID(0xef, 0x70, 0x18), 217 .name = "w25q128jv", 218 .size = SZ_16M, 219 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 220 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 221 }, { 222 .id = SNOR_ID(0xef, 0x70, 0x19), 223 .name = "w25q256jvm", 224 }, { 225 .id = SNOR_ID(0xef, 0x71, 0x19), 226 .name = "w25m512jv", 227 .size = SZ_64M, 228 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 229 }, { 230 .id = SNOR_ID(0xef, 0x80, 0x16), 231 .name = "w25q32jwm", 232 .size = SZ_4M, 233 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 234 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 235 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 236 }, { 237 .id = SNOR_ID(0xef, 0x80, 0x17), 238 .name = "w25q64jwm", 239 .size = SZ_8M, 240 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 241 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 242 }, { 243 .id = SNOR_ID(0xef, 0x80, 0x18), 244 .name = "w25q128jwm", 245 .size = SZ_16M, 246 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 247 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 248 }, { 249 .id = SNOR_ID(0xef, 0x80, 0x19), 250 .name = "w25q256jwm", 251 .size = SZ_32M, 252 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 253 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 254 }, { 255 .id = SNOR_ID(0xef, 0x80, 0x20), 256 .name = "w25q512nwm", 257 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 258 }, 259 }; 260 261 /** 262 * winbond_nor_write_ear() - Write Extended Address Register. 263 * @nor: pointer to 'struct spi_nor'. 264 * @ear: value to write to the Extended Address Register. 265 * 266 * Return: 0 on success, -errno otherwise. 267 */ 268 static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear) 269 { 270 int ret; 271 272 nor->bouncebuf[0] = ear; 273 274 if (nor->spimem) { 275 struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf); 276 277 spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); 278 279 ret = spi_mem_exec_op(nor->spimem, &op); 280 } else { 281 ret = spi_nor_controller_ops_write_reg(nor, 282 WINBOND_NOR_OP_WREAR, 283 nor->bouncebuf, 1); 284 } 285 286 if (ret) 287 dev_dbg(nor->dev, "error %d writing EAR\n", ret); 288 289 return ret; 290 } 291 292 /** 293 * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond 294 * flashes. 295 * @nor: pointer to 'struct spi_nor'. 296 * @enable: true to enter the 4-byte address mode, false to exit the 4-byte 297 * address mode. 298 * 299 * Return: 0 on success, -errno otherwise. 300 */ 301 static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) 302 { 303 int ret; 304 305 ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable); 306 if (ret || enable) 307 return ret; 308 309 /* 310 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address 311 * Register to be set to 1, so all 3-byte-address reads come from the 312 * second 16M. We must clear the register to enable normal behavior. 313 */ 314 ret = spi_nor_write_enable(nor); 315 if (ret) 316 return ret; 317 318 ret = winbond_nor_write_ear(nor, 0); 319 if (ret) 320 return ret; 321 322 return spi_nor_write_disable(nor); 323 } 324 325 static const struct spi_nor_otp_ops winbond_nor_otp_ops = { 326 .read = spi_nor_otp_read_secr, 327 .write = spi_nor_otp_write_secr, 328 .erase = spi_nor_otp_erase_secr, 329 .lock = spi_nor_otp_lock_sr2, 330 .is_locked = spi_nor_otp_is_locked_sr2, 331 }; 332 333 static int winbond_nor_late_init(struct spi_nor *nor) 334 { 335 struct spi_nor_flash_parameter *params = nor->params; 336 337 if (params->otp.org) 338 params->otp.ops = &winbond_nor_otp_ops; 339 340 /* 341 * Winbond seems to require that the Extended Address Register to be set 342 * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV. 343 * This requirement is not described in the JESD216 SFDP standard, thus 344 * it is Winbond specific. Since we do not know if other Winbond flashes 345 * have the same requirement, play safe and overwrite the method parsed 346 * from BFPT, if any. 347 */ 348 params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; 349 350 return 0; 351 } 352 353 static const struct spi_nor_fixups winbond_nor_fixups = { 354 .late_init = winbond_nor_late_init, 355 }; 356 357 const struct spi_nor_manufacturer spi_nor_winbond = { 358 .name = "winbond", 359 .parts = winbond_nor_parts, 360 .nparts = ARRAY_SIZE(winbond_nor_parts), 361 .fixups = &winbond_nor_fixups, 362 }; 363