| 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 | /* flash_info mfr_flag. Used to read proprietary FSR register. */ |
| 12 | #define USE_FSR BIT(0) |
| 13 | |
| 14 | #define SPINOR_OP_MT_DIE_ERASE 0xc4 /* Chip (die) erase opcode */ |
| 15 | #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ |
| 16 | #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ |
| 17 | #define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ |
| 18 | #define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */ |
| 19 | #define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */ |
| 20 | #define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */ |
| 21 | #define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */ |
| 22 | #define SPINOR_REG_MT_CFR1V_DEF 0x1f /* Default dummy cycles */ |
| 23 | #define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */ |
| 24 | #define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */ |
| 25 | |
| 26 | /* Flag Status Register bits */ |
| 27 | #define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */ |
| 28 | #define FSR_E_ERR BIT(5) /* Erase operation status */ |
| 29 | #define FSR_P_ERR BIT(4) /* Program operation status */ |
| 30 | #define FSR_PT_ERR BIT(1) /* Protection error bit */ |
| 31 | |
| 32 | /* Micron ST SPI NOR flash operations. */ |
| 33 | #define MICRON_ST_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ |
| 34 | SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 0), \ |
| 35 | SPI_MEM_OP_ADDR(naddr, addr, 0), \ |
| 36 | SPI_MEM_OP_NO_DUMMY, \ |
| 37 | SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) |
| 38 | |
| 39 | #define MICRON_ST_RDFSR_OP(buf) \ |
| 40 | SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), \ |
| 41 | SPI_MEM_OP_NO_ADDR, \ |
| 42 | SPI_MEM_OP_NO_DUMMY, \ |
| 43 | SPI_MEM_OP_DATA_IN(1, buf, 0)) |
| 44 | |
| 45 | #define MICRON_ST_CLFSR_OP \ |
| 46 | SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), \ |
| 47 | SPI_MEM_OP_NO_ADDR, \ |
| 48 | SPI_MEM_OP_NO_DUMMY, \ |
| 49 | SPI_MEM_OP_NO_DATA) |
| 50 | |
| 51 | static int micron_st_nor_octal_dtr_en(struct spi_nor *nor) |
| 52 | { |
| 53 | struct spi_mem_op op; |
| 54 | u8 *buf = nor->bouncebuf; |
| 55 | int ret; |
| 56 | u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; |
| 57 | |
| 58 | /* Use 20 dummy cycles for memory array reads. */ |
| 59 | *buf = 20; |
| 60 | op = (struct spi_mem_op) |
| 61 | MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes, |
| 62 | SPINOR_REG_MT_CFR1V, 1, buf); |
| 63 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: nor->reg_proto); |
| 64 | if (ret) |
| 65 | return ret; |
| 66 | |
| 67 | buf[0] = SPINOR_MT_OCT_DTR; |
| 68 | op = (struct spi_mem_op) |
| 69 | MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes, |
| 70 | SPINOR_REG_MT_CFR0V, 1, buf); |
| 71 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: nor->reg_proto); |
| 72 | if (ret) |
| 73 | return ret; |
| 74 | |
| 75 | /* Read flash ID to make sure the switch was successful. */ |
| 76 | ret = spi_nor_read_id(nor, naddr: 0, ndummy: 8, id: buf, reg_proto: SNOR_PROTO_8_8_8_DTR); |
| 77 | if (ret) { |
| 78 | dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n" , ret); |
| 79 | return ret; |
| 80 | } |
| 81 | |
| 82 | if (memcmp(p: buf, q: nor->info->id->bytes, size: nor->info->id->len)) |
| 83 | return -EINVAL; |
| 84 | |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor) |
| 89 | { |
| 90 | struct spi_mem_op op; |
| 91 | u8 *buf = nor->bouncebuf; |
| 92 | int ret; |
| 93 | |
| 94 | /* |
| 95 | * The register is 1-byte wide, but 1-byte transactions are not allowed |
| 96 | * in 8D-8D-8D mode. The next register is the dummy cycle configuration |
| 97 | * register. Since the transaction needs to be at least 2 bytes wide, |
| 98 | * set the next register to its default value. This also makes sense |
| 99 | * because the value was changed when enabling 8D-8D-8D mode, it should |
| 100 | * be reset when disabling. |
| 101 | */ |
| 102 | buf[0] = SPINOR_MT_EXSPI; |
| 103 | buf[1] = SPINOR_REG_MT_CFR1V_DEF; |
| 104 | op = (struct spi_mem_op) |
| 105 | MICRON_ST_NOR_WR_ANY_REG_OP(nor->addr_nbytes, |
| 106 | SPINOR_REG_MT_CFR0V, 2, buf); |
| 107 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: SNOR_PROTO_8_8_8_DTR); |
| 108 | if (ret) |
| 109 | return ret; |
| 110 | |
| 111 | /* Read flash ID to make sure the switch was successful. */ |
| 112 | ret = spi_nor_read_id(nor, naddr: 0, ndummy: 0, id: buf, reg_proto: SNOR_PROTO_1_1_1); |
| 113 | if (ret) { |
| 114 | dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n" , ret); |
| 115 | return ret; |
| 116 | } |
| 117 | |
| 118 | if (memcmp(p: buf, q: nor->info->id->bytes, size: nor->info->id->len)) |
| 119 | return -EINVAL; |
| 120 | |
| 121 | return 0; |
| 122 | } |
| 123 | |
| 124 | static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable) |
| 125 | { |
| 126 | return enable ? micron_st_nor_octal_dtr_en(nor) : |
| 127 | micron_st_nor_octal_dtr_dis(nor); |
| 128 | } |
| 129 | |
| 130 | static void mt35xu512aba_default_init(struct spi_nor *nor) |
| 131 | { |
| 132 | nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr; |
| 133 | } |
| 134 | |
| 135 | static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) |
| 136 | { |
| 137 | /* Set the Fast Read settings. */ |
| 138 | nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; |
| 139 | spi_nor_set_read_settings(read: &nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], |
| 140 | num_mode_clocks: 0, num_wait_states: 20, SPINOR_OP_MT_DTR_RD, |
| 141 | proto: SNOR_PROTO_8_8_8_DTR); |
| 142 | |
| 143 | nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; |
| 144 | nor->params->rdsr_dummy = 8; |
| 145 | nor->params->rdsr_addr_nbytes = 0; |
| 146 | |
| 147 | /* |
| 148 | * The BFPT quad enable field is set to a reserved value so the quad |
| 149 | * enable function is ignored by spi_nor_parse_bfpt(). Make sure we |
| 150 | * disable it. |
| 151 | */ |
| 152 | nor->params->quad_enable = NULL; |
| 153 | |
| 154 | return 0; |
| 155 | } |
| 156 | |
| 157 | static const struct spi_nor_fixups mt35xu512aba_fixups = { |
| 158 | .default_init = mt35xu512aba_default_init, |
| 159 | .post_sfdp = mt35xu512aba_post_sfdp_fixup, |
| 160 | }; |
| 161 | |
| 162 | static const struct flash_info micron_nor_parts[] = { |
| 163 | { |
| 164 | .id = SNOR_ID(0x2c, 0x5b, 0x1a), |
| 165 | .name = "mt35xu512aba" , |
| 166 | .sector_size = SZ_128K, |
| 167 | .size = SZ_64M, |
| 168 | .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ | |
| 169 | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP, |
| 170 | .mfr_flags = USE_FSR, |
| 171 | .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE, |
| 172 | .fixups = &mt35xu512aba_fixups, |
| 173 | }, { |
| 174 | .id = SNOR_ID(0x2c, 0x5b, 0x1c), |
| 175 | .name = "mt35xu02g" , |
| 176 | .sector_size = SZ_128K, |
| 177 | .size = SZ_256M, |
| 178 | .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ, |
| 179 | .mfr_flags = USE_FSR, |
| 180 | .fixup_flags = SPI_NOR_4B_OPCODES, |
| 181 | }, |
| 182 | }; |
| 183 | |
| 184 | static int mt25qu512a_post_bfpt_fixup(struct spi_nor *nor, |
| 185 | const struct sfdp_parameter_header *, |
| 186 | const struct sfdp_bfpt *bfpt) |
| 187 | { |
| 188 | nor->flags &= ~SNOR_F_HAS_16BIT_SR; |
| 189 | return 0; |
| 190 | } |
| 191 | |
| 192 | static struct spi_nor_fixups mt25qu512a_fixups = { |
| 193 | .post_bfpt = mt25qu512a_post_bfpt_fixup, |
| 194 | }; |
| 195 | |
| 196 | static int st_nor_four_die_late_init(struct spi_nor *nor) |
| 197 | { |
| 198 | struct spi_nor_flash_parameter *params = nor->params; |
| 199 | |
| 200 | params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; |
| 201 | params->n_dice = 4; |
| 202 | |
| 203 | /* |
| 204 | * Unfortunately the die erase opcode does not have a 4-byte opcode |
| 205 | * correspondent for these flashes. The SFDP 4BAIT table fails to |
| 206 | * consider the die erase too. We're forced to enter in the 4 byte |
| 207 | * address mode in order to benefit of the die erase. |
| 208 | */ |
| 209 | return spi_nor_set_4byte_addr_mode(nor, enable: true); |
| 210 | } |
| 211 | |
| 212 | static int st_nor_two_die_late_init(struct spi_nor *nor) |
| 213 | { |
| 214 | struct spi_nor_flash_parameter *params = nor->params; |
| 215 | |
| 216 | params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; |
| 217 | params->n_dice = 2; |
| 218 | |
| 219 | /* |
| 220 | * Unfortunately the die erase opcode does not have a 4-byte opcode |
| 221 | * correspondent for these flashes. The SFDP 4BAIT table fails to |
| 222 | * consider the die erase too. We're forced to enter in the 4 byte |
| 223 | * address mode in order to benefit of the die erase. |
| 224 | */ |
| 225 | return spi_nor_set_4byte_addr_mode(nor, enable: true); |
| 226 | } |
| 227 | |
| 228 | static struct spi_nor_fixups n25q00_fixups = { |
| 229 | .late_init = st_nor_four_die_late_init, |
| 230 | }; |
| 231 | |
| 232 | static struct spi_nor_fixups mt25q01_fixups = { |
| 233 | .late_init = st_nor_two_die_late_init, |
| 234 | }; |
| 235 | |
| 236 | static struct spi_nor_fixups mt25q02_fixups = { |
| 237 | .late_init = st_nor_four_die_late_init, |
| 238 | }; |
| 239 | |
| 240 | static const struct flash_info st_nor_parts[] = { |
| 241 | { |
| 242 | .name = "m25p05-nonjedec" , |
| 243 | .sector_size = SZ_32K, |
| 244 | .size = SZ_64K, |
| 245 | }, { |
| 246 | .name = "m25p10-nonjedec" , |
| 247 | .sector_size = SZ_32K, |
| 248 | .size = SZ_128K, |
| 249 | }, { |
| 250 | .name = "m25p20-nonjedec" , |
| 251 | .size = SZ_256K, |
| 252 | }, { |
| 253 | .name = "m25p40-nonjedec" , |
| 254 | .size = SZ_512K, |
| 255 | }, { |
| 256 | .name = "m25p80-nonjedec" , |
| 257 | .size = SZ_1M, |
| 258 | }, { |
| 259 | .name = "m25p16-nonjedec" , |
| 260 | .size = SZ_2M, |
| 261 | }, { |
| 262 | .name = "m25p32-nonjedec" , |
| 263 | .size = SZ_4M, |
| 264 | }, { |
| 265 | .name = "m25p64-nonjedec" , |
| 266 | .size = SZ_8M, |
| 267 | }, { |
| 268 | .name = "m25p128-nonjedec" , |
| 269 | .sector_size = SZ_256K, |
| 270 | .size = SZ_16M, |
| 271 | }, { |
| 272 | .id = SNOR_ID(0x20, 0x20, 0x10), |
| 273 | .name = "m25p05" , |
| 274 | .sector_size = SZ_32K, |
| 275 | .size = SZ_64K, |
| 276 | }, { |
| 277 | .id = SNOR_ID(0x20, 0x20, 0x11), |
| 278 | .name = "m25p10" , |
| 279 | .sector_size = SZ_32K, |
| 280 | .size = SZ_128K, |
| 281 | }, { |
| 282 | .id = SNOR_ID(0x20, 0x20, 0x12), |
| 283 | .name = "m25p20" , |
| 284 | .size = SZ_256K, |
| 285 | }, { |
| 286 | .id = SNOR_ID(0x20, 0x20, 0x13), |
| 287 | .name = "m25p40" , |
| 288 | .size = SZ_512K, |
| 289 | }, { |
| 290 | .id = SNOR_ID(0x20, 0x20, 0x14), |
| 291 | .name = "m25p80" , |
| 292 | .size = SZ_1M, |
| 293 | }, { |
| 294 | .id = SNOR_ID(0x20, 0x20, 0x15), |
| 295 | .name = "m25p16" , |
| 296 | .size = SZ_2M, |
| 297 | }, { |
| 298 | .id = SNOR_ID(0x20, 0x20, 0x16), |
| 299 | .name = "m25p32" , |
| 300 | .size = SZ_4M, |
| 301 | }, { |
| 302 | .id = SNOR_ID(0x20, 0x20, 0x17), |
| 303 | .name = "m25p64" , |
| 304 | .size = SZ_8M, |
| 305 | }, { |
| 306 | .id = SNOR_ID(0x20, 0x20, 0x18), |
| 307 | .name = "m25p128" , |
| 308 | .sector_size = SZ_256K, |
| 309 | .size = SZ_16M, |
| 310 | }, { |
| 311 | .id = SNOR_ID(0x20, 0x40, 0x11), |
| 312 | .name = "m45pe10" , |
| 313 | .size = SZ_128K, |
| 314 | }, { |
| 315 | .id = SNOR_ID(0x20, 0x40, 0x14), |
| 316 | .name = "m45pe80" , |
| 317 | .size = SZ_1M, |
| 318 | }, { |
| 319 | .id = SNOR_ID(0x20, 0x40, 0x15), |
| 320 | .name = "m45pe16" , |
| 321 | .size = SZ_2M, |
| 322 | }, { |
| 323 | .id = SNOR_ID(0x20, 0x63, 0x16), |
| 324 | .name = "m25px32-s1" , |
| 325 | .size = SZ_4M, |
| 326 | .no_sfdp_flags = SECT_4K, |
| 327 | }, { |
| 328 | .id = SNOR_ID(0x20, 0x71, 0x14), |
| 329 | .name = "m25px80" , |
| 330 | .size = SZ_1M, |
| 331 | }, { |
| 332 | .id = SNOR_ID(0x20, 0x71, 0x15), |
| 333 | .name = "m25px16" , |
| 334 | .size = SZ_2M, |
| 335 | .no_sfdp_flags = SECT_4K, |
| 336 | }, { |
| 337 | .id = SNOR_ID(0x20, 0x71, 0x16), |
| 338 | .name = "m25px32" , |
| 339 | .size = SZ_4M, |
| 340 | .no_sfdp_flags = SECT_4K, |
| 341 | }, { |
| 342 | .id = SNOR_ID(0x20, 0x71, 0x17), |
| 343 | .name = "m25px64" , |
| 344 | .size = SZ_8M, |
| 345 | }, { |
| 346 | .id = SNOR_ID(0x20, 0x73, 0x16), |
| 347 | .name = "m25px32-s0" , |
| 348 | .size = SZ_4M, |
| 349 | .no_sfdp_flags = SECT_4K, |
| 350 | }, { |
| 351 | .id = SNOR_ID(0x20, 0x80, 0x12), |
| 352 | .name = "m25pe20" , |
| 353 | .size = SZ_256K, |
| 354 | }, { |
| 355 | .id = SNOR_ID(0x20, 0x80, 0x14), |
| 356 | .name = "m25pe80" , |
| 357 | .size = SZ_1M, |
| 358 | }, { |
| 359 | .id = SNOR_ID(0x20, 0x80, 0x15), |
| 360 | .name = "m25pe16" , |
| 361 | .size = SZ_2M, |
| 362 | .no_sfdp_flags = SECT_4K, |
| 363 | }, { |
| 364 | .id = SNOR_ID(0x20, 0xba, 0x16), |
| 365 | .name = "n25q032" , |
| 366 | .size = SZ_4M, |
| 367 | .no_sfdp_flags = SPI_NOR_QUAD_READ, |
| 368 | }, { |
| 369 | .id = SNOR_ID(0x20, 0xba, 0x17), |
| 370 | .name = "n25q064" , |
| 371 | .size = SZ_8M, |
| 372 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 373 | }, { |
| 374 | .id = SNOR_ID(0x20, 0xba, 0x18), |
| 375 | .name = "n25q128a13" , |
| 376 | .size = SZ_16M, |
| 377 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 378 | SPI_NOR_BP3_SR_BIT6, |
| 379 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 380 | .mfr_flags = USE_FSR, |
| 381 | }, { |
| 382 | .id = SNOR_ID(0x20, 0xba, 0x19, 0x10, 0x44, 0x00), |
| 383 | .name = "mt25ql256a" , |
| 384 | .size = SZ_32M, |
| 385 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 386 | .fixup_flags = SPI_NOR_4B_OPCODES, |
| 387 | .mfr_flags = USE_FSR, |
| 388 | }, { |
| 389 | .id = SNOR_ID(0x20, 0xba, 0x19), |
| 390 | .name = "n25q256a" , |
| 391 | .size = SZ_32M, |
| 392 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 393 | .mfr_flags = USE_FSR, |
| 394 | }, { |
| 395 | .id = SNOR_ID(0x20, 0xba, 0x20, 0x10, 0x44, 0x00), |
| 396 | .name = "mt25ql512a" , |
| 397 | .size = SZ_64M, |
| 398 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 399 | .fixup_flags = SPI_NOR_4B_OPCODES, |
| 400 | .mfr_flags = USE_FSR, |
| 401 | }, { |
| 402 | .id = SNOR_ID(0x20, 0xba, 0x20), |
| 403 | .name = "n25q512ax3" , |
| 404 | .size = SZ_64M, |
| 405 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 406 | SPI_NOR_BP3_SR_BIT6, |
| 407 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 408 | .mfr_flags = USE_FSR, |
| 409 | }, { |
| 410 | .id = SNOR_ID(0x20, 0xba, 0x21), |
| 411 | .name = "n25q00" , |
| 412 | .size = SZ_128M, |
| 413 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 414 | SPI_NOR_BP3_SR_BIT6, |
| 415 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 416 | .mfr_flags = USE_FSR, |
| 417 | .fixups = &n25q00_fixups, |
| 418 | }, { |
| 419 | .id = SNOR_ID(0x20, 0xba, 0x22), |
| 420 | .name = "mt25ql02g" , |
| 421 | .size = SZ_256M, |
| 422 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 423 | .mfr_flags = USE_FSR, |
| 424 | .fixups = &mt25q02_fixups, |
| 425 | }, { |
| 426 | .id = SNOR_ID(0x20, 0xbb, 0x15), |
| 427 | .name = "n25q016a" , |
| 428 | .size = SZ_2M, |
| 429 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 430 | }, { |
| 431 | .id = SNOR_ID(0x20, 0xbb, 0x16), |
| 432 | .name = "n25q032a" , |
| 433 | .size = SZ_4M, |
| 434 | .no_sfdp_flags = SPI_NOR_QUAD_READ, |
| 435 | }, { |
| 436 | .id = SNOR_ID(0x20, 0xbb, 0x17), |
| 437 | .name = "n25q064a" , |
| 438 | .size = SZ_8M, |
| 439 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 440 | SPI_NOR_BP3_SR_BIT6, |
| 441 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 442 | }, { |
| 443 | .id = SNOR_ID(0x20, 0xbb, 0x18), |
| 444 | .name = "n25q128a11" , |
| 445 | .size = SZ_16M, |
| 446 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 447 | SPI_NOR_BP3_SR_BIT6, |
| 448 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 449 | .mfr_flags = USE_FSR, |
| 450 | }, { |
| 451 | .id = SNOR_ID(0x20, 0xbb, 0x19, 0x10, 0x44, 0x00), |
| 452 | .name = "mt25qu256a" , |
| 453 | .size = SZ_32M, |
| 454 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 455 | SPI_NOR_BP3_SR_BIT6, |
| 456 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 457 | .fixup_flags = SPI_NOR_4B_OPCODES, |
| 458 | .mfr_flags = USE_FSR, |
| 459 | }, { |
| 460 | .id = SNOR_ID(0x20, 0xbb, 0x19), |
| 461 | .name = "n25q256ax1" , |
| 462 | .size = SZ_32M, |
| 463 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 464 | .mfr_flags = USE_FSR, |
| 465 | }, { |
| 466 | .id = SNOR_ID(0x20, 0xbb, 0x20, 0x10, 0x44, 0x00), |
| 467 | .name = "mt25qu512a" , |
| 468 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 469 | SPI_NOR_BP3_SR_BIT6, |
| 470 | .mfr_flags = USE_FSR, |
| 471 | .fixups = &mt25qu512a_fixups, |
| 472 | }, { |
| 473 | .id = SNOR_ID(0x20, 0xbb, 0x20), |
| 474 | .name = "n25q512a" , |
| 475 | .size = SZ_64M, |
| 476 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | |
| 477 | SPI_NOR_BP3_SR_BIT6, |
| 478 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 479 | .mfr_flags = USE_FSR, |
| 480 | }, { |
| 481 | .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), |
| 482 | .name = "mt25qu01g" , |
| 483 | .mfr_flags = USE_FSR, |
| 484 | .fixups = &mt25q01_fixups, |
| 485 | }, { |
| 486 | .id = SNOR_ID(0x20, 0xbb, 0x21), |
| 487 | .name = "n25q00a" , |
| 488 | .size = SZ_128M, |
| 489 | .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, |
| 490 | .mfr_flags = USE_FSR, |
| 491 | .fixups = &n25q00_fixups, |
| 492 | }, { |
| 493 | .id = SNOR_ID(0x20, 0xbb, 0x22), |
| 494 | .name = "mt25qu02g" , |
| 495 | .size = SZ_256M, |
| 496 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 497 | .mfr_flags = USE_FSR, |
| 498 | .fixups = &mt25q02_fixups, |
| 499 | } |
| 500 | }; |
| 501 | |
| 502 | /** |
| 503 | * micron_st_nor_read_fsr() - Read the Flag Status Register. |
| 504 | * @nor: pointer to 'struct spi_nor' |
| 505 | * @fsr: pointer to a DMA-able buffer where the value of the |
| 506 | * Flag Status Register will be written. Should be at least 2 |
| 507 | * bytes. |
| 508 | * |
| 509 | * Return: 0 on success, -errno otherwise. |
| 510 | */ |
| 511 | static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr) |
| 512 | { |
| 513 | int ret; |
| 514 | |
| 515 | if (nor->spimem) { |
| 516 | struct spi_mem_op op = MICRON_ST_RDFSR_OP(fsr); |
| 517 | |
| 518 | if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { |
| 519 | op.addr.nbytes = nor->params->rdsr_addr_nbytes; |
| 520 | op.dummy.nbytes = nor->params->rdsr_dummy; |
| 521 | /* |
| 522 | * We don't want to read only one byte in DTR mode. So, |
| 523 | * read 2 and then discard the second byte. |
| 524 | */ |
| 525 | op.data.nbytes = 2; |
| 526 | } |
| 527 | |
| 528 | spi_nor_spimem_setup_op(nor, op: &op, proto: nor->reg_proto); |
| 529 | |
| 530 | ret = spi_mem_exec_op(mem: nor->spimem, op: &op); |
| 531 | } else { |
| 532 | ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, buf: fsr, |
| 533 | len: 1); |
| 534 | } |
| 535 | |
| 536 | if (ret) |
| 537 | dev_dbg(nor->dev, "error %d reading FSR\n" , ret); |
| 538 | |
| 539 | return ret; |
| 540 | } |
| 541 | |
| 542 | /** |
| 543 | * micron_st_nor_clear_fsr() - Clear the Flag Status Register. |
| 544 | * @nor: pointer to 'struct spi_nor'. |
| 545 | */ |
| 546 | static void micron_st_nor_clear_fsr(struct spi_nor *nor) |
| 547 | { |
| 548 | int ret; |
| 549 | |
| 550 | if (nor->spimem) { |
| 551 | struct spi_mem_op op = MICRON_ST_CLFSR_OP; |
| 552 | |
| 553 | spi_nor_spimem_setup_op(nor, op: &op, proto: nor->reg_proto); |
| 554 | |
| 555 | ret = spi_mem_exec_op(mem: nor->spimem, op: &op); |
| 556 | } else { |
| 557 | ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR, |
| 558 | NULL, len: 0); |
| 559 | } |
| 560 | |
| 561 | if (ret) |
| 562 | dev_dbg(nor->dev, "error %d clearing FSR\n" , ret); |
| 563 | } |
| 564 | |
| 565 | /** |
| 566 | * micron_st_nor_ready() - Query the Status Register as well as the Flag Status |
| 567 | * Register to see if the flash is ready for new commands. If there are any |
| 568 | * errors in the FSR clear them. |
| 569 | * @nor: pointer to 'struct spi_nor'. |
| 570 | * |
| 571 | * Return: 1 if ready, 0 if not ready, -errno on errors. |
| 572 | */ |
| 573 | static int micron_st_nor_ready(struct spi_nor *nor) |
| 574 | { |
| 575 | int sr_ready, ret; |
| 576 | |
| 577 | sr_ready = spi_nor_sr_ready(nor); |
| 578 | if (sr_ready < 0) |
| 579 | return sr_ready; |
| 580 | |
| 581 | ret = micron_st_nor_read_fsr(nor, fsr: nor->bouncebuf); |
| 582 | if (ret) { |
| 583 | /* |
| 584 | * Some controllers, such as Intel SPI, do not support low |
| 585 | * level operations such as reading the flag status |
| 586 | * register. They only expose small amount of high level |
| 587 | * operations to the software. If this is the case we use |
| 588 | * only the status register value. |
| 589 | */ |
| 590 | return ret == -EOPNOTSUPP ? sr_ready : ret; |
| 591 | } |
| 592 | |
| 593 | if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { |
| 594 | if (nor->bouncebuf[0] & FSR_E_ERR) |
| 595 | dev_err(nor->dev, "Erase operation failed.\n" ); |
| 596 | else |
| 597 | dev_err(nor->dev, "Program operation failed.\n" ); |
| 598 | |
| 599 | if (nor->bouncebuf[0] & FSR_PT_ERR) |
| 600 | dev_err(nor->dev, |
| 601 | "Attempted to modify a protected sector.\n" ); |
| 602 | |
| 603 | micron_st_nor_clear_fsr(nor); |
| 604 | |
| 605 | /* |
| 606 | * WEL bit remains set to one when an erase or page program |
| 607 | * error occurs. Issue a Write Disable command to protect |
| 608 | * against inadvertent writes that can possibly corrupt the |
| 609 | * contents of the memory. |
| 610 | */ |
| 611 | ret = spi_nor_write_disable(nor); |
| 612 | if (ret) |
| 613 | return ret; |
| 614 | |
| 615 | return -EIO; |
| 616 | } |
| 617 | |
| 618 | return sr_ready && !!(nor->bouncebuf[0] & FSR_READY); |
| 619 | } |
| 620 | |
| 621 | static void micron_st_nor_default_init(struct spi_nor *nor) |
| 622 | { |
| 623 | nor->flags |= SNOR_F_HAS_LOCK; |
| 624 | nor->flags &= ~SNOR_F_HAS_16BIT_SR; |
| 625 | nor->params->quad_enable = NULL; |
| 626 | } |
| 627 | |
| 628 | static int micron_st_nor_late_init(struct spi_nor *nor) |
| 629 | { |
| 630 | struct spi_nor_flash_parameter *params = nor->params; |
| 631 | |
| 632 | if (nor->info->mfr_flags & USE_FSR) |
| 633 | params->ready = micron_st_nor_ready; |
| 634 | |
| 635 | if (!params->set_4byte_addr_mode) |
| 636 | params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b; |
| 637 | |
| 638 | return 0; |
| 639 | } |
| 640 | |
| 641 | static const struct spi_nor_fixups micron_st_nor_fixups = { |
| 642 | .default_init = micron_st_nor_default_init, |
| 643 | .late_init = micron_st_nor_late_init, |
| 644 | }; |
| 645 | |
| 646 | const struct spi_nor_manufacturer spi_nor_micron = { |
| 647 | .name = "micron" , |
| 648 | .parts = micron_nor_parts, |
| 649 | .nparts = ARRAY_SIZE(micron_nor_parts), |
| 650 | .fixups = µn_st_nor_fixups, |
| 651 | }; |
| 652 | |
| 653 | const struct spi_nor_manufacturer spi_nor_st = { |
| 654 | .name = "st" , |
| 655 | .parts = st_nor_parts, |
| 656 | .nparts = ARRAY_SIZE(st_nor_parts), |
| 657 | .fixups = µn_st_nor_fixups, |
| 658 | }; |
| 659 | |