sst.c (5850B)
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 16static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 17{ 18 return -EOPNOTSUPP; 19} 20 21static 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 41static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) 42{ 43 return -EOPNOTSUPP; 44} 45 46static 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 52static void sst26vf_nor_late_init(struct spi_nor *nor) 53{ 54 nor->params->locking_ops = &sst26vf_nor_locking_ops; 55} 56 57static const struct spi_nor_fixups sst26vf_nor_fixups = { 58 .late_init = sst26vf_nor_late_init, 59}; 60 61static const struct flash_info sst_nor_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_nor_fixups }, 118}; 119 120static int sst_nor_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 } 200out: 201 *retlen += actual; 202 spi_nor_unlock_and_unprep(nor); 203 return ret; 204} 205 206static void sst_nor_late_init(struct spi_nor *nor) 207{ 208 if (nor->info->mfr_flags & SST_WRITE) 209 nor->mtd._write = sst_nor_write; 210} 211 212static const struct spi_nor_fixups sst_nor_fixups = { 213 .late_init = sst_nor_late_init, 214}; 215 216const struct spi_nor_manufacturer spi_nor_sst = { 217 .name = "sst", 218 .parts = sst_nor_parts, 219 .nparts = ARRAY_SIZE(sst_nor_parts), 220 .fixups = &sst_nor_fixups, 221};