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
sst26vf_nor_lock(struct spi_nor * nor,loff_t ofs,uint64_t len)16 static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17 {
18 return -EOPNOTSUPP;
19 }
20
sst26vf_nor_unlock(struct spi_nor * nor,loff_t ofs,uint64_t len)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
sst26vf_nor_is_locked(struct spi_nor * nor,loff_t ofs,uint64_t len)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
sst26vf_nor_late_init(struct spi_nor * nor)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
sst_nor_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)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
sst_nor_late_init(struct spi_nor * nor)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