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