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