xref: /linux/drivers/mtd/spi-nor/sst.c (revision db10cb9b)
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 	/* 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 
126 static 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 	}
206 out:
207 	*retlen += actual;
208 	spi_nor_unlock_and_unprep(nor);
209 	return ret;
210 }
211 
212 static 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 
220 static const struct spi_nor_fixups sst_nor_fixups = {
221 	.late_init = sst_nor_late_init,
222 };
223 
224 const 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