xref: /linux/drivers/mtd/spi-nor/sst.c (revision bde5d79d00255db609fe9d859eef8c7b6d38b137)
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, 1, 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 		/* 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 	}
249 out:
250 	*retlen += actual;
251 	spi_nor_unlock_and_unprep(nor);
252 	return ret;
253 }
254 
255 static 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 
263 static const struct spi_nor_fixups sst_nor_fixups = {
264 	.late_init = sst_nor_late_init,
265 };
266 
267 const 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 };
273