File Index
Files and directories
Sign in
FILE

sst.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/spi-nor/sst.c C
  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, u64 len)
 17{
 18	return -EOPNOTSUPP;
 19}
 20
 21static 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
 41static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 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 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
 59static const struct spi_nor_fixups sst26vf_nor_fixups = {
 60	.late_init = sst26vf_nor_late_init,
 61};
 62
 63static 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
170static 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, len, 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
185static 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	}
249out:
250	*retlen += actual;
251	spi_nor_unlock_and_unprep(nor);
252	return ret;
253}
254
255static 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
263static const struct spi_nor_fixups sst_nor_fixups = {
264	.late_init = sst_nor_late_init,
265};
266
267const 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};
~/ Read-only file preview.