xref: /rk3399_rockchip-uboot/drivers/mtd/nand/spi/winbond.c (revision 302a7e190eee187f9f86de259761514a2e92ff5e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2017 exceet electronics GmbH
4  *
5  * Authors:
6  *	Frieder Schrempf <frieder.schrempf@exceet.de>
7  *	Boris Brezillon <boris.brezillon@bootlin.com>
8  */
9 
10 #ifndef __UBOOT__
11 #include <linux/device.h>
12 #include <linux/kernel.h>
13 #endif
14 #include <linux/mtd/spinand.h>
15 
16 #define SPINAND_MFR_WINBOND		0xEF
17 
18 #define WINBOND_CFG_BUF_READ		BIT(3)
19 
20 static SPINAND_OP_VARIANTS(read_cache_variants,
21 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
22 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
23 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
24 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
25 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
26 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
27 
28 #ifdef CONFIG_SPI_NAND_WINBOND_CONT_READ
29 static SPINAND_OP_VARIANTS(read_cache_variants_cont,
30 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
31 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
32 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
33 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
34 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
35 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
36 #endif
37 
38 static SPINAND_OP_VARIANTS(write_cache_variants,
39 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
40 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
41 
42 static SPINAND_OP_VARIANTS(update_cache_variants,
43 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
44 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
45 
w25m02gv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)46 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
47 				  struct mtd_oob_region *region)
48 {
49 	if (section > 3)
50 		return -ERANGE;
51 
52 	region->offset = (16 * section) + 8;
53 	region->length = 8;
54 
55 	return 0;
56 }
57 
w25m02gv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)58 static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
59 				   struct mtd_oob_region *region)
60 {
61 	if (section > 3)
62 		return -ERANGE;
63 
64 	region->offset = (16 * section) + 2;
65 	region->length = 6;
66 
67 	return 0;
68 }
69 
70 static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
71 	.ecc = w25m02gv_ooblayout_ecc,
72 	.rfree = w25m02gv_ooblayout_free,
73 };
74 
w25m02gv_select_target(struct spinand_device * spinand,unsigned int target)75 static int w25m02gv_select_target(struct spinand_device *spinand,
76 				  unsigned int target)
77 {
78 	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
79 					  SPI_MEM_OP_NO_ADDR,
80 					  SPI_MEM_OP_NO_DUMMY,
81 					  SPI_MEM_OP_DATA_OUT(1,
82 							spinand->scratchbuf,
83 							1));
84 
85 	*spinand->scratchbuf = target;
86 	return spi_mem_exec_op(spinand->slave, &op);
87 }
88 
w25n02kv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)89 static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
90 				  struct mtd_oob_region *region)
91 {
92 	if (section)
93 		return -ERANGE;
94 
95 	region->offset = 64;
96 	region->length = 64;
97 
98 	return 0;
99 }
100 
w25n02kv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)101 static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
102 				   struct mtd_oob_region *region)
103 {
104 	if (section)
105 		return -ERANGE;
106 
107 	/* Reserve 2 bytes for the BBM. */
108 	region->offset = 2;
109 	region->length = 62;
110 
111 	return 0;
112 }
113 
114 static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
115 	.ecc = w25n02kv_ooblayout_ecc,
116 	.rfree = w25n02kv_ooblayout_free,
117 };
118 
w25n02kv_ecc_get_status(struct spinand_device * spinand,u8 status)119 static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
120 				   u8 status)
121 {
122 	struct nand_device *nand = spinand_to_nand(spinand);
123 
124 	switch (status & STATUS_ECC_MASK) {
125 	case STATUS_ECC_NO_BITFLIPS:
126 		return 0;
127 
128 	case STATUS_ECC_UNCOR_ERROR:
129 		return -EBADMSG;
130 
131 	case STATUS_ECC_HAS_BITFLIPS:
132 		return 1;
133 
134 	default:
135 		return nand->eccreq.strength;
136 	}
137 
138 	return -EINVAL;
139 }
140 
w25n04lw_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)141 static int w25n04lw_ooblayout_ecc(struct mtd_info *mtd, int section,
142 				    struct mtd_oob_region *region)
143 {
144 	if (section)
145 		return -ERANGE;
146 
147 	region->offset = mtd->oobsize / 2;
148 	region->length = mtd->oobsize / 2;
149 
150 	return 0;
151 }
152 
w25n04lw_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)153 static int w25n04lw_ooblayout_free(struct mtd_info *mtd, int section,
154 				     struct mtd_oob_region *region)
155 {
156 	if (section)
157 		return -ERANGE;
158 
159 	region->offset = 2;
160 	region->length = mtd->oobsize / 2 - 2;
161 
162 	return 0;
163 }
164 
165 static const struct mtd_ooblayout_ops w25n04lw_ooblayout = {
166 	.ecc = w25n04lw_ooblayout_ecc,
167 	.rfree = w25n04lw_ooblayout_free,
168 };
169 
170 /* Another set for the same id[2] devices in one series */
171 static const struct spinand_info winbond_spinand_table[] = {
172 	SPINAND_INFO("W25M02GV",
173 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAB),
174 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
175 		     NAND_ECCREQ(1, 512),
176 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
177 					      &write_cache_variants,
178 					      &update_cache_variants),
179 		     0,
180 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
181 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
182 	SPINAND_INFO("W25N512GV",
183 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x20),
184 		     NAND_MEMORG(1, 2048, 64, 64, 512, 1, 1, 1),
185 		     NAND_ECCREQ(1, 512),
186 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
187 					      &write_cache_variants,
188 					      &update_cache_variants),
189 		     0,
190 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
191 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
192 	SPINAND_INFO("W25N01GV",
193 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x21),
194 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
195 		     NAND_ECCREQ(1, 512),
196 #ifdef CONFIG_SPI_NAND_WINBOND_CONT_READ
197 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_cont,
198 					      &write_cache_variants,
199 					      &update_cache_variants),
200 #else
201 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
202 					      &write_cache_variants,
203 					      &update_cache_variants),
204 #endif
205 		     0,
206 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
207 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
208 	SPINAND_INFO("W25N02KV",
209 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x22),
210 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
211 		     NAND_ECCREQ(8, 512),
212 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
213 					      &write_cache_variants,
214 					      &update_cache_variants),
215 		     0,
216 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
217 				     w25n02kv_ecc_get_status)),
218 	SPINAND_INFO("W25N04KV",
219 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x23),
220 		     NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
221 		     NAND_ECCREQ(8, 512),
222 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
223 					      &write_cache_variants,
224 					      &update_cache_variants),
225 		     0,
226 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
227 				     w25n02kv_ecc_get_status)),
228 	SPINAND_INFO("W25N01GW",
229 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x21),
230 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
231 		     NAND_ECCREQ(1, 512),
232 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
233 					      &write_cache_variants,
234 					      &update_cache_variants),
235 		     0,
236 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
237 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
238 	SPINAND_INFO("W25N02KW",
239 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x22),
240 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
241 		     NAND_ECCREQ(8, 512),
242 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
243 					      &write_cache_variants,
244 					      &update_cache_variants),
245 		     0,
246 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
247 				     w25n02kv_ecc_get_status)),
248 	SPINAND_INFO("W25N01KV",
249 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAE, 0x21),
250 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
251 		     NAND_ECCREQ(4, 512),
252 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
253 					      &write_cache_variants,
254 					      &update_cache_variants),
255 		     0,
256 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
257 				     w25n02kv_ecc_get_status)),
258 	SPINAND_INFO("W25N01JWZEIG",
259 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBC, 0x21),
260 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
261 		     NAND_ECCREQ(1, 512),
262 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
263 					      &write_cache_variants,
264 					      &update_cache_variants),
265 		     SPINAND_HAS_QE_BIT,
266 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
267 	SPINAND_INFO("W25N01KWZPIG",
268 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBE, 0x21),
269 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
270 		     NAND_ECCREQ(4, 512),
271 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
272 					      &write_cache_variants,
273 					      &update_cache_variants),
274 		     0,
275 		     SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
276 	SPINAND_INFO("W25N04LW2EIG",
277 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2, 0x23),
278 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
279 		     NAND_ECCREQ(8, 512),
280 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
281 					      &write_cache_variants,
282 					      &update_cache_variants),
283 		     SPINAND_HAS_QE_BIT,
284 		     SPINAND_ECCINFO(&w25n04lw_ooblayout, w25n02kv_ecc_get_status)),
285 };
286 
winbond_spinand_init(struct spinand_device * spinand)287 static int winbond_spinand_init(struct spinand_device *spinand)
288 {
289 	struct nand_device *nand = spinand_to_nand(spinand);
290 	unsigned int i;
291 
292 	/*
293 	 * Make sure all dies are in buffer read mode and not continuous read
294 	 * mode.
295 	 */
296 	for (i = 0; i < nand->memorg.ntargets; i++) {
297 		spinand_select_target(spinand, i);
298 		spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
299 				WINBOND_CFG_BUF_READ);
300 	}
301 
302 	/* W25N01JWZEIG enable continuous read */
303 #ifdef CONFIG_SPI_NAND_WINBOND_CONT_READ
304 	if (spinand->id.data[1] == 0xaa && spinand->id.data[2] == 0x21) {
305 		spinand->support_cont_read = true;
306 		spinand_upd_cfg(spinand, CFG_BUF_ENABLE, 0);
307 		printf("Support cont_read\n");
308 	}
309 #endif
310 
311 	return 0;
312 }
313 
314 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
315 	.init = winbond_spinand_init,
316 };
317 
318 const struct spinand_manufacturer winbond_spinand_manufacturer = {
319 	.id = SPINAND_MFR_WINBOND,
320 	.name = "Winbond",
321 	.chips = winbond_spinand_table,
322 	.nchips = ARRAY_SIZE(winbond_spinand_table),
323 	.ops = &winbond_spinand_manuf_ops,
324 };
325