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};
274