xref: /OK3568_Linux_fs/kernel/drivers/mtd/nand/spi/winbond.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #include <linux/mtd/spinand.h>
13 
14 #define SPINAND_MFR_WINBOND		0xEF
15 
16 #define WINBOND_CFG_BUF_READ		BIT(3)
17 #define WINBOND_STATUS_ECC_HAS_BITFLIPS_T	(3 << 4)
18 
19 static SPINAND_OP_VARIANTS(read_cache_variants,
20 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
21 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
22 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
23 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
24 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
25 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
26 
27 static SPINAND_OP_VARIANTS(write_cache_variants,
28 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
29 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
30 
31 static SPINAND_OP_VARIANTS(update_cache_variants,
32 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
33 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
34 
w25m02gv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)35 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
36 				  struct mtd_oob_region *region)
37 {
38 	if (section > 3)
39 		return -ERANGE;
40 
41 	region->offset = (16 * section) + 8;
42 	region->length = 8;
43 
44 	return 0;
45 }
46 
w25m02gv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)47 static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
48 				   struct mtd_oob_region *region)
49 {
50 	if (section > 3)
51 		return -ERANGE;
52 
53 	region->offset = (16 * section) + 2;
54 	region->length = 6;
55 
56 	return 0;
57 }
58 
59 static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
60 	.ecc = w25m02gv_ooblayout_ecc,
61 	.free = w25m02gv_ooblayout_free,
62 };
63 
w25m02gv_select_target(struct spinand_device * spinand,unsigned int target)64 static int w25m02gv_select_target(struct spinand_device *spinand,
65 				  unsigned int target)
66 {
67 	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
68 					  SPI_MEM_OP_NO_ADDR,
69 					  SPI_MEM_OP_NO_DUMMY,
70 					  SPI_MEM_OP_DATA_OUT(1,
71 							spinand->scratchbuf,
72 							1));
73 
74 	*spinand->scratchbuf = target;
75 	return spi_mem_exec_op(spinand->spimem, &op);
76 }
77 
w25n02kv_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)78 static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
79 				  struct mtd_oob_region *region)
80 {
81 	if (section)
82 		return -ERANGE;
83 
84 	region->offset = 64;
85 	region->length = 64;
86 
87 	return 0;
88 }
89 
w25n02kv_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)90 static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
91 				   struct mtd_oob_region *region)
92 {
93 	if (section)
94 		return -ERANGE;
95 
96 	/* Reserve 2 bytes for the BBM. */
97 	region->offset = 2;
98 	region->length = 62;
99 
100 	return 0;
101 }
102 
103 static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
104 	.ecc = w25n02kv_ooblayout_ecc,
105 	.free = w25n02kv_ooblayout_free,
106 };
107 
w25n02kv_ecc_get_status(struct spinand_device * spinand,u8 status)108 static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
109 				   u8 status)
110 {
111 	struct nand_device *nand = spinand_to_nand(spinand);
112 
113 	switch (status & STATUS_ECC_MASK) {
114 	case STATUS_ECC_NO_BITFLIPS:
115 		return 0;
116 
117 	case STATUS_ECC_UNCOR_ERROR:
118 		return -EBADMSG;
119 
120 	case STATUS_ECC_HAS_BITFLIPS:
121 		return 1;
122 
123 	default:
124 		return nanddev_get_ecc_requirements(nand)->strength;
125 	}
126 
127 	return -EINVAL;
128 }
129 
130 
131 static const struct spinand_info winbond_spinand_table[] = {
132 	SPINAND_INFO("W25M02GV",
133 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
134 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
135 		     NAND_ECCREQ(1, 512),
136 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
137 					      &write_cache_variants,
138 					      &update_cache_variants),
139 		     0,
140 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
141 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
142 	SPINAND_INFO("W25N512GV",
143 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x20),
144 		     NAND_MEMORG(1, 2048, 64, 64, 512, 10, 1, 1, 1),
145 		     NAND_ECCREQ(1, 512),
146 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
147 					      &write_cache_variants,
148 					      &update_cache_variants),
149 		     0,
150 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
151 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
152 	SPINAND_INFO("W25N01GV",
153 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x21),
154 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
155 		     NAND_ECCREQ(1, 512),
156 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
157 					      &write_cache_variants,
158 					      &update_cache_variants),
159 		     0,
160 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
161 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
162 	SPINAND_INFO("W25N02KV",
163 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x22),
164 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 20, 1, 1, 1),
165 		     NAND_ECCREQ(8, 512),
166 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
167 					      &write_cache_variants,
168 					      &update_cache_variants),
169 		     0,
170 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
171 				     w25n02kv_ecc_get_status)),
172 	SPINAND_INFO("W25N04KV",
173 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAA, 0x23),
174 		     NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 1, 1, 1),
175 		     NAND_ECCREQ(8, 512),
176 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
177 					      &write_cache_variants,
178 					      &update_cache_variants),
179 		     0,
180 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
181 				     w25n02kv_ecc_get_status)),
182 	SPINAND_INFO("W25N01GW",
183 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x21),
184 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 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("W25N02KW",
193 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBA, 0x22),
194 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 20, 1, 1, 1),
195 		     NAND_ECCREQ(8, 512),
196 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
197 					      &write_cache_variants,
198 					      &update_cache_variants),
199 		     0,
200 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
201 				     w25n02kv_ecc_get_status)),
202 	SPINAND_INFO("W25N01KV",
203 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAE, 0x21),
204 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
205 		     NAND_ECCREQ(4, 512),
206 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
207 					      &write_cache_variants,
208 					      &update_cache_variants),
209 		     0,
210 		     SPINAND_ECCINFO(&w25n02kv_ooblayout,
211 				     w25n02kv_ecc_get_status)),
212 };
213 
winbond_spinand_init(struct spinand_device * spinand)214 static int winbond_spinand_init(struct spinand_device *spinand)
215 {
216 	struct nand_device *nand = spinand_to_nand(spinand);
217 	unsigned int i;
218 
219 	/*
220 	 * Make sure all dies are in buffer read mode and not continuous read
221 	 * mode.
222 	 */
223 	for (i = 0; i < nand->memorg.ntargets; i++) {
224 		spinand_select_target(spinand, i);
225 		spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
226 				WINBOND_CFG_BUF_READ);
227 	}
228 
229 	return 0;
230 }
231 
232 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
233 	.init = winbond_spinand_init,
234 };
235 
236 const struct spinand_manufacturer winbond_spinand_manufacturer = {
237 	.id = SPINAND_MFR_WINBOND,
238 	.name = "Winbond",
239 	.chips = winbond_spinand_table,
240 	.nchips = ARRAY_SIZE(winbond_spinand_table),
241 	.ops = &winbond_spinand_manuf_ops,
242 };
243