xref: /kernel/linux/linux-6.6/drivers/mtd/spi-nor/sst.c (revision 62306a36)
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, uint64_t len)
17{
18	return -EOPNOTSUPP;
19}
20
21static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t 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, uint64_t 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	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
65	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8)
66		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
67		NO_SFDP_FLAGS(SECT_4K)
68		MFR_FLAGS(SST_WRITE) },
69	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16)
70		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
71		NO_SFDP_FLAGS(SECT_4K)
72		MFR_FLAGS(SST_WRITE) },
73	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32)
74		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
75		NO_SFDP_FLAGS(SECT_4K)
76		MFR_FLAGS(SST_WRITE) },
77	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64)
78		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
79		NO_SFDP_FLAGS(SECT_4K)
80		MFR_FLAGS(SST_WRITE) },
81	{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128)
82		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP |
83		      SPI_NOR_SWP_IS_VOLATILE)
84		NO_SFDP_FLAGS(SECT_4K) },
85	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1)
86		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
87		NO_SFDP_FLAGS(SECT_4K)
88		MFR_FLAGS(SST_WRITE) },
89	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2)
90		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
91		NO_SFDP_FLAGS(SECT_4K)
92		MFR_FLAGS(SST_WRITE) },
93	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4)
94		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
95		NO_SFDP_FLAGS(SECT_4K)
96		MFR_FLAGS(SST_WRITE) },
97	{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024,  4)
98		FLAGS(SPI_NOR_HAS_LOCK)
99		NO_SFDP_FLAGS(SECT_4K) },
100	{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8)
101		FLAGS(SPI_NOR_HAS_LOCK)
102		NO_SFDP_FLAGS(SECT_4K) },
103	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8)
104		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
105		NO_SFDP_FLAGS(SECT_4K)
106		MFR_FLAGS(SST_WRITE) },
107	{ "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16)
108		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
109		NO_SFDP_FLAGS(SECT_4K)
110		MFR_FLAGS(SST_WRITE) },
111	{ "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32)
112		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
113			      SPI_NOR_QUAD_READ) },
114	{ "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32)
115		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
116	{ "sst26vf032b", INFO(0xbf2642, 0, 0, 0)
117		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
118		PARSE_SFDP
119		.fixups = &sst26vf_nor_fixups },
120	{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128)
121		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
122		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
123		.fixups = &sst26vf_nor_fixups },
124};
125
126static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
127			 size_t *retlen, const u_char *buf)
128{
129	struct spi_nor *nor = mtd_to_spi_nor(mtd);
130	size_t actual = 0;
131	int ret;
132
133	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
134
135	ret = spi_nor_prep_and_lock(nor);
136	if (ret)
137		return ret;
138
139	ret = spi_nor_write_enable(nor);
140	if (ret)
141		goto out;
142
143	nor->sst_write_second = false;
144
145	/* Start write from odd address. */
146	if (to % 2) {
147		nor->program_opcode = SPINOR_OP_BP;
148
149		/* write one byte. */
150		ret = spi_nor_write_data(nor, to, 1, buf);
151		if (ret < 0)
152			goto out;
153		WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);
154		ret = spi_nor_wait_till_ready(nor);
155		if (ret)
156			goto out;
157
158		to++;
159		actual++;
160	}
161
162	/* Write out most of the data here. */
163	for (; actual < len - 1; actual += 2) {
164		nor->program_opcode = SPINOR_OP_AAI_WP;
165
166		/* write two bytes. */
167		ret = spi_nor_write_data(nor, to, 2, buf + actual);
168		if (ret < 0)
169			goto out;
170		WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret);
171		ret = spi_nor_wait_till_ready(nor);
172		if (ret)
173			goto out;
174		to += 2;
175		nor->sst_write_second = true;
176	}
177	nor->sst_write_second = false;
178
179	ret = spi_nor_write_disable(nor);
180	if (ret)
181		goto out;
182
183	ret = spi_nor_wait_till_ready(nor);
184	if (ret)
185		goto out;
186
187	/* Write out trailing byte if it exists. */
188	if (actual != len) {
189		ret = spi_nor_write_enable(nor);
190		if (ret)
191			goto out;
192
193		nor->program_opcode = SPINOR_OP_BP;
194		ret = spi_nor_write_data(nor, to, 1, buf + actual);
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		actual += 1;
203
204		ret = spi_nor_write_disable(nor);
205	}
206out:
207	*retlen += actual;
208	spi_nor_unlock_and_unprep(nor);
209	return ret;
210}
211
212static int sst_nor_late_init(struct spi_nor *nor)
213{
214	if (nor->info->mfr_flags & SST_WRITE)
215		nor->mtd._write = sst_nor_write;
216
217	return 0;
218}
219
220static const struct spi_nor_fixups sst_nor_fixups = {
221	.late_init = sst_nor_late_init,
222};
223
224const struct spi_nor_manufacturer spi_nor_sst = {
225	.name = "sst",
226	.parts = sst_nor_parts,
227	.nparts = ARRAY_SIZE(sst_nor_parts),
228	.fixups = &sst_nor_fixups,
229};
230