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 /* SST flash_info mfr_flag. Used to specify SST byte programming. */ 12 #define SST_WRITE BIT(0) 13 14 #define SST26VF_CR_BPNV BIT(3) 15 16 static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 17 { 18 return -EOPNOTSUPP; 19 } 20 21 static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) 22 { 23 int ret; 24 25 /* We only support unlocking the entire flash array. */ 26 if (ofs != 0 || len != nor->params->size) 27 return -EINVAL; 28 29 ret = spi_nor_read_cr(nor, nor->bouncebuf); 30 if (ret) 31 return ret; 32 33 if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) { 34 dev_dbg(nor->dev, "Any block has been permanently locked\n"); 35 return -EINVAL; 36 } 37 38 return spi_nor_global_block_unlock(nor); 39 } 40 41 static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) 42 { 43 return -EOPNOTSUPP; 44 } 45 46 static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = { 47 .lock = sst26vf_nor_lock, 48 .unlock = sst26vf_nor_unlock, 49 .is_locked = sst26vf_nor_is_locked, 50 }; 51 52 static int sst26vf_nor_late_init(struct spi_nor *nor) 53 { 54 nor->params->locking_ops = &sst26vf_nor_locking_ops; 55 56 return 0; 57 } 58 59 static const struct spi_nor_fixups sst26vf_nor_fixups = { 60 .late_init = sst26vf_nor_late_init, 61 }; 62 63 static const struct flash_info sst_nor_parts[] = { 64 { 65 .id = SNOR_ID(0x62, 0x16, 0x12), 66 .name = "sst25wf020a", 67 .size = SZ_256K, 68 .flags = SPI_NOR_HAS_LOCK, 69 .no_sfdp_flags = SECT_4K, 70 }, { 71 .id = SNOR_ID(0x62, 0x16, 0x13), 72 .name = "sst25wf040b", 73 .size = SZ_512K, 74 .flags = SPI_NOR_HAS_LOCK, 75 .no_sfdp_flags = SECT_4K, 76 }, { 77 .id = SNOR_ID(0xbf, 0x25, 0x01), 78 .name = "sst25wf512", 79 .size = SZ_64K, 80 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 81 .no_sfdp_flags = SECT_4K, 82 .mfr_flags = SST_WRITE, 83 }, { 84 .id = SNOR_ID(0xbf, 0x25, 0x02), 85 .name = "sst25wf010", 86 .size = SZ_128K, 87 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 88 .no_sfdp_flags = SECT_4K, 89 .mfr_flags = SST_WRITE, 90 }, { 91 .id = SNOR_ID(0xbf, 0x25, 0x03), 92 .name = "sst25wf020", 93 .size = SZ_256K, 94 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 95 .no_sfdp_flags = SECT_4K, 96 .mfr_flags = SST_WRITE, 97 }, { 98 .id = SNOR_ID(0xbf, 0x25, 0x04), 99 .name = "sst25wf040", 100 .size = SZ_512K, 101 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 102 .no_sfdp_flags = SECT_4K, 103 .mfr_flags = SST_WRITE, 104 }, { 105 .id = SNOR_ID(0xbf, 0x25, 0x05), 106 .name = "sst25wf080", 107 .size = SZ_1M, 108 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 109 .no_sfdp_flags = SECT_4K, 110 .mfr_flags = SST_WRITE, 111 }, { 112 .id = SNOR_ID(0xbf, 0x25, 0x41), 113 .name = "sst25vf016b", 114 .size = SZ_2M, 115 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 116 .no_sfdp_flags = SECT_4K, 117 .mfr_flags = SST_WRITE, 118 }, { 119 .id = SNOR_ID(0xbf, 0x25, 0x4a), 120 .name = "sst25vf032b", 121 .size = SZ_4M, 122 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 123 .no_sfdp_flags = SECT_4K, 124 .mfr_flags = SST_WRITE, 125 }, { 126 .id = SNOR_ID(0xbf, 0x25, 0x4b), 127 .name = "sst25vf064c", 128 .size = SZ_8M, 129 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_SWP_IS_VOLATILE, 130 .no_sfdp_flags = SECT_4K, 131 }, { 132 .id = SNOR_ID(0xbf, 0x25, 0x8d), 133 .name = "sst25vf040b", 134 .size = SZ_512K, 135 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 136 .no_sfdp_flags = SECT_4K, 137 .mfr_flags = SST_WRITE, 138 }, { 139 .id = SNOR_ID(0xbf, 0x25, 0x8e), 140 .name = "sst25vf080b", 141 .size = SZ_1M, 142 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 143 .no_sfdp_flags = SECT_4K, 144 .mfr_flags = SST_WRITE, 145 }, { 146 .id = SNOR_ID(0xbf, 0x26, 0x41), 147 .name = "sst26vf016b", 148 .size = SZ_2M, 149 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ, 150 }, { 151 .id = SNOR_ID(0xbf, 0x26, 0x42), 152 .name = "sst26vf032b", 153 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 154 .fixups = &sst26vf_nor_fixups, 155 }, { 156 .id = SNOR_ID(0xbf, 0x26, 0x43), 157 .name = "sst26vf064b", 158 .size = SZ_8M, 159 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 160 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 161 .fixups = &sst26vf_nor_fixups, 162 }, { 163 .id = SNOR_ID(0xbf, 0x26, 0x51), 164 .name = "sst26wf016b", 165 .size = SZ_2M, 166 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 167 } 168 }; 169 170 static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, 171 size_t *retlen, const u_char *buf) 172 { 173 struct spi_nor *nor = mtd_to_spi_nor(mtd); 174 size_t actual = 0; 175 int ret; 176 177 dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); 178 179 ret = spi_nor_prep_and_lock(nor); 180 if (ret) 181 return ret; 182 183 ret = spi_nor_write_enable(nor); 184 if (ret) 185 goto out; 186 187 nor->sst_write_second = false; 188 189 /* Start write from odd address. */ 190 if (to % 2) { 191 nor->program_opcode = SPINOR_OP_BP; 192 193 /* write one byte. */ 194 ret = spi_nor_write_data(nor, to, 1, buf); 195 if (ret < 0) 196 goto out; 197 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 198 ret = spi_nor_wait_till_ready(nor); 199 if (ret) 200 goto out; 201 202 to++; 203 actual++; 204 } 205 206 /* Write out most of the data here. */ 207 for (; actual < len - 1; actual += 2) { 208 nor->program_opcode = SPINOR_OP_AAI_WP; 209 210 /* write two bytes. */ 211 ret = spi_nor_write_data(nor, to, 2, buf + actual); 212 if (ret < 0) 213 goto out; 214 WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); 215 ret = spi_nor_wait_till_ready(nor); 216 if (ret) 217 goto out; 218 to += 2; 219 nor->sst_write_second = true; 220 } 221 nor->sst_write_second = false; 222 223 ret = spi_nor_write_disable(nor); 224 if (ret) 225 goto out; 226 227 ret = spi_nor_wait_till_ready(nor); 228 if (ret) 229 goto out; 230 231 /* Write out trailing byte if it exists. */ 232 if (actual != len) { 233 ret = spi_nor_write_enable(nor); 234 if (ret) 235 goto out; 236 237 nor->program_opcode = SPINOR_OP_BP; 238 ret = spi_nor_write_data(nor, to, 1, buf + actual); 239 if (ret < 0) 240 goto out; 241 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 242 ret = spi_nor_wait_till_ready(nor); 243 if (ret) 244 goto out; 245 246 actual += 1; 247 248 ret = spi_nor_write_disable(nor); 249 } 250 out: 251 *retlen += actual; 252 spi_nor_unlock_and_unprep(nor); 253 return ret; 254 } 255 256 static int sst_nor_late_init(struct spi_nor *nor) 257 { 258 if (nor->info->mfr_flags & SST_WRITE) 259 nor->mtd._write = sst_nor_write; 260 261 return 0; 262 } 263 264 static const struct spi_nor_fixups sst_nor_fixups = { 265 .late_init = sst_nor_late_init, 266 }; 267 268 const struct spi_nor_manufacturer spi_nor_sst = { 269 .name = "sst", 270 .parts = sst_nor_parts, 271 .nparts = ARRAY_SIZE(sst_nor_parts), 272 .fixups = &sst_nor_fixups, 273 }; 274