Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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(struct mtd_info *mtd, loff_t to, size_t len,
171			 size_t *retlen, const u_char *buf)
172{
173	struct spi_nor *nor = mtd_to_spi_nor(mtd);
174	size_t actual = 0;
175	int ret;
176
177	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
178
179	ret = spi_nor_prep_and_lock(nor);
180	if (ret)
181		return ret;
182
183	ret = spi_nor_write_enable(nor);
184	if (ret)
185		goto out;
186
187	nor->sst_write_second = false;
188
189	/* Start write from odd address. */
190	if (to % 2) {
191		nor->program_opcode = SPINOR_OP_BP;
192
193		/* write one byte. */
194		ret = spi_nor_write_data(nor, to, 1, buf);
195		if (ret < 0)
196			goto out;
197		WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);
198		ret = spi_nor_wait_till_ready(nor);
199		if (ret)
200			goto out;
201
202		to++;
203		actual++;
204	}
205
206	/* Write out most of the data here. */
207	for (; actual < len - 1; actual += 2) {
208		nor->program_opcode = SPINOR_OP_AAI_WP;
209
210		/* write two bytes. */
211		ret = spi_nor_write_data(nor, to, 2, buf + actual);
212		if (ret < 0)
213			goto out;
214		WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret);
215		ret = spi_nor_wait_till_ready(nor);
216		if (ret)
217			goto out;
218		to += 2;
219		nor->sst_write_second = true;
220	}
221	nor->sst_write_second = false;
222
223	ret = spi_nor_write_disable(nor);
224	if (ret)
225		goto out;
226
227	ret = spi_nor_wait_till_ready(nor);
228	if (ret)
229		goto out;
230
231	/* Write out trailing byte if it exists. */
232	if (actual != len) {
233		ret = spi_nor_write_enable(nor);
234		if (ret)
235			goto out;
236
237		nor->program_opcode = SPINOR_OP_BP;
238		ret = spi_nor_write_data(nor, to, 1, buf + actual);
239		if (ret < 0)
240			goto out;
241		WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);
242		ret = spi_nor_wait_till_ready(nor);
243		if (ret)
244			goto out;
245
246		actual += 1;
247
248		ret = spi_nor_write_disable(nor);
249	}
250out:
251	*retlen += actual;
252	spi_nor_unlock_and_unprep(nor);
253	return ret;
254}
255
256static int sst_nor_late_init(struct spi_nor *nor)
257{
258	if (nor->info->mfr_flags & SST_WRITE)
259		nor->mtd._write = sst_nor_write;
260
261	return 0;
262}
263
264static const struct spi_nor_fixups sst_nor_fixups = {
265	.late_init = sst_nor_late_init,
266};
267
268const struct spi_nor_manufacturer spi_nor_sst = {
269	.name = "sst",
270	.parts = sst_nor_parts,
271	.nparts = ARRAY_SIZE(sst_nor_parts),
272	.fixups = &sst_nor_fixups,
273};