xref: /linux/drivers/mtd/nand/spi/gigadevice.c (revision 6387ad9caf8f09747a8569e5876086b72ee9382c)
1c93c6132SChuanhong Guo // SPDX-License-Identifier: GPL-2.0
2c93c6132SChuanhong Guo /*
3c93c6132SChuanhong Guo  * Author:
4c93c6132SChuanhong Guo  *	Chuanhong Guo <gch981213@gmail.com>
5c93c6132SChuanhong Guo  */
6c93c6132SChuanhong Guo 
7c93c6132SChuanhong Guo #include <linux/device.h>
8c93c6132SChuanhong Guo #include <linux/kernel.h>
9c93c6132SChuanhong Guo #include <linux/mtd/spinand.h>
10c93c6132SChuanhong Guo 
11c93c6132SChuanhong Guo #define SPINAND_MFR_GIGADEVICE			0xC8
12cfd93d7cSJeff Kletsky 
13c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4)
14c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4)
15c93c6132SChuanhong Guo 
16c40c7a99SStefan Roese #define GD5FXGQ4UEXXG_REG_STATUS2		0xf0
17c40c7a99SStefan Roese 
18cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK		(7 << 4)
19cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS	(0 << 4)
20cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS	(1 << 4)
21cfd93d7cSJeff Kletsky #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR	(7 << 4)
22cfd93d7cSJeff Kletsky 
23c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(read_cache_variants,
24*6387ad9cSHauke Mehrtens 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
25c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
26c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
27c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
28c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
29c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
30c93c6132SChuanhong Guo 
31cfd93d7cSJeff Kletsky static SPINAND_OP_VARIANTS(read_cache_variants_f,
32*6387ad9cSHauke Mehrtens 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
33cfd93d7cSJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
34cfd93d7cSJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
35cfd93d7cSJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0),
36cfd93d7cSJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
37cfd93d7cSJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
38cfd93d7cSJeff Kletsky 
39c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(write_cache_variants,
40c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
41c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
42c93c6132SChuanhong Guo 
43c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(update_cache_variants,
44c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
45c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
46c93c6132SChuanhong Guo 
47c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
48c93c6132SChuanhong Guo 				  struct mtd_oob_region *region)
49c93c6132SChuanhong Guo {
50c93c6132SChuanhong Guo 	if (section > 3)
51c93c6132SChuanhong Guo 		return -ERANGE;
52c93c6132SChuanhong Guo 
53c93c6132SChuanhong Guo 	region->offset = (16 * section) + 8;
54c93c6132SChuanhong Guo 	region->length = 8;
55c93c6132SChuanhong Guo 
56c93c6132SChuanhong Guo 	return 0;
57c93c6132SChuanhong Guo }
58c93c6132SChuanhong Guo 
59c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
60c93c6132SChuanhong Guo 				   struct mtd_oob_region *region)
61c93c6132SChuanhong Guo {
62c93c6132SChuanhong Guo 	if (section > 3)
63c93c6132SChuanhong Guo 		return -ERANGE;
64c93c6132SChuanhong Guo 
65c93c6132SChuanhong Guo 	if (section) {
66c93c6132SChuanhong Guo 		region->offset = 16 * section;
67c93c6132SChuanhong Guo 		region->length = 8;
68c93c6132SChuanhong Guo 	} else {
69c93c6132SChuanhong Guo 		/* section 0 has one byte reserved for bad block mark */
70c93c6132SChuanhong Guo 		region->offset = 1;
71c93c6132SChuanhong Guo 		region->length = 7;
72c93c6132SChuanhong Guo 	}
73c93c6132SChuanhong Guo 	return 0;
74c93c6132SChuanhong Guo }
75c93c6132SChuanhong Guo 
76cfd93d7cSJeff Kletsky static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
77cfd93d7cSJeff Kletsky 	.ecc = gd5fxgq4xa_ooblayout_ecc,
78cfd93d7cSJeff Kletsky 	.free = gd5fxgq4xa_ooblayout_free,
79cfd93d7cSJeff Kletsky };
80cfd93d7cSJeff Kletsky 
81c93c6132SChuanhong Guo static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
82c93c6132SChuanhong Guo 					 u8 status)
83c93c6132SChuanhong Guo {
84c93c6132SChuanhong Guo 	switch (status & STATUS_ECC_MASK) {
85c93c6132SChuanhong Guo 	case STATUS_ECC_NO_BITFLIPS:
86c93c6132SChuanhong Guo 		return 0;
87c93c6132SChuanhong Guo 
88c93c6132SChuanhong Guo 	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
89c93c6132SChuanhong Guo 		/* 1-7 bits are flipped. return the maximum. */
90c93c6132SChuanhong Guo 		return 7;
91c93c6132SChuanhong Guo 
92c93c6132SChuanhong Guo 	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
93c93c6132SChuanhong Guo 		return 8;
94c93c6132SChuanhong Guo 
95c93c6132SChuanhong Guo 	case STATUS_ECC_UNCOR_ERROR:
96c93c6132SChuanhong Guo 		return -EBADMSG;
97c93c6132SChuanhong Guo 
98c93c6132SChuanhong Guo 	default:
99c93c6132SChuanhong Guo 		break;
100c93c6132SChuanhong Guo 	}
101c93c6132SChuanhong Guo 
102c93c6132SChuanhong Guo 	return -EINVAL;
103c93c6132SChuanhong Guo }
104c93c6132SChuanhong Guo 
105cfd93d7cSJeff Kletsky static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
106c40c7a99SStefan Roese 				       struct mtd_oob_region *region)
107c40c7a99SStefan Roese {
108c40c7a99SStefan Roese 	if (section)
109c40c7a99SStefan Roese 		return -ERANGE;
110c40c7a99SStefan Roese 
111c40c7a99SStefan Roese 	region->offset = 64;
112c40c7a99SStefan Roese 	region->length = 64;
113c40c7a99SStefan Roese 
114c40c7a99SStefan Roese 	return 0;
115c40c7a99SStefan Roese }
116c40c7a99SStefan Roese 
117cfd93d7cSJeff Kletsky static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
118c40c7a99SStefan Roese 					struct mtd_oob_region *region)
119c40c7a99SStefan Roese {
120c40c7a99SStefan Roese 	if (section)
121c40c7a99SStefan Roese 		return -ERANGE;
122c40c7a99SStefan Roese 
123c40c7a99SStefan Roese 	/* Reserve 1 bytes for the BBM. */
124c40c7a99SStefan Roese 	region->offset = 1;
125c40c7a99SStefan Roese 	region->length = 63;
126c40c7a99SStefan Roese 
127c40c7a99SStefan Roese 	return 0;
128c40c7a99SStefan Roese }
129c40c7a99SStefan Roese 
130cfd93d7cSJeff Kletsky static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = {
131cfd93d7cSJeff Kletsky 	.ecc = gd5fxgq4_variant2_ooblayout_ecc,
132cfd93d7cSJeff Kletsky 	.free = gd5fxgq4_variant2_ooblayout_free,
133cfd93d7cSJeff Kletsky };
134cfd93d7cSJeff Kletsky 
135c40c7a99SStefan Roese static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
136c40c7a99SStefan Roese 					u8 status)
137c40c7a99SStefan Roese {
138c40c7a99SStefan Roese 	u8 status2;
139c40c7a99SStefan Roese 	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
140c40c7a99SStefan Roese 						      &status2);
141c40c7a99SStefan Roese 	int ret;
142c40c7a99SStefan Roese 
143c40c7a99SStefan Roese 	switch (status & STATUS_ECC_MASK) {
144c40c7a99SStefan Roese 	case STATUS_ECC_NO_BITFLIPS:
145c40c7a99SStefan Roese 		return 0;
146c40c7a99SStefan Roese 
147c40c7a99SStefan Roese 	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
148c40c7a99SStefan Roese 		/*
149c40c7a99SStefan Roese 		 * Read status2 register to determine a more fine grained
150c40c7a99SStefan Roese 		 * bit error status
151c40c7a99SStefan Roese 		 */
152c40c7a99SStefan Roese 		ret = spi_mem_exec_op(spinand->spimem, &op);
153c40c7a99SStefan Roese 		if (ret)
154c40c7a99SStefan Roese 			return ret;
155c40c7a99SStefan Roese 
156c40c7a99SStefan Roese 		/*
157c40c7a99SStefan Roese 		 * 4 ... 7 bits are flipped (1..4 can't be detected, so
158c40c7a99SStefan Roese 		 * report the maximum of 4 in this case
159c40c7a99SStefan Roese 		 */
160c40c7a99SStefan Roese 		/* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
161c40c7a99SStefan Roese 		return ((status & STATUS_ECC_MASK) >> 2) |
162c40c7a99SStefan Roese 			((status2 & STATUS_ECC_MASK) >> 4);
163c40c7a99SStefan Roese 
164c40c7a99SStefan Roese 	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
165c40c7a99SStefan Roese 		return 8;
166c40c7a99SStefan Roese 
167c40c7a99SStefan Roese 	case STATUS_ECC_UNCOR_ERROR:
168c40c7a99SStefan Roese 		return -EBADMSG;
169c40c7a99SStefan Roese 
170c40c7a99SStefan Roese 	default:
171c40c7a99SStefan Roese 		break;
172c40c7a99SStefan Roese 	}
173c40c7a99SStefan Roese 
174c40c7a99SStefan Roese 	return -EINVAL;
175c40c7a99SStefan Roese }
176c40c7a99SStefan Roese 
177cfd93d7cSJeff Kletsky static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
178cfd93d7cSJeff Kletsky 					u8 status)
179cfd93d7cSJeff Kletsky {
180cfd93d7cSJeff Kletsky 	switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
181cfd93d7cSJeff Kletsky 	case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
182cfd93d7cSJeff Kletsky 		return 0;
183c93c6132SChuanhong Guo 
184cfd93d7cSJeff Kletsky 	case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
185cfd93d7cSJeff Kletsky 		return 3;
186cfd93d7cSJeff Kletsky 
187cfd93d7cSJeff Kletsky 	case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
188cfd93d7cSJeff Kletsky 		return -EBADMSG;
189cfd93d7cSJeff Kletsky 
190cfd93d7cSJeff Kletsky 	default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
191cfd93d7cSJeff Kletsky 		return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
192cfd93d7cSJeff Kletsky 	}
193cfd93d7cSJeff Kletsky 
194cfd93d7cSJeff Kletsky 	return -EINVAL;
195cfd93d7cSJeff Kletsky }
196c40c7a99SStefan Roese 
197c93c6132SChuanhong Guo static const struct spinand_info gigadevice_spinand_table[] = {
198f1541773SChuanhong Guo 	SPINAND_INFO("GD5F1GQ4xA",
199f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1),
200377e517bSBoris Brezillon 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
201c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
202c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
203c93c6132SChuanhong Guo 					      &write_cache_variants,
204c93c6132SChuanhong Guo 					      &update_cache_variants),
205c93c6132SChuanhong Guo 		     0,
206c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
207c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
208f1541773SChuanhong Guo 	SPINAND_INFO("GD5F2GQ4xA",
209f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2),
210377e517bSBoris Brezillon 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
211c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
212c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
213c93c6132SChuanhong Guo 					      &write_cache_variants,
214c93c6132SChuanhong Guo 					      &update_cache_variants),
215c93c6132SChuanhong Guo 		     0,
216c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
217c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
218f1541773SChuanhong Guo 	SPINAND_INFO("GD5F4GQ4xA",
219f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4),
220a126483eSFrieder Schrempf 		     NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1),
221c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
222c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
223c93c6132SChuanhong Guo 					      &write_cache_variants,
224c93c6132SChuanhong Guo 					      &update_cache_variants),
225c93c6132SChuanhong Guo 		     0,
226c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
227c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
228f1541773SChuanhong Guo 	SPINAND_INFO("GD5F1GQ4UExxG",
229f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1),
230377e517bSBoris Brezillon 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
231c40c7a99SStefan Roese 		     NAND_ECCREQ(8, 512),
232c40c7a99SStefan Roese 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
233c40c7a99SStefan Roese 					      &write_cache_variants,
234c40c7a99SStefan Roese 					      &update_cache_variants),
235c40c7a99SStefan Roese 		     0,
236cfd93d7cSJeff Kletsky 		     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
237c40c7a99SStefan Roese 				     gd5fxgq4uexxg_ecc_get_status)),
238f1541773SChuanhong Guo 	SPINAND_INFO("GD5F1GQ4UFxxG",
239f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
240cfd93d7cSJeff Kletsky 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
241cfd93d7cSJeff Kletsky 		     NAND_ECCREQ(8, 512),
242cfd93d7cSJeff Kletsky 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
243cfd93d7cSJeff Kletsky 					      &write_cache_variants,
244cfd93d7cSJeff Kletsky 					      &update_cache_variants),
245cfd93d7cSJeff Kletsky 		     0,
246cfd93d7cSJeff Kletsky 		     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
247cfd93d7cSJeff Kletsky 				     gd5fxgq4ufxxg_ecc_get_status)),
248c93c6132SChuanhong Guo };
249c93c6132SChuanhong Guo 
250c93c6132SChuanhong Guo static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
251c93c6132SChuanhong Guo };
252c93c6132SChuanhong Guo 
253c93c6132SChuanhong Guo const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
254c93c6132SChuanhong Guo 	.id = SPINAND_MFR_GIGADEVICE,
255c93c6132SChuanhong Guo 	.name = "GigaDevice",
256f1541773SChuanhong Guo 	.chips = gigadevice_spinand_table,
257f1541773SChuanhong Guo 	.nchips = ARRAY_SIZE(gigadevice_spinand_table),
258c93c6132SChuanhong Guo 	.ops = &gigadevice_spinand_manuf_ops,
259c93c6132SChuanhong Guo };
260