xref: /openbmc/linux/drivers/mtd/nand/spi/micron.c (revision 2a598d0b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2016-2017 Micron Technology, Inc.
4  *
5  * Authors:
6  *	Peter Pan <peterpandong@micron.com>
7  */
8 
9 #include <linux/device.h>
10 #include <linux/kernel.h>
11 #include <linux/mtd/spinand.h>
12 
13 #define SPINAND_MFR_MICRON		0x2c
14 
15 #define MICRON_STATUS_ECC_MASK		GENMASK(7, 4)
16 #define MICRON_STATUS_ECC_NO_BITFLIPS	(0 << 4)
17 #define MICRON_STATUS_ECC_1TO3_BITFLIPS	(1 << 4)
18 #define MICRON_STATUS_ECC_4TO6_BITFLIPS	(3 << 4)
19 #define MICRON_STATUS_ECC_7TO8_BITFLIPS	(5 << 4)
20 
21 #define MICRON_CFG_CR			BIT(0)
22 
23 /*
24  * As per datasheet, die selection is done by the 6th bit of Die
25  * Select Register (Address 0xD0).
26  */
27 #define MICRON_DIE_SELECT_REG	0xD0
28 
29 #define MICRON_SELECT_DIE(x)	((x) << 6)
30 
31 static SPINAND_OP_VARIANTS(quadio_read_cache_variants,
32 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
33 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
34 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
35 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
36 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
37 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
38 
39 static SPINAND_OP_VARIANTS(x4_write_cache_variants,
40 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
41 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
42 
43 static SPINAND_OP_VARIANTS(x4_update_cache_variants,
44 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
45 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
46 
47 /* Micron  MT29F2G01AAAED Device */
48 static SPINAND_OP_VARIANTS(x4_read_cache_variants,
49 			   SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
50 			   SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
51 			   SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
52 			   SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
53 
54 static SPINAND_OP_VARIANTS(x1_write_cache_variants,
55 			   SPINAND_PROG_LOAD(true, 0, NULL, 0));
56 
57 static SPINAND_OP_VARIANTS(x1_update_cache_variants,
58 			   SPINAND_PROG_LOAD(false, 0, NULL, 0));
59 
60 static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section,
61 				  struct mtd_oob_region *region)
62 {
63 	if (section)
64 		return -ERANGE;
65 
66 	region->offset = mtd->oobsize / 2;
67 	region->length = mtd->oobsize / 2;
68 
69 	return 0;
70 }
71 
72 static int micron_8_ooblayout_free(struct mtd_info *mtd, int section,
73 				   struct mtd_oob_region *region)
74 {
75 	if (section)
76 		return -ERANGE;
77 
78 	/* Reserve 2 bytes for the BBM. */
79 	region->offset = 2;
80 	region->length = (mtd->oobsize / 2) - 2;
81 
82 	return 0;
83 }
84 
85 static const struct mtd_ooblayout_ops micron_8_ooblayout = {
86 	.ecc = micron_8_ooblayout_ecc,
87 	.free = micron_8_ooblayout_free,
88 };
89 
90 static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section,
91 				  struct mtd_oob_region *region)
92 {
93 	struct spinand_device *spinand = mtd_to_spinand(mtd);
94 
95 	if (section >= spinand->base.memorg.pagesize /
96 			mtd->ecc_step_size)
97 		return -ERANGE;
98 
99 	region->offset = (section * 16) + 8;
100 	region->length = 8;
101 
102 	return 0;
103 }
104 
105 static int micron_4_ooblayout_free(struct mtd_info *mtd, int section,
106 				   struct mtd_oob_region *region)
107 {
108 	struct spinand_device *spinand = mtd_to_spinand(mtd);
109 
110 	if (section >= spinand->base.memorg.pagesize /
111 			mtd->ecc_step_size)
112 		return -ERANGE;
113 
114 	if (section) {
115 		region->offset = 16 * section;
116 		region->length = 8;
117 	} else {
118 		/* section 0 has two bytes reserved for the BBM */
119 		region->offset = 2;
120 		region->length = 6;
121 	}
122 
123 	return 0;
124 }
125 
126 static const struct mtd_ooblayout_ops micron_4_ooblayout = {
127 	.ecc = micron_4_ooblayout_ecc,
128 	.free = micron_4_ooblayout_free,
129 };
130 
131 static int micron_select_target(struct spinand_device *spinand,
132 				unsigned int target)
133 {
134 	struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG,
135 						      spinand->scratchbuf);
136 
137 	if (target > 1)
138 		return -EINVAL;
139 
140 	*spinand->scratchbuf = MICRON_SELECT_DIE(target);
141 
142 	return spi_mem_exec_op(spinand->spimem, &op);
143 }
144 
145 static int micron_8_ecc_get_status(struct spinand_device *spinand,
146 				   u8 status)
147 {
148 	switch (status & MICRON_STATUS_ECC_MASK) {
149 	case STATUS_ECC_NO_BITFLIPS:
150 		return 0;
151 
152 	case STATUS_ECC_UNCOR_ERROR:
153 		return -EBADMSG;
154 
155 	case MICRON_STATUS_ECC_1TO3_BITFLIPS:
156 		return 3;
157 
158 	case MICRON_STATUS_ECC_4TO6_BITFLIPS:
159 		return 6;
160 
161 	case MICRON_STATUS_ECC_7TO8_BITFLIPS:
162 		return 8;
163 
164 	default:
165 		break;
166 	}
167 
168 	return -EINVAL;
169 }
170 
171 static const struct spinand_info micron_spinand_table[] = {
172 	/* M79A 2Gb 3.3V */
173 	SPINAND_INFO("MT29F2G01ABAGD",
174 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
175 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
176 		     NAND_ECCREQ(8, 512),
177 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
178 					      &x4_write_cache_variants,
179 					      &x4_update_cache_variants),
180 		     0,
181 		     SPINAND_ECCINFO(&micron_8_ooblayout,
182 				     micron_8_ecc_get_status)),
183 	/* M79A 2Gb 1.8V */
184 	SPINAND_INFO("MT29F2G01ABBGD",
185 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
186 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
187 		     NAND_ECCREQ(8, 512),
188 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
189 					      &x4_write_cache_variants,
190 					      &x4_update_cache_variants),
191 		     0,
192 		     SPINAND_ECCINFO(&micron_8_ooblayout,
193 				     micron_8_ecc_get_status)),
194 	/* M78A 1Gb 3.3V */
195 	SPINAND_INFO("MT29F1G01ABAFD",
196 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
197 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
198 		     NAND_ECCREQ(8, 512),
199 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
200 					      &x4_write_cache_variants,
201 					      &x4_update_cache_variants),
202 		     0,
203 		     SPINAND_ECCINFO(&micron_8_ooblayout,
204 				     micron_8_ecc_get_status)),
205 	/* M78A 1Gb 1.8V */
206 	SPINAND_INFO("MT29F1G01ABAFD",
207 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
208 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
209 		     NAND_ECCREQ(8, 512),
210 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
211 					      &x4_write_cache_variants,
212 					      &x4_update_cache_variants),
213 		     0,
214 		     SPINAND_ECCINFO(&micron_8_ooblayout,
215 				     micron_8_ecc_get_status)),
216 	/* M79A 4Gb 3.3V */
217 	SPINAND_INFO("MT29F4G01ADAGD",
218 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36),
219 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2),
220 		     NAND_ECCREQ(8, 512),
221 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
222 					      &x4_write_cache_variants,
223 					      &x4_update_cache_variants),
224 		     0,
225 		     SPINAND_ECCINFO(&micron_8_ooblayout,
226 				     micron_8_ecc_get_status),
227 		     SPINAND_SELECT_TARGET(micron_select_target)),
228 	/* M70A 4Gb 3.3V */
229 	SPINAND_INFO("MT29F4G01ABAFD",
230 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
231 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
232 		     NAND_ECCREQ(8, 512),
233 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
234 					      &x4_write_cache_variants,
235 					      &x4_update_cache_variants),
236 		     SPINAND_HAS_CR_FEAT_BIT,
237 		     SPINAND_ECCINFO(&micron_8_ooblayout,
238 				     micron_8_ecc_get_status)),
239 	/* M70A 4Gb 1.8V */
240 	SPINAND_INFO("MT29F4G01ABBFD",
241 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
242 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
243 		     NAND_ECCREQ(8, 512),
244 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
245 					      &x4_write_cache_variants,
246 					      &x4_update_cache_variants),
247 		     SPINAND_HAS_CR_FEAT_BIT,
248 		     SPINAND_ECCINFO(&micron_8_ooblayout,
249 				     micron_8_ecc_get_status)),
250 	/* M70A 8Gb 3.3V */
251 	SPINAND_INFO("MT29F8G01ADAFD",
252 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46),
253 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),
254 		     NAND_ECCREQ(8, 512),
255 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
256 					      &x4_write_cache_variants,
257 					      &x4_update_cache_variants),
258 		     SPINAND_HAS_CR_FEAT_BIT,
259 		     SPINAND_ECCINFO(&micron_8_ooblayout,
260 				     micron_8_ecc_get_status),
261 		     SPINAND_SELECT_TARGET(micron_select_target)),
262 	/* M70A 8Gb 1.8V */
263 	SPINAND_INFO("MT29F8G01ADBFD",
264 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47),
265 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),
266 		     NAND_ECCREQ(8, 512),
267 		     SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
268 					      &x4_write_cache_variants,
269 					      &x4_update_cache_variants),
270 		     SPINAND_HAS_CR_FEAT_BIT,
271 		     SPINAND_ECCINFO(&micron_8_ooblayout,
272 				     micron_8_ecc_get_status),
273 		     SPINAND_SELECT_TARGET(micron_select_target)),
274 	/* M69A 2Gb 3.3V */
275 	SPINAND_INFO("MT29F2G01AAAED",
276 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F),
277 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1),
278 		     NAND_ECCREQ(4, 512),
279 		     SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants,
280 					      &x1_write_cache_variants,
281 					      &x1_update_cache_variants),
282 		     0,
283 		     SPINAND_ECCINFO(&micron_4_ooblayout, NULL)),
284 };
285 
286 static int micron_spinand_init(struct spinand_device *spinand)
287 {
288 	/*
289 	 * M70A device series enable Continuous Read feature at Power-up,
290 	 * which is not supported. Disable this bit to avoid any possible
291 	 * failure.
292 	 */
293 	if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT)
294 		return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0);
295 
296 	return 0;
297 }
298 
299 static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
300 	.init = micron_spinand_init,
301 };
302 
303 const struct spinand_manufacturer micron_spinand_manufacturer = {
304 	.id = SPINAND_MFR_MICRON,
305 	.name = "Micron",
306 	.chips = micron_spinand_table,
307 	.nchips = ARRAY_SIZE(micron_spinand_table),
308 	.ops = &micron_spinand_manuf_ops,
309 };
310