GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / mtd / spi-nor / sst.c
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 };