162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005, Intec Automation Inc. 462306a36Sopenharmony_ci * Copyright (C) 2014, Freescale Semiconductor, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/mtd/spi-nor.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "core.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* SST flash_info mfr_flag. Used to specify SST byte programming. */ 1262306a36Sopenharmony_ci#define SST_WRITE BIT(0) 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define SST26VF_CR_BPNV BIT(3) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return -EOPNOTSUPP; 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int ret; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* We only support unlocking the entire flash array. */ 2662306a36Sopenharmony_ci if (ofs != 0 || len != nor->params->size) 2762306a36Sopenharmony_ci return -EINVAL; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci ret = spi_nor_read_cr(nor, nor->bouncebuf); 3062306a36Sopenharmony_ci if (ret) 3162306a36Sopenharmony_ci return ret; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) { 3462306a36Sopenharmony_ci dev_dbg(nor->dev, "Any block has been permanently locked\n"); 3562306a36Sopenharmony_ci return -EINVAL; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return spi_nor_global_block_unlock(nor); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return -EOPNOTSUPP; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const struct spi_nor_locking_ops sst26vf_nor_locking_ops = { 4762306a36Sopenharmony_ci .lock = sst26vf_nor_lock, 4862306a36Sopenharmony_ci .unlock = sst26vf_nor_unlock, 4962306a36Sopenharmony_ci .is_locked = sst26vf_nor_is_locked, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int sst26vf_nor_late_init(struct spi_nor *nor) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci nor->params->locking_ops = &sst26vf_nor_locking_ops; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const struct spi_nor_fixups sst26vf_nor_fixups = { 6062306a36Sopenharmony_ci .late_init = sst26vf_nor_late_init, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct flash_info sst_nor_parts[] = { 6462306a36Sopenharmony_ci /* SST -- large erase sizes are "overlays", "sectors" are 4K */ 6562306a36Sopenharmony_ci { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8) 6662306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 6762306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 6862306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 6962306a36Sopenharmony_ci { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16) 7062306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 7162306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 7262306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 7362306a36Sopenharmony_ci { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32) 7462306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 7562306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 7662306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 7762306a36Sopenharmony_ci { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64) 7862306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 7962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 8062306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 8162306a36Sopenharmony_ci { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128) 8262306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | 8362306a36Sopenharmony_ci SPI_NOR_SWP_IS_VOLATILE) 8462306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) }, 8562306a36Sopenharmony_ci { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1) 8662306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 8762306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 8862306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 8962306a36Sopenharmony_ci { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2) 9062306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 9162306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 9262306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 9362306a36Sopenharmony_ci { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4) 9462306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 9562306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 9662306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 9762306a36Sopenharmony_ci { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4) 9862306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK) 9962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) }, 10062306a36Sopenharmony_ci { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8) 10162306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK) 10262306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) }, 10362306a36Sopenharmony_ci { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8) 10462306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 10562306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 10662306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 10762306a36Sopenharmony_ci { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16) 10862306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 10962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) 11062306a36Sopenharmony_ci MFR_FLAGS(SST_WRITE) }, 11162306a36Sopenharmony_ci { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32) 11262306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 11362306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 11462306a36Sopenharmony_ci { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32) 11562306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, 11662306a36Sopenharmony_ci { "sst26vf032b", INFO(0xbf2642, 0, 0, 0) 11762306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 11862306a36Sopenharmony_ci PARSE_SFDP 11962306a36Sopenharmony_ci .fixups = &sst26vf_nor_fixups }, 12062306a36Sopenharmony_ci { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128) 12162306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 12262306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 12362306a36Sopenharmony_ci .fixups = &sst26vf_nor_fixups }, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, 12762306a36Sopenharmony_ci size_t *retlen, const u_char *buf) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct spi_nor *nor = mtd_to_spi_nor(mtd); 13062306a36Sopenharmony_ci size_t actual = 0; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ret = spi_nor_prep_and_lock(nor); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = spi_nor_write_enable(nor); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci nor->sst_write_second = false; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Start write from odd address. */ 14662306a36Sopenharmony_ci if (to % 2) { 14762306a36Sopenharmony_ci nor->program_opcode = SPINOR_OP_BP; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* write one byte. */ 15062306a36Sopenharmony_ci ret = spi_nor_write_data(nor, to, 1, buf); 15162306a36Sopenharmony_ci if (ret < 0) 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 15462306a36Sopenharmony_ci ret = spi_nor_wait_till_ready(nor); 15562306a36Sopenharmony_ci if (ret) 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci to++; 15962306a36Sopenharmony_ci actual++; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Write out most of the data here. */ 16362306a36Sopenharmony_ci for (; actual < len - 1; actual += 2) { 16462306a36Sopenharmony_ci nor->program_opcode = SPINOR_OP_AAI_WP; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* write two bytes. */ 16762306a36Sopenharmony_ci ret = spi_nor_write_data(nor, to, 2, buf + actual); 16862306a36Sopenharmony_ci if (ret < 0) 16962306a36Sopenharmony_ci goto out; 17062306a36Sopenharmony_ci WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); 17162306a36Sopenharmony_ci ret = spi_nor_wait_till_ready(nor); 17262306a36Sopenharmony_ci if (ret) 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci to += 2; 17562306a36Sopenharmony_ci nor->sst_write_second = true; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci nor->sst_write_second = false; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = spi_nor_write_disable(nor); 18062306a36Sopenharmony_ci if (ret) 18162306a36Sopenharmony_ci goto out; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = spi_nor_wait_till_ready(nor); 18462306a36Sopenharmony_ci if (ret) 18562306a36Sopenharmony_ci goto out; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Write out trailing byte if it exists. */ 18862306a36Sopenharmony_ci if (actual != len) { 18962306a36Sopenharmony_ci ret = spi_nor_write_enable(nor); 19062306a36Sopenharmony_ci if (ret) 19162306a36Sopenharmony_ci goto out; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci nor->program_opcode = SPINOR_OP_BP; 19462306a36Sopenharmony_ci ret = spi_nor_write_data(nor, to, 1, buf + actual); 19562306a36Sopenharmony_ci if (ret < 0) 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); 19862306a36Sopenharmony_ci ret = spi_nor_wait_till_ready(nor); 19962306a36Sopenharmony_ci if (ret) 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci actual += 1; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ret = spi_nor_write_disable(nor); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ciout: 20762306a36Sopenharmony_ci *retlen += actual; 20862306a36Sopenharmony_ci spi_nor_unlock_and_unprep(nor); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int sst_nor_late_init(struct spi_nor *nor) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci if (nor->info->mfr_flags & SST_WRITE) 21562306a36Sopenharmony_ci nor->mtd._write = sst_nor_write; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic const struct spi_nor_fixups sst_nor_fixups = { 22162306a36Sopenharmony_ci .late_init = sst_nor_late_init, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciconst struct spi_nor_manufacturer spi_nor_sst = { 22562306a36Sopenharmony_ci .name = "sst", 22662306a36Sopenharmony_ci .parts = sst_nor_parts, 22762306a36Sopenharmony_ci .nparts = ARRAY_SIZE(sst_nor_parts), 22862306a36Sopenharmony_ci .fixups = &sst_nor_fixups, 22962306a36Sopenharmony_ci}; 230