0001
0002
0003
0004
0005
0006
0007 #include <linux/mtd/spi-nor.h>
0008
0009 #include "core.h"
0010
0011 #define WINBOND_NOR_OP_RDEAR 0xc8
0012 #define WINBOND_NOR_OP_WREAR 0xc5
0013
0014 #define WINBOND_NOR_WREAR_OP(buf) \
0015 SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \
0016 SPI_MEM_OP_NO_ADDR, \
0017 SPI_MEM_OP_NO_DUMMY, \
0018 SPI_MEM_OP_DATA_OUT(1, buf, 0))
0019
0020 static int
0021 w25q256_post_bfpt_fixups(struct spi_nor *nor,
0022 const struct sfdp_parameter_header *bfpt_header,
0023 const struct sfdp_bfpt *bfpt)
0024 {
0025
0026
0027
0028
0029
0030
0031
0032
0033 if (bfpt_header->major == SFDP_JESD216_MAJOR &&
0034 bfpt_header->minor == SFDP_JESD216A_MINOR)
0035 nor->flags |= SNOR_F_4B_OPCODES;
0036
0037 return 0;
0038 }
0039
0040 static const struct spi_nor_fixups w25q256_fixups = {
0041 .post_bfpt = w25q256_post_bfpt_fixups,
0042 };
0043
0044 static const struct flash_info winbond_nor_parts[] = {
0045
0046 { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1)
0047 NO_SFDP_FLAGS(SECT_4K) },
0048 { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2)
0049 NO_SFDP_FLAGS(SECT_4K) },
0050 { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4)
0051 NO_SFDP_FLAGS(SECT_4K) },
0052 { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8)
0053 NO_SFDP_FLAGS(SECT_4K) },
0054 { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16)
0055 NO_SFDP_FLAGS(SECT_4K) },
0056 { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32)
0057 NO_SFDP_FLAGS(SECT_4K) },
0058 { "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32)
0059 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0060 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0061 SPI_NOR_QUAD_READ) },
0062 { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64)
0063 NO_SFDP_FLAGS(SECT_4K) },
0064 { "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32)
0065 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0066 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0067 SPI_NOR_QUAD_READ) },
0068 { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4)
0069 NO_SFDP_FLAGS(SECT_4K) },
0070 { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4)
0071 NO_SFDP_FLAGS(SECT_4K) },
0072 { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4)
0073 NO_SFDP_FLAGS(SECT_4K) },
0074 { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64)
0075 NO_SFDP_FLAGS(SECT_4K) },
0076 { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64)
0077 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0078 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0079 OTP_INFO(256, 3, 0x1000, 0x1000) },
0080 { "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64)
0081 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0082 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0083 SPI_NOR_QUAD_READ) },
0084 { "w25q32jwm", INFO(0xef8016, 0, 64 * 1024, 64)
0085 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0086 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0087 OTP_INFO(256, 3, 0x1000, 0x1000) },
0088 { "w25q64jwm", INFO(0xef8017, 0, 64 * 1024, 128)
0089 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0090 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0091 SPI_NOR_QUAD_READ) },
0092 { "w25q128jwm", INFO(0xef8018, 0, 64 * 1024, 256)
0093 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0094 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0095 SPI_NOR_QUAD_READ) },
0096 { "w25q256jwm", INFO(0xef8019, 0, 64 * 1024, 512)
0097 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0098 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0099 SPI_NOR_QUAD_READ) },
0100 { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128)
0101 NO_SFDP_FLAGS(SECT_4K) },
0102 { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128)
0103 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0104 SPI_NOR_QUAD_READ) },
0105 { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128)
0106 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0107 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0108 SPI_NOR_QUAD_READ) },
0109 { "w25q64jvm", INFO(0xef7017, 0, 64 * 1024, 128)
0110 NO_SFDP_FLAGS(SECT_4K) },
0111 { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256)
0112 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0113 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0114 SPI_NOR_QUAD_READ) },
0115 { "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256)
0116 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0117 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0118 SPI_NOR_QUAD_READ) },
0119 { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16)
0120 NO_SFDP_FLAGS(SECT_4K) },
0121 { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16)
0122 NO_SFDP_FLAGS(SECT_4K) },
0123 { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256)
0124 NO_SFDP_FLAGS(SECT_4K) },
0125 { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512)
0126 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0127 .fixups = &w25q256_fixups },
0128 { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512)
0129 PARSE_SFDP },
0130 { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512)
0131 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0132 SPI_NOR_QUAD_READ) },
0133 { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
0134 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
0135 SPI_NOR_DUAL_READ) },
0136 { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
0137 PARSE_SFDP
0138 OTP_INFO(256, 3, 0x1000, 0x1000) },
0139 { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024)
0140 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0141 SPI_NOR_QUAD_READ) },
0142 };
0143
0144
0145
0146
0147
0148
0149
0150
0151 static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
0152 {
0153 int ret;
0154
0155 nor->bouncebuf[0] = ear;
0156
0157 if (nor->spimem) {
0158 struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
0159
0160 spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
0161
0162 ret = spi_mem_exec_op(nor->spimem, &op);
0163 } else {
0164 ret = spi_nor_controller_ops_write_reg(nor,
0165 WINBOND_NOR_OP_WREAR,
0166 nor->bouncebuf, 1);
0167 }
0168
0169 if (ret)
0170 dev_dbg(nor->dev, "error %d writing EAR\n", ret);
0171
0172 return ret;
0173 }
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184 static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
0185 {
0186 int ret;
0187
0188 ret = spi_nor_set_4byte_addr_mode(nor, enable);
0189 if (ret || enable)
0190 return ret;
0191
0192
0193
0194
0195
0196
0197 ret = spi_nor_write_enable(nor);
0198 if (ret)
0199 return ret;
0200
0201 ret = winbond_nor_write_ear(nor, 0);
0202 if (ret)
0203 return ret;
0204
0205 return spi_nor_write_disable(nor);
0206 }
0207
0208 static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
0209 .read = spi_nor_otp_read_secr,
0210 .write = spi_nor_otp_write_secr,
0211 .erase = spi_nor_otp_erase_secr,
0212 .lock = spi_nor_otp_lock_sr2,
0213 .is_locked = spi_nor_otp_is_locked_sr2,
0214 };
0215
0216 static void winbond_nor_default_init(struct spi_nor *nor)
0217 {
0218 nor->params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
0219 }
0220
0221 static void winbond_nor_late_init(struct spi_nor *nor)
0222 {
0223 if (nor->params->otp.org->n_regions)
0224 nor->params->otp.ops = &winbond_nor_otp_ops;
0225 }
0226
0227 static const struct spi_nor_fixups winbond_nor_fixups = {
0228 .default_init = winbond_nor_default_init,
0229 .late_init = winbond_nor_late_init,
0230 };
0231
0232 const struct spi_nor_manufacturer spi_nor_winbond = {
0233 .name = "winbond",
0234 .parts = winbond_nor_parts,
0235 .nparts = ARRAY_SIZE(winbond_nor_parts),
0236 .fixups = &winbond_nor_fixups,
0237 };