| 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 MXIC_NOR_OP_RD_CR2 0x71 /* Read configuration register 2 opcode */ |
| 12 | #define MXIC_NOR_OP_WR_CR2 0x72 /* Write configuration register 2 opcode */ |
| 13 | #define MXIC_NOR_ADDR_CR2_MODE 0x00000000 /* CR2 address for setting spi/sopi/dopi mode */ |
| 14 | #define MXIC_NOR_ADDR_CR2_DC 0x00000300 /* CR2 address for setting dummy cycles */ |
| 15 | #define MXIC_NOR_REG_DOPI_EN 0x2 /* Enable Octal DTR */ |
| 16 | #define MXIC_NOR_REG_SPI_EN 0x0 /* Enable SPI */ |
| 17 | |
| 18 | /* Convert dummy cycles to bit pattern */ |
| 19 | #define MXIC_NOR_REG_DC(p) \ |
| 20 | ((20 - (p)) >> 1) |
| 21 | |
| 22 | #define MXIC_NOR_WR_CR2(addr, ndata, buf) \ |
| 23 | SPI_MEM_OP(SPI_MEM_OP_CMD(MXIC_NOR_OP_WR_CR2, 0), \ |
| 24 | SPI_MEM_OP_ADDR(4, addr, 0), \ |
| 25 | SPI_MEM_OP_NO_DUMMY, \ |
| 26 | SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) |
| 27 | |
| 28 | static int |
| 29 | mx25l25635_post_bfpt_fixups(struct spi_nor *nor, |
| 30 | const struct sfdp_parameter_header *, |
| 31 | const struct sfdp_bfpt *bfpt) |
| 32 | { |
| 33 | /* |
| 34 | * MX25L25635F supports 4B opcodes but MX25L25635E does not. |
| 35 | * Unfortunately, Macronix has re-used the same JEDEC ID for both |
| 36 | * variants which prevents us from defining a new entry in the parts |
| 37 | * table. |
| 38 | * We need a way to differentiate MX25L25635E and MX25L25635F, and it |
| 39 | * seems that the F version advertises support for Fast Read 4-4-4 in |
| 40 | * its BFPT table. |
| 41 | */ |
| 42 | if (bfpt->dwords[SFDP_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) |
| 43 | nor->flags |= SNOR_F_4B_OPCODES; |
| 44 | |
| 45 | return 0; |
| 46 | } |
| 47 | |
| 48 | static int |
| 49 | macronix_qpp4b_post_sfdp_fixups(struct spi_nor *nor) |
| 50 | { |
| 51 | /* PP_1_1_4_4B is supported but missing in 4BAIT. */ |
| 52 | struct spi_nor_flash_parameter *params = nor->params; |
| 53 | |
| 54 | params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; |
| 55 | spi_nor_set_pp_settings(pp: ¶ms->page_programs[SNOR_CMD_PP_1_1_4], |
| 56 | SPINOR_OP_PP_1_1_4_4B, proto: SNOR_PROTO_1_1_4); |
| 57 | |
| 58 | return 0; |
| 59 | } |
| 60 | |
| 61 | static int |
| 62 | mx25l3255e_late_init_fixups(struct spi_nor *nor) |
| 63 | { |
| 64 | struct spi_nor_flash_parameter *params = nor->params; |
| 65 | |
| 66 | /* |
| 67 | * SFDP of MX25L3255E is JESD216, which does not include the Quad |
| 68 | * Enable bit Requirement in BFPT. As a result, during BFPT parsing, |
| 69 | * the quad_enable method is not set to spi_nor_sr1_bit6_quad_enable. |
| 70 | * Therefore, it is necessary to correct this setting by late_init. |
| 71 | */ |
| 72 | params->quad_enable = spi_nor_sr1_bit6_quad_enable; |
| 73 | |
| 74 | /* |
| 75 | * In addition, MX25L3255E also supports 1-4-4 page program in 3-byte |
| 76 | * address mode. However, since the 3-byte address 1-4-4 page program |
| 77 | * is not defined in SFDP, it needs to be configured in late_init. |
| 78 | */ |
| 79 | params->hwcaps.mask |= SNOR_HWCAPS_PP_1_4_4; |
| 80 | spi_nor_set_pp_settings(pp: ¶ms->page_programs[SNOR_CMD_PP_1_4_4], |
| 81 | SPINOR_OP_PP_1_4_4, proto: SNOR_PROTO_1_4_4); |
| 82 | |
| 83 | return 0; |
| 84 | } |
| 85 | |
| 86 | static const struct spi_nor_fixups mx25l25635_fixups = { |
| 87 | .post_bfpt = mx25l25635_post_bfpt_fixups, |
| 88 | .post_sfdp = macronix_qpp4b_post_sfdp_fixups, |
| 89 | }; |
| 90 | |
| 91 | static const struct spi_nor_fixups macronix_qpp4b_fixups = { |
| 92 | .post_sfdp = macronix_qpp4b_post_sfdp_fixups, |
| 93 | }; |
| 94 | |
| 95 | static const struct spi_nor_fixups mx25l3255e_fixups = { |
| 96 | .late_init = mx25l3255e_late_init_fixups, |
| 97 | }; |
| 98 | |
| 99 | static const struct flash_info macronix_nor_parts[] = { |
| 100 | { |
| 101 | .id = SNOR_ID(0xc2, 0x20, 0x10), |
| 102 | .name = "mx25l512e" , |
| 103 | .size = SZ_64K, |
| 104 | .no_sfdp_flags = SECT_4K, |
| 105 | }, { |
| 106 | .id = SNOR_ID(0xc2, 0x20, 0x12), |
| 107 | .name = "mx25l2005a" , |
| 108 | .size = SZ_256K, |
| 109 | .no_sfdp_flags = SECT_4K, |
| 110 | }, { |
| 111 | .id = SNOR_ID(0xc2, 0x20, 0x13), |
| 112 | .name = "mx25l4005a" , |
| 113 | .size = SZ_512K, |
| 114 | .no_sfdp_flags = SECT_4K, |
| 115 | }, { |
| 116 | .id = SNOR_ID(0xc2, 0x20, 0x14), |
| 117 | .name = "mx25l8005" , |
| 118 | .size = SZ_1M, |
| 119 | }, { |
| 120 | /* MX25L1606E */ |
| 121 | .id = SNOR_ID(0xc2, 0x20, 0x15), |
| 122 | }, { |
| 123 | .id = SNOR_ID(0xc2, 0x20, 0x16), |
| 124 | .name = "mx25l3205d" , |
| 125 | .size = SZ_4M, |
| 126 | .no_sfdp_flags = SECT_4K, |
| 127 | }, { |
| 128 | .id = SNOR_ID(0xc2, 0x20, 0x17), |
| 129 | .name = "mx25l6405d" , |
| 130 | .size = SZ_8M, |
| 131 | .no_sfdp_flags = SECT_4K, |
| 132 | }, { |
| 133 | /* MX25L12805D */ |
| 134 | .id = SNOR_ID(0xc2, 0x20, 0x18), |
| 135 | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP, |
| 136 | }, { |
| 137 | /* MX25L25635E, MX25L25645G */ |
| 138 | .id = SNOR_ID(0xc2, 0x20, 0x19), |
| 139 | .fixups = &mx25l25635_fixups |
| 140 | }, { |
| 141 | /* MX66L51235F */ |
| 142 | .id = SNOR_ID(0xc2, 0x20, 0x1a), |
| 143 | .fixup_flags = SPI_NOR_4B_OPCODES, |
| 144 | .fixups = ¯onix_qpp4b_fixups, |
| 145 | }, { |
| 146 | /* MX66L1G45G */ |
| 147 | .id = SNOR_ID(0xc2, 0x20, 0x1b), |
| 148 | .fixups = ¯onix_qpp4b_fixups, |
| 149 | }, { |
| 150 | /* MX66L2G45G */ |
| 151 | .id = SNOR_ID(0xc2, 0x20, 0x1c), |
| 152 | .fixups = ¯onix_qpp4b_fixups, |
| 153 | }, { |
| 154 | .id = SNOR_ID(0xc2, 0x23, 0x14), |
| 155 | .name = "mx25v8035f" , |
| 156 | .size = SZ_1M, |
| 157 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 158 | }, { |
| 159 | .id = SNOR_ID(0xc2, 0x25, 0x32), |
| 160 | .name = "mx25u2033e" , |
| 161 | .size = SZ_256K, |
| 162 | .no_sfdp_flags = SECT_4K, |
| 163 | }, { |
| 164 | .id = SNOR_ID(0xc2, 0x25, 0x33), |
| 165 | .name = "mx25u4035" , |
| 166 | .size = SZ_512K, |
| 167 | .no_sfdp_flags = SECT_4K, |
| 168 | }, { |
| 169 | .id = SNOR_ID(0xc2, 0x25, 0x34), |
| 170 | .name = "mx25u8035" , |
| 171 | .size = SZ_1M, |
| 172 | .no_sfdp_flags = SECT_4K, |
| 173 | }, { |
| 174 | .id = SNOR_ID(0xc2, 0x25, 0x36), |
| 175 | .name = "mx25u3235f" , |
| 176 | .size = SZ_4M, |
| 177 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 178 | }, { |
| 179 | .id = SNOR_ID(0xc2, 0x25, 0x37), |
| 180 | .name = "mx25u6435f" , |
| 181 | .size = SZ_8M, |
| 182 | .no_sfdp_flags = SECT_4K, |
| 183 | }, { |
| 184 | .id = SNOR_ID(0xc2, 0x25, 0x38), |
| 185 | .name = "mx25u12835f" , |
| 186 | .size = SZ_16M, |
| 187 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 188 | }, { |
| 189 | /* MX25U51245G */ |
| 190 | .id = SNOR_ID(0xc2, 0x25, 0x3a), |
| 191 | .fixups = ¯onix_qpp4b_fixups, |
| 192 | }, { |
| 193 | /* MX66U1G45G */ |
| 194 | .id = SNOR_ID(0xc2, 0x25, 0x3b), |
| 195 | .fixups = ¯onix_qpp4b_fixups, |
| 196 | }, { |
| 197 | /* MX66U2G45G */ |
| 198 | .id = SNOR_ID(0xc2, 0x25, 0x3c), |
| 199 | .fixups = ¯onix_qpp4b_fixups, |
| 200 | }, { |
| 201 | .id = SNOR_ID(0xc2, 0x26, 0x18), |
| 202 | .name = "mx25l12855e" , |
| 203 | .size = SZ_16M, |
| 204 | }, { |
| 205 | .id = SNOR_ID(0xc2, 0x26, 0x19), |
| 206 | .name = "mx25l25655e" , |
| 207 | .size = SZ_32M, |
| 208 | }, { |
| 209 | .id = SNOR_ID(0xc2, 0x26, 0x1b), |
| 210 | .name = "mx66l1g55g" , |
| 211 | .size = SZ_128M, |
| 212 | .no_sfdp_flags = SPI_NOR_QUAD_READ, |
| 213 | }, { |
| 214 | .id = SNOR_ID(0xc2, 0x28, 0x15), |
| 215 | .name = "mx25r1635f" , |
| 216 | .size = SZ_2M, |
| 217 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 218 | }, { |
| 219 | .id = SNOR_ID(0xc2, 0x28, 0x16), |
| 220 | .name = "mx25r3235f" , |
| 221 | .size = SZ_4M, |
| 222 | .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, |
| 223 | }, { |
| 224 | /* MX25UW51245G */ |
| 225 | .id = SNOR_ID(0xc2, 0x81, 0x3a), |
| 226 | .n_banks = 4, |
| 227 | .flags = SPI_NOR_RWW, |
| 228 | }, { |
| 229 | /* MX25L3255E */ |
| 230 | .id = SNOR_ID(0xc2, 0x9e, 0x16), |
| 231 | .fixups = &mx25l3255e_fixups, |
| 232 | }, |
| 233 | /* |
| 234 | * This spares us of adding new flash entries for flashes that can be |
| 235 | * initialized solely based on the SFDP data, but still need the |
| 236 | * manufacturer hooks to set parameters that can't be discovered at SFDP |
| 237 | * parsing time. |
| 238 | */ |
| 239 | { .id = SNOR_ID(0xc2) } |
| 240 | }; |
| 241 | |
| 242 | static int macronix_nor_octal_dtr_en(struct spi_nor *nor) |
| 243 | { |
| 244 | struct spi_mem_op op; |
| 245 | u8 *buf = nor->bouncebuf, i; |
| 246 | int ret; |
| 247 | |
| 248 | /* Use dummy cycles which is parse by SFDP and convert to bit pattern. */ |
| 249 | buf[0] = MXIC_NOR_REG_DC(nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].num_wait_states); |
| 250 | op = (struct spi_mem_op)MXIC_NOR_WR_CR2(MXIC_NOR_ADDR_CR2_DC, 1, buf); |
| 251 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: nor->reg_proto); |
| 252 | if (ret) |
| 253 | return ret; |
| 254 | |
| 255 | /* Set the octal and DTR enable bits. */ |
| 256 | buf[0] = MXIC_NOR_REG_DOPI_EN; |
| 257 | op = (struct spi_mem_op)MXIC_NOR_WR_CR2(MXIC_NOR_ADDR_CR2_MODE, 1, buf); |
| 258 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: nor->reg_proto); |
| 259 | if (ret) |
| 260 | return ret; |
| 261 | |
| 262 | /* Read flash ID to make sure the switch was successful. */ |
| 263 | ret = spi_nor_read_id(nor, naddr: nor->addr_nbytes, ndummy: 4, id: buf, |
| 264 | reg_proto: SNOR_PROTO_8_8_8_DTR); |
| 265 | if (ret) { |
| 266 | dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n" , ret); |
| 267 | return ret; |
| 268 | } |
| 269 | |
| 270 | /* Macronix SPI-NOR flash 8D-8D-8D read ID would get 6 bytes data A-A-B-B-C-C */ |
| 271 | for (i = 0; i < nor->info->id->len; i++) |
| 272 | if (buf[i * 2] != buf[(i * 2) + 1] || buf[i * 2] != nor->info->id->bytes[i]) |
| 273 | return -EINVAL; |
| 274 | |
| 275 | return 0; |
| 276 | } |
| 277 | |
| 278 | static int macronix_nor_octal_dtr_dis(struct spi_nor *nor) |
| 279 | { |
| 280 | struct spi_mem_op op; |
| 281 | u8 *buf = nor->bouncebuf; |
| 282 | int ret; |
| 283 | |
| 284 | /* |
| 285 | * The register is 1-byte wide, but 1-byte transactions are not |
| 286 | * allowed in 8D-8D-8D mode. Since there is no register at the |
| 287 | * next location, just initialize the value to 0 and let the |
| 288 | * transaction go on. |
| 289 | */ |
| 290 | buf[0] = MXIC_NOR_REG_SPI_EN; |
| 291 | buf[1] = 0x0; |
| 292 | op = (struct spi_mem_op)MXIC_NOR_WR_CR2(MXIC_NOR_ADDR_CR2_MODE, 2, buf); |
| 293 | ret = spi_nor_write_any_volatile_reg(nor, op: &op, proto: SNOR_PROTO_8_8_8_DTR); |
| 294 | if (ret) |
| 295 | return ret; |
| 296 | |
| 297 | /* Read flash ID to make sure the switch was successful. */ |
| 298 | ret = spi_nor_read_id(nor, naddr: 0, ndummy: 0, id: buf, reg_proto: SNOR_PROTO_1_1_1); |
| 299 | if (ret) { |
| 300 | dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n" , ret); |
| 301 | return ret; |
| 302 | } |
| 303 | |
| 304 | if (memcmp(p: buf, q: nor->info->id->bytes, size: nor->info->id->len)) |
| 305 | return -EINVAL; |
| 306 | |
| 307 | return 0; |
| 308 | } |
| 309 | |
| 310 | static int macronix_nor_set_octal_dtr(struct spi_nor *nor, bool enable) |
| 311 | { |
| 312 | return enable ? macronix_nor_octal_dtr_en(nor) : macronix_nor_octal_dtr_dis(nor); |
| 313 | } |
| 314 | |
| 315 | static void macronix_nor_default_init(struct spi_nor *nor) |
| 316 | { |
| 317 | nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; |
| 318 | } |
| 319 | |
| 320 | static int macronix_nor_late_init(struct spi_nor *nor) |
| 321 | { |
| 322 | if (!nor->params->set_4byte_addr_mode) |
| 323 | nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; |
| 324 | nor->params->set_octal_dtr = macronix_nor_set_octal_dtr; |
| 325 | |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | static const struct spi_nor_fixups macronix_nor_fixups = { |
| 330 | .default_init = macronix_nor_default_init, |
| 331 | .late_init = macronix_nor_late_init, |
| 332 | }; |
| 333 | |
| 334 | const struct spi_nor_manufacturer spi_nor_macronix = { |
| 335 | .name = "macronix" , |
| 336 | .parts = macronix_nor_parts, |
| 337 | .nparts = ARRAY_SIZE(macronix_nor_parts), |
| 338 | .fixups = ¯onix_nor_fixups, |
| 339 | }; |
| 340 | |