xref: /linux/drivers/mtd/spi-nor/sst.c (revision 45bd2d77fbedec862204bb5c0fcaba2b7fa5fb56)
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 
16 static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
17 {
18 	return -EOPNOTSUPP;
19 }
20 
21 static 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 
41 static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
42 {
43 	return -EOPNOTSUPP;
44 }
45 
46 static 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 
52 static 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 
59 static const struct spi_nor_fixups sst26vf_nor_fixups = {
60 	.late_init = sst26vf_nor_late_init,
61 };
62 
63 static 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 
170 static 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 
185 static 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 		bool needs_write_enable = (len > 1);
207 
208 		/* write one byte. */
209 		ret = sst_nor_write_data(nor, to, 1, buf);
210 		if (ret < 0)
211 			goto out;
212 
213 		to++;
214 		actual++;
215 
216 		/*
217 		 * Byte program clears the write enable latch. If more
218 		 * data needs to be written using the AAI sequence,
219 		 * re-enable writes.
220 		 */
221 		if (needs_write_enable) {
222 			ret = spi_nor_write_enable(nor);
223 			if (ret)
224 				goto out;
225 		}
226 	}
227 
228 	/* Write out most of the data here. */
229 	for (; actual < len - 1; actual += 2) {
230 		/* write two bytes. */
231 		ret = sst_nor_write_data(nor, to, 2, buf + actual);
232 		if (ret < 0)
233 			goto out;
234 
235 		to += 2;
236 		nor->sst_write_second = true;
237 	}
238 	nor->sst_write_second = false;
239 
240 	ret = spi_nor_write_disable(nor);
241 	if (ret)
242 		goto out;
243 
244 	ret = spi_nor_wait_till_ready(nor);
245 	if (ret)
246 		goto out;
247 
248 	/* Write out trailing byte if it exists. */
249 	if (actual != len) {
250 		ret = spi_nor_write_enable(nor);
251 		if (ret)
252 			goto out;
253 
254 		ret = sst_nor_write_data(nor, to, 1, buf + actual);
255 		if (ret < 0)
256 			goto out;
257 
258 		actual += 1;
259 
260 		ret = spi_nor_write_disable(nor);
261 	}
262 out:
263 	*retlen += actual;
264 	spi_nor_unlock_and_unprep(nor);
265 	return ret;
266 }
267 
268 static int sst_nor_late_init(struct spi_nor *nor)
269 {
270 	if (nor->info->mfr_flags & SST_WRITE)
271 		nor->mtd._write = sst_nor_write;
272 
273 	return 0;
274 }
275 
276 static const struct spi_nor_fixups sst_nor_fixups = {
277 	.late_init = sst_nor_late_init,
278 };
279 
280 const struct spi_nor_manufacturer spi_nor_sst = {
281 	.name = "sst",
282 	.parts = sst_nor_parts,
283 	.nparts = ARRAY_SIZE(sst_nor_parts),
284 	.fixups = &sst_nor_fixups,
285 };
286