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, u64 len) 17 { 18 return -EOPNOTSUPP; 19 } 20 21 static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 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, u64 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_data(struct spi_nor *nor, loff_t to, size_t len, 171 const u_char *buf) 172 { 173 u8 op = (len == 1) ? SPINOR_OP_BP : SPINOR_OP_AAI_WP; 174 int ret; 175 176 nor->program_opcode = op; 177 ret = spi_nor_write_data(nor, to, 1, buf); 178 if (ret < 0) 179 return ret; 180 WARN(ret != len, "While writing %zu byte written %i bytes\n", len, ret); 181 182 return spi_nor_wait_till_ready(nor); 183 } 184 185 static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, 186 size_t *retlen, const u_char *buf) 187 { 188 struct spi_nor *nor = mtd_to_spi_nor(mtd); 189 size_t actual = 0; 190 int ret; 191 192 dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); 193 194 ret = spi_nor_prep_and_lock(nor); 195 if (ret) 196 return ret; 197 198 ret = spi_nor_write_enable(nor); 199 if (ret) 200 goto out; 201 202 nor->sst_write_second = false; 203 204 /* Start write from odd address. */ 205 if (to % 2) { 206 /* write one byte. */ 207 ret = sst_nor_write_data(nor, to, 1, buf); 208 if (ret < 0) 209 goto out; 210 211 to++; 212 actual++; 213 } 214 215 /* Write out most of the data here. */ 216 for (; actual < len - 1; actual += 2) { 217 /* write two bytes. */ 218 ret = sst_nor_write_data(nor, to, 2, buf + actual); 219 if (ret < 0) 220 goto out; 221 222 to += 2; 223 nor->sst_write_second = true; 224 } 225 nor->sst_write_second = false; 226 227 ret = spi_nor_write_disable(nor); 228 if (ret) 229 goto out; 230 231 ret = spi_nor_wait_till_ready(nor); 232 if (ret) 233 goto out; 234 235 /* Write out trailing byte if it exists. */ 236 if (actual != len) { 237 ret = spi_nor_write_enable(nor); 238 if (ret) 239 goto out; 240 241 ret = sst_nor_write_data(nor, to, 1, buf + actual); 242 if (ret < 0) 243 goto out; 244 245 actual += 1; 246 247 ret = spi_nor_write_disable(nor); 248 } 249 out: 250 *retlen += actual; 251 spi_nor_unlock_and_unprep(nor); 252 return ret; 253 } 254 255 static int sst_nor_late_init(struct spi_nor *nor) 256 { 257 if (nor->info->mfr_flags & SST_WRITE) 258 nor->mtd._write = sst_nor_write; 259 260 return 0; 261 } 262 263 static const struct spi_nor_fixups sst_nor_fixups = { 264 .late_init = sst_nor_late_init, 265 }; 266 267 const struct spi_nor_manufacturer spi_nor_sst = { 268 .name = "sst", 269 .parts = sst_nor_parts, 270 .nparts = ARRAY_SIZE(sst_nor_parts), 271 .fixups = &sst_nor_fixups, 272 }; 273