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 .name = "w25q128", 133 .size = SZ_16M, 134 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 135 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 136 .fixups = &w25q128_fixups, 137 }, { 138 .id = SNOR_ID(0xef, 0x40, 0x19), 139 .name = "w25q256", 140 .size = SZ_32M, 141 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 142 .fixups = &w25q256_fixups, 143 }, { 144 .id = SNOR_ID(0xef, 0x40, 0x20), 145 .name = "w25q512jvq", 146 .size = SZ_64M, 147 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 148 }, { 149 .id = SNOR_ID(0xef, 0x50, 0x12), 150 .name = "w25q20bw", 151 .size = SZ_256K, 152 .no_sfdp_flags = SECT_4K, 153 }, { 154 .id = SNOR_ID(0xef, 0x50, 0x14), 155 .name = "w25q80", 156 .size = SZ_1M, 157 .no_sfdp_flags = SECT_4K, 158 }, { 159 .id = SNOR_ID(0xef, 0x60, 0x12), 160 .name = "w25q20ew", 161 .size = SZ_256K, 162 .no_sfdp_flags = SECT_4K, 163 }, { 164 .id = SNOR_ID(0xef, 0x60, 0x15), 165 .name = "w25q16dw", 166 .size = SZ_2M, 167 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 168 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 169 }, { 170 .id = SNOR_ID(0xef, 0x60, 0x16), 171 .name = "w25q32dw", 172 .size = SZ_4M, 173 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 174 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 175 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 176 }, { 177 .id = SNOR_ID(0xef, 0x60, 0x17), 178 .name = "w25q64dw", 179 .size = SZ_8M, 180 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 181 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 182 }, { 183 .id = SNOR_ID(0xef, 0x60, 0x18), 184 .name = "w25q128fw", 185 .size = SZ_16M, 186 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 187 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 188 }, { 189 .id = SNOR_ID(0xef, 0x60, 0x19), 190 .name = "w25q256jw", 191 .size = SZ_32M, 192 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 193 }, { 194 .id = SNOR_ID(0xef, 0x60, 0x20), 195 .name = "w25q512nwq", 196 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 197 }, { 198 .id = SNOR_ID(0xef, 0x70, 0x15), 199 .name = "w25q16jv-im/jm", 200 .size = SZ_2M, 201 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 202 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 203 }, { 204 .id = SNOR_ID(0xef, 0x70, 0x16), 205 .name = "w25q32jv", 206 .size = SZ_4M, 207 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 208 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 209 }, { 210 .id = SNOR_ID(0xef, 0x70, 0x17), 211 .name = "w25q64jvm", 212 .size = SZ_8M, 213 .no_sfdp_flags = SECT_4K, 214 }, { 215 .id = SNOR_ID(0xef, 0x70, 0x18), 216 .name = "w25q128jv", 217 .size = SZ_16M, 218 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 219 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 220 }, { 221 .id = SNOR_ID(0xef, 0x70, 0x19), 222 .name = "w25q256jvm", 223 }, { 224 .id = SNOR_ID(0xef, 0x71, 0x19), 225 .name = "w25m512jv", 226 .size = SZ_64M, 227 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 228 }, { 229 .id = SNOR_ID(0xef, 0x80, 0x16), 230 .name = "w25q32jwm", 231 .size = SZ_4M, 232 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 233 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 234 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 235 }, { 236 .id = SNOR_ID(0xef, 0x80, 0x17), 237 .name = "w25q64jwm", 238 .size = SZ_8M, 239 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 240 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 241 }, { 242 .id = SNOR_ID(0xef, 0x80, 0x18), 243 .name = "w25q128jwm", 244 .size = SZ_16M, 245 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 246 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 247 }, { 248 .id = SNOR_ID(0xef, 0x80, 0x19), 249 .name = "w25q256jwm", 250 .size = SZ_32M, 251 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, 252 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 253 }, { 254 .id = SNOR_ID(0xef, 0x80, 0x20), 255 .name = "w25q512nwm", 256 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), 257 }, 258 }; 259 260 /** 261 * winbond_nor_write_ear() - Write Extended Address Register. 262 * @nor: pointer to 'struct spi_nor'. 263 * @ear: value to write to the Extended Address Register. 264 * 265 * Return: 0 on success, -errno otherwise. 266 */ 267 static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear) 268 { 269 int ret; 270 271 nor->bouncebuf[0] = ear; 272 273 if (nor->spimem) { 274 struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf); 275 276 spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); 277 278 ret = spi_mem_exec_op(nor->spimem, &op); 279 } else { 280 ret = spi_nor_controller_ops_write_reg(nor, 281 WINBOND_NOR_OP_WREAR, 282 nor->bouncebuf, 1); 283 } 284 285 if (ret) 286 dev_dbg(nor->dev, "error %d writing EAR\n", ret); 287 288 return ret; 289 } 290 291 /** 292 * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond 293 * flashes. 294 * @nor: pointer to 'struct spi_nor'. 295 * @enable: true to enter the 4-byte address mode, false to exit the 4-byte 296 * address mode. 297 * 298 * Return: 0 on success, -errno otherwise. 299 */ 300 static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) 301 { 302 int ret; 303 304 ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable); 305 if (ret || enable) 306 return ret; 307 308 /* 309 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address 310 * Register to be set to 1, so all 3-byte-address reads come from the 311 * second 16M. We must clear the register to enable normal behavior. 312 */ 313 ret = spi_nor_write_enable(nor); 314 if (ret) 315 return ret; 316 317 ret = winbond_nor_write_ear(nor, 0); 318 if (ret) 319 return ret; 320 321 return spi_nor_write_disable(nor); 322 } 323 324 static const struct spi_nor_otp_ops winbond_nor_otp_ops = { 325 .read = spi_nor_otp_read_secr, 326 .write = spi_nor_otp_write_secr, 327 .erase = spi_nor_otp_erase_secr, 328 .lock = spi_nor_otp_lock_sr2, 329 .is_locked = spi_nor_otp_is_locked_sr2, 330 }; 331 332 static int winbond_nor_late_init(struct spi_nor *nor) 333 { 334 struct spi_nor_flash_parameter *params = nor->params; 335 336 if (params->otp.org) 337 params->otp.ops = &winbond_nor_otp_ops; 338 339 /* 340 * Winbond seems to require that the Extended Address Register to be set 341 * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV. 342 * This requirement is not described in the JESD216 SFDP standard, thus 343 * it is Winbond specific. Since we do not know if other Winbond flashes 344 * have the same requirement, play safe and overwrite the method parsed 345 * from BFPT, if any. 346 */ 347 params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; 348 349 return 0; 350 } 351 352 static const struct spi_nor_fixups winbond_nor_fixups = { 353 .late_init = winbond_nor_late_init, 354 }; 355 356 const struct spi_nor_manufacturer spi_nor_winbond = { 357 .name = "winbond", 358 .parts = winbond_nor_parts, 359 .nparts = ARRAY_SIZE(winbond_nor_parts), 360 .fixups = &winbond_nor_fixups, 361 }; 362