1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2017 exceet electronics GmbH
4 *
5 * Authors:
6 * Frieder Schrempf <[email protected]>
7 * Boris Brezillon <[email protected]>
8 */
9
10 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #include <linux/mtd/spinand.h>
13 #include <linux/units.h>
14
15 #define SPINAND_MFR_WINBOND 0xEF
16
17 #define WINBOND_CFG_BUF_READ BIT(3)
18
19 #define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
20
21 /*
22 * "X2" in the core is equivalent to "dual output" in the datasheets,
23 * "X4" in the core is equivalent to "quad output" in the datasheets.
24 */
25
26 static SPINAND_OP_VARIANTS(read_cache_dtr_variants,
27 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_DTR_OP(0, 8, NULL, 0, 80 * HZ_PER_MHZ),
28 SPINAND_PAGE_READ_FROM_CACHE_X4_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
29 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
30 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
31 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_DTR_OP(0, 4, NULL, 0, 80 * HZ_PER_MHZ),
32 SPINAND_PAGE_READ_FROM_CACHE_X2_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
33 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
34 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
35 SPINAND_PAGE_READ_FROM_CACHE_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
36 SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0),
37 SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0, 54 * HZ_PER_MHZ));
38
39 static SPINAND_OP_VARIANTS(read_cache_variants,
40 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
41 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
42 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
43 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
44 SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0),
45 SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0));
46
47 static SPINAND_OP_VARIANTS(write_cache_variants,
48 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
49 SPINAND_PROG_LOAD(true, 0, NULL, 0));
50
51 static SPINAND_OP_VARIANTS(update_cache_variants,
52 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
53 SPINAND_PROG_LOAD(false, 0, NULL, 0));
54
w25m02gv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)55 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
56 struct mtd_oob_region *region)
57 {
58 if (section > 3)
59 return -ERANGE;
60
61 region->offset = (16 * section) + 8;
62 region->length = 8;
63
64 return 0;
65 }
66
w25m02gv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)67 static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
68 struct mtd_oob_region *region)
69 {
70 if (section > 3)
71 return -ERANGE;
72
73 region->offset = (16 * section) + 2;
74 region->length = 6;
75
76 return 0;
77 }
78
79 static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
80 .ecc = w25m02gv_ooblayout_ecc,
81 .free = w25m02gv_ooblayout_free,
82 };
83
w25m02gv_select_target(struct spinand_device * spinand,unsigned int target)84 static int w25m02gv_select_target(struct spinand_device *spinand,
85 unsigned int target)
86 {
87 struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
88 SPI_MEM_OP_NO_ADDR,
89 SPI_MEM_OP_NO_DUMMY,
90 SPI_MEM_OP_DATA_OUT(1,
91 spinand->scratchbuf,
92 1));
93
94 *spinand->scratchbuf = target;
95 return spi_mem_exec_op(spinand->spimem, &op);
96 }
97
w25n01kv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)98 static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section,
99 struct mtd_oob_region *region)
100 {
101 if (section > 3)
102 return -ERANGE;
103
104 region->offset = 64 + (8 * section);
105 region->length = 7;
106
107 return 0;
108 }
109
w25n02kv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)110 static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
111 struct mtd_oob_region *region)
112 {
113 if (section > 3)
114 return -ERANGE;
115
116 region->offset = 64 + (16 * section);
117 region->length = 13;
118
119 return 0;
120 }
121
w25n02kv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)122 static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
123 struct mtd_oob_region *region)
124 {
125 if (section > 3)
126 return -ERANGE;
127
128 region->offset = (16 * section) + 2;
129 region->length = 14;
130
131 return 0;
132 }
133
134 static const struct mtd_ooblayout_ops w25n01kv_ooblayout = {
135 .ecc = w25n01kv_ooblayout_ecc,
136 .free = w25n02kv_ooblayout_free,
137 };
138
139 static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
140 .ecc = w25n02kv_ooblayout_ecc,
141 .free = w25n02kv_ooblayout_free,
142 };
143
w25n02kv_ecc_get_status(struct spinand_device * spinand,u8 status)144 static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
145 u8 status)
146 {
147 struct nand_device *nand = spinand_to_nand(spinand);
148 u8 mbf = 0;
149 struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf);
150
151 switch (status & STATUS_ECC_MASK) {
152 case STATUS_ECC_NO_BITFLIPS:
153 return 0;
154
155 case STATUS_ECC_UNCOR_ERROR:
156 return -EBADMSG;
157
158 case STATUS_ECC_HAS_BITFLIPS:
159 case W25N04KV_STATUS_ECC_5_8_BITFLIPS:
160 /*
161 * Let's try to retrieve the real maximum number of bitflips
162 * in order to avoid forcing the wear-leveling layer to move
163 * data around if it's not necessary.
164 */
165 if (spi_mem_exec_op(spinand->spimem, &op))
166 return nanddev_get_ecc_conf(nand)->strength;
167
168 mbf = *(spinand->scratchbuf) >> 4;
169
170 if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
171 return nanddev_get_ecc_conf(nand)->strength;
172
173 return mbf;
174
175 default:
176 break;
177 }
178
179 return -EINVAL;
180 }
181
182 static const struct spinand_info winbond_spinand_table[] = {
183 /* 512M-bit densities */
184 SPINAND_INFO("W25N512GW", /* 1.8V */
185 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x20),
186 NAND_MEMORG(1, 2048, 64, 64, 512, 10, 1, 1, 1),
187 NAND_ECCREQ(1, 512),
188 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
189 &write_cache_variants,
190 &update_cache_variants),
191 0,
192 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
193 /* 1G-bit densities */
194 SPINAND_INFO("W25N01GV", /* 3.3V */
195 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
196 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
197 NAND_ECCREQ(1, 512),
198 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
199 &write_cache_variants,
200 &update_cache_variants),
201 0,
202 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
203 SPINAND_INFO("W25N01GW", /* 1.8V */
204 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x21),
205 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
206 NAND_ECCREQ(1, 512),
207 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
208 &write_cache_variants,
209 &update_cache_variants),
210 0,
211 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
212 SPINAND_INFO("W25N01JW", /* high-speed 1.8V */
213 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
214 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
215 NAND_ECCREQ(1, 512),
216 SPINAND_INFO_OP_VARIANTS(&read_cache_dtr_variants,
217 &write_cache_variants,
218 &update_cache_variants),
219 0,
220 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
221 SPINAND_INFO("W25N01KV", /* 3.3V */
222 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
223 NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
224 NAND_ECCREQ(4, 512),
225 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
226 &write_cache_variants,
227 &update_cache_variants),
228 0,
229 SPINAND_ECCINFO(&w25n01kv_ooblayout, w25n02kv_ecc_get_status)),
230 /* 2G-bit densities */
231 SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
232 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
233 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
234 NAND_ECCREQ(1, 512),
235 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
236 &write_cache_variants,
237 &update_cache_variants),
238 0,
239 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
240 SPINAND_SELECT_TARGET(w25m02gv_select_target)),
241 SPINAND_INFO("W25N02JW", /* high-speed 1.8V */
242 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
243 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
244 NAND_ECCREQ(1, 512),
245 SPINAND_INFO_OP_VARIANTS(&read_cache_dtr_variants,
246 &write_cache_variants,
247 &update_cache_variants),
248 0,
249 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
250 SPINAND_INFO("W25N02KV", /* 3.3V */
251 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
252 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
253 NAND_ECCREQ(8, 512),
254 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
255 &write_cache_variants,
256 &update_cache_variants),
257 0,
258 SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
259 SPINAND_INFO("W25N02KW", /* 1.8V */
260 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x22),
261 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
262 NAND_ECCREQ(8, 512),
263 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
264 &write_cache_variants,
265 &update_cache_variants),
266 0,
267 SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
268 /* 4G-bit densities */
269 SPINAND_INFO("W25N04KV", /* 3.3V */
270 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
271 NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1),
272 NAND_ECCREQ(8, 512),
273 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
274 &write_cache_variants,
275 &update_cache_variants),
276 0,
277 SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
278 SPINAND_INFO("W25N04KW", /* 1.8V */
279 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x23),
280 NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 1, 1, 1),
281 NAND_ECCREQ(8, 512),
282 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
283 &write_cache_variants,
284 &update_cache_variants),
285 0,
286 SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
287 };
288
winbond_spinand_init(struct spinand_device * spinand)289 static int winbond_spinand_init(struct spinand_device *spinand)
290 {
291 struct nand_device *nand = spinand_to_nand(spinand);
292 unsigned int i;
293
294 /*
295 * Make sure all dies are in buffer read mode and not continuous read
296 * mode.
297 */
298 for (i = 0; i < nand->memorg.ntargets; i++) {
299 spinand_select_target(spinand, i);
300 spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
301 WINBOND_CFG_BUF_READ);
302 }
303
304 return 0;
305 }
306
307 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
308 .init = winbond_spinand_init,
309 };
310
311 const struct spinand_manufacturer winbond_spinand_manufacturer = {
312 .id = SPINAND_MFR_WINBOND,
313 .name = "Winbond",
314 .chips = winbond_spinand_table,
315 .nchips = ARRAY_SIZE(winbond_spinand_table),
316 .ops = &winbond_spinand_manuf_ops,
317 };
318