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_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 17 { 18 return -EOPNOTSUPP; 19 } 20 21 static int sst26vf_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_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_locking_ops = { 47 .lock = sst26vf_lock, 48 .unlock = sst26vf_unlock, 49 .is_locked = sst26vf_is_locked, 50 }; 51 52 static void sst26vf_late_init(struct spi_nor *nor) 53 { 54 nor->params->locking_ops = &sst26vf_locking_ops; 55 } 56 57 static const struct spi_nor_fixups sst26vf_fixups = { 58 .late_init = sst26vf_late_init, 59 }; 60 61 static const struct flash_info sst_parts[] = { 62 /* SST -- large erase sizes are "overlays", "sectors" are 4K */ 63 { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8) 64 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 65 NO_SFDP_FLAGS(SECT_4K) 66 MFR_FLAGS(SST_WRITE) }, 67 { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16) 68 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 69 NO_SFDP_FLAGS(SECT_4K) 70 MFR_FLAGS(SST_WRITE) }, 71 { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32) 72 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 73 NO_SFDP_FLAGS(SECT_4K) 74 MFR_FLAGS(SST_WRITE) }, 75 { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64) 76 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 77 NO_SFDP_FLAGS(SECT_4K) 78 MFR_FLAGS(SST_WRITE) }, 79 { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128) 80 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | 81 SPI_NOR_SWP_IS_VOLATILE) 82 NO_SFDP_FLAGS(SECT_4K) }, 83 { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1) 84 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 85 NO_SFDP_FLAGS(SECT_4K) 86 MFR_FLAGS(SST_WRITE) }, 87 { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2) 88 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 89 NO_SFDP_FLAGS(SECT_4K) 90 MFR_FLAGS(SST_WRITE) }, 91 { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4) 92 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 93 NO_SFDP_FLAGS(SECT_4K) 94 MFR_FLAGS(SST_WRITE) }, 95 { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4) 96 FLAGS(SPI_NOR_HAS_LOCK) 97 NO_SFDP_FLAGS(SECT_4K) }, 98 { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8) 99 FLAGS(SPI_NOR_HAS_LOCK) 100 NO_SFDP_FLAGS(SECT_4K) }, 101 { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8) 102 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 103 NO_SFDP_FLAGS(SECT_4K) 104 MFR_FLAGS(SST_WRITE) }, 105 { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16) 106 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 107 NO_SFDP_FLAGS(SECT_4K) 108 MFR_FLAGS(SST_WRITE) }, 109 { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32) 110 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 111 SPI_NOR_QUAD_READ) }, 112 { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32) 113 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, 114 { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128) 115 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 116 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 117 .fixups = &sst26vf_fixups }, 118 }; 119 120 static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, 121 size_t *retlen, const u_char *buf) 122 { 123 struct spi_nor *nor = mtd_to_spi_nor(mtd); 124 size_t actual = 0; 125 int ret; 126 127 dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); 128 129 ret = spi_nor_lock_and_prep(nor); 130 if (ret) 131 return ret; 132 133 ret = spi_nor_write_enable(nor); 134 if (ret) 135 goto out; 136 137 nor->sst_write_second = false; 138 139 /* Start write from odd address. */ 140 if (to % 2) { 141 nor->program_opcode = SPINOR_OP_BP; 142 143 /* write one byte. */ 144 ret = spi_nor_write_data(nor, to, 1, buf); 145 if (ret < 0) 146 goto out; 147 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 148 ret = spi_nor_wait_till_ready(nor); 149 if (ret) 150 goto out; 151 152 to++; 153 actual++; 154 } 155 156 /* Write out most of the data here. */ 157 for (; actual < len - 1; actual += 2) { 158 nor->program_opcode = SPINOR_OP_AAI_WP; 159 160 /* write two bytes. */ 161 ret = spi_nor_write_data(nor, to, 2, buf + actual); 162 if (ret < 0) 163 goto out; 164 WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); 165 ret = spi_nor_wait_till_ready(nor); 166 if (ret) 167 goto out; 168 to += 2; 169 nor->sst_write_second = true; 170 } 171 nor->sst_write_second = false; 172 173 ret = spi_nor_write_disable(nor); 174 if (ret) 175 goto out; 176 177 ret = spi_nor_wait_till_ready(nor); 178 if (ret) 179 goto out; 180 181 /* Write out trailing byte if it exists. */ 182 if (actual != len) { 183 ret = spi_nor_write_enable(nor); 184 if (ret) 185 goto out; 186 187 nor->program_opcode = SPINOR_OP_BP; 188 ret = spi_nor_write_data(nor, to, 1, buf + actual); 189 if (ret < 0) 190 goto out; 191 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 192 ret = spi_nor_wait_till_ready(nor); 193 if (ret) 194 goto out; 195 196 actual += 1; 197 198 ret = spi_nor_write_disable(nor); 199 } 200 out: 201 *retlen += actual; 202 spi_nor_unlock_and_unprep(nor); 203 return ret; 204 } 205 206 static void sst_late_init(struct spi_nor *nor) 207 { 208 if (nor->info->mfr_flags & SST_WRITE) 209 nor->mtd._write = sst_write; 210 } 211 212 static const struct spi_nor_fixups sst_fixups = { 213 .late_init = sst_late_init, 214 }; 215 216 const struct spi_nor_manufacturer spi_nor_sst = { 217 .name = "sst", 218 .parts = sst_parts, 219 .nparts = ARRAY_SIZE(sst_parts), 220 .fixups = &sst_fixups, 221 }; 222