09fdc7023e09a09737e64ecf35d3cfd982e197d7
[linux-2.6-microblaze.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 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         { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128)
117                 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
118                 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
119                 .fixups = &sst26vf_nor_fixups },
120 };
121
122 static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
123                          size_t *retlen, const u_char *buf)
124 {
125         struct spi_nor *nor = mtd_to_spi_nor(mtd);
126         size_t actual = 0;
127         int ret;
128
129         dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
130
131         ret = spi_nor_prep_and_lock(nor);
132         if (ret)
133                 return ret;
134
135         ret = spi_nor_write_enable(nor);
136         if (ret)
137                 goto out;
138
139         nor->sst_write_second = false;
140
141         /* Start write from odd address. */
142         if (to % 2) {
143                 nor->program_opcode = SPINOR_OP_BP;
144
145                 /* write one byte. */
146                 ret = spi_nor_write_data(nor, to, 1, buf);
147                 if (ret < 0)
148                         goto out;
149                 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);
150                 ret = spi_nor_wait_till_ready(nor);
151                 if (ret)
152                         goto out;
153
154                 to++;
155                 actual++;
156         }
157
158         /* Write out most of the data here. */
159         for (; actual < len - 1; actual += 2) {
160                 nor->program_opcode = SPINOR_OP_AAI_WP;
161
162                 /* write two bytes. */
163                 ret = spi_nor_write_data(nor, to, 2, buf + actual);
164                 if (ret < 0)
165                         goto out;
166                 WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret);
167                 ret = spi_nor_wait_till_ready(nor);
168                 if (ret)
169                         goto out;
170                 to += 2;
171                 nor->sst_write_second = true;
172         }
173         nor->sst_write_second = false;
174
175         ret = spi_nor_write_disable(nor);
176         if (ret)
177                 goto out;
178
179         ret = spi_nor_wait_till_ready(nor);
180         if (ret)
181                 goto out;
182
183         /* Write out trailing byte if it exists. */
184         if (actual != len) {
185                 ret = spi_nor_write_enable(nor);
186                 if (ret)
187                         goto out;
188
189                 nor->program_opcode = SPINOR_OP_BP;
190                 ret = spi_nor_write_data(nor, to, 1, buf + actual);
191                 if (ret < 0)
192                         goto out;
193                 WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);
194                 ret = spi_nor_wait_till_ready(nor);
195                 if (ret)
196                         goto out;
197
198                 actual += 1;
199
200                 ret = spi_nor_write_disable(nor);
201         }
202 out:
203         *retlen += actual;
204         spi_nor_unlock_and_unprep(nor);
205         return ret;
206 }
207
208 static int sst_nor_late_init(struct spi_nor *nor)
209 {
210         if (nor->info->mfr_flags & SST_WRITE)
211                 nor->mtd._write = sst_nor_write;
212
213         return 0;
214 }
215
216 static const struct spi_nor_fixups sst_nor_fixups = {
217         .late_init = sst_nor_late_init,
218 };
219
220 const struct spi_nor_manufacturer spi_nor_sst = {
221         .name = "sst",
222         .parts = sst_nor_parts,
223         .nparts = ARRAY_SIZE(sst_nor_parts),
224         .fixups = &sst_nor_fixups,
225 };