xref: /linux/drivers/mtd/spi-nor/sst.c (revision 8e07e0e3964ca4e23ce7b68e2096fe660a888942)
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, uint64_t len)
17 {
18 	return -EOPNOTSUPP;
19 }
20 
21 static 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 
41 static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t 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(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 	}
250 out:
251 	*retlen += actual;
252 	spi_nor_unlock_and_unprep(nor);
253 	return ret;
254 }
255 
256 static 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 
264 static const struct spi_nor_fixups sst_nor_fixups = {
265 	.late_init = sst_nor_late_init,
266 };
267 
268 const 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