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, cr: 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, len: 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, len: 2, buf: 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, len: 1, buf: 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 | |