xref: /linux/drivers/mtd/nand/spi/gigadevice.c (revision c40c7a990a46e5102a1cc4190557bf315d32d80d)
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
12c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4)
13c93c6132SChuanhong Guo #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4)
14c93c6132SChuanhong Guo 
15*c40c7a99SStefan Roese #define GD5FXGQ4UEXXG_REG_STATUS2		0xf0
16*c40c7a99SStefan Roese 
17c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(read_cache_variants,
18c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
19c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
20c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
21c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
22c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
23c93c6132SChuanhong Guo 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
24c93c6132SChuanhong Guo 
25c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(write_cache_variants,
26c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
27c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
28c93c6132SChuanhong Guo 
29c93c6132SChuanhong Guo static SPINAND_OP_VARIANTS(update_cache_variants,
30c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
31c93c6132SChuanhong Guo 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
32c93c6132SChuanhong Guo 
33c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
34c93c6132SChuanhong Guo 				  struct mtd_oob_region *region)
35c93c6132SChuanhong Guo {
36c93c6132SChuanhong Guo 	if (section > 3)
37c93c6132SChuanhong Guo 		return -ERANGE;
38c93c6132SChuanhong Guo 
39c93c6132SChuanhong Guo 	region->offset = (16 * section) + 8;
40c93c6132SChuanhong Guo 	region->length = 8;
41c93c6132SChuanhong Guo 
42c93c6132SChuanhong Guo 	return 0;
43c93c6132SChuanhong Guo }
44c93c6132SChuanhong Guo 
45c93c6132SChuanhong Guo static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
46c93c6132SChuanhong Guo 				   struct mtd_oob_region *region)
47c93c6132SChuanhong Guo {
48c93c6132SChuanhong Guo 	if (section > 3)
49c93c6132SChuanhong Guo 		return -ERANGE;
50c93c6132SChuanhong Guo 
51c93c6132SChuanhong Guo 	if (section) {
52c93c6132SChuanhong Guo 		region->offset = 16 * section;
53c93c6132SChuanhong Guo 		region->length = 8;
54c93c6132SChuanhong Guo 	} else {
55c93c6132SChuanhong Guo 		/* section 0 has one byte reserved for bad block mark */
56c93c6132SChuanhong Guo 		region->offset = 1;
57c93c6132SChuanhong Guo 		region->length = 7;
58c93c6132SChuanhong Guo 	}
59c93c6132SChuanhong Guo 	return 0;
60c93c6132SChuanhong Guo }
61c93c6132SChuanhong Guo 
62c93c6132SChuanhong Guo static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
63c93c6132SChuanhong Guo 					 u8 status)
64c93c6132SChuanhong Guo {
65c93c6132SChuanhong Guo 	switch (status & STATUS_ECC_MASK) {
66c93c6132SChuanhong Guo 	case STATUS_ECC_NO_BITFLIPS:
67c93c6132SChuanhong Guo 		return 0;
68c93c6132SChuanhong Guo 
69c93c6132SChuanhong Guo 	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
70c93c6132SChuanhong Guo 		/* 1-7 bits are flipped. return the maximum. */
71c93c6132SChuanhong Guo 		return 7;
72c93c6132SChuanhong Guo 
73c93c6132SChuanhong Guo 	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
74c93c6132SChuanhong Guo 		return 8;
75c93c6132SChuanhong Guo 
76c93c6132SChuanhong Guo 	case STATUS_ECC_UNCOR_ERROR:
77c93c6132SChuanhong Guo 		return -EBADMSG;
78c93c6132SChuanhong Guo 
79c93c6132SChuanhong Guo 	default:
80c93c6132SChuanhong Guo 		break;
81c93c6132SChuanhong Guo 	}
82c93c6132SChuanhong Guo 
83c93c6132SChuanhong Guo 	return -EINVAL;
84c93c6132SChuanhong Guo }
85c93c6132SChuanhong Guo 
86*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
87*c40c7a99SStefan Roese 				       struct mtd_oob_region *region)
88*c40c7a99SStefan Roese {
89*c40c7a99SStefan Roese 	if (section)
90*c40c7a99SStefan Roese 		return -ERANGE;
91*c40c7a99SStefan Roese 
92*c40c7a99SStefan Roese 	region->offset = 64;
93*c40c7a99SStefan Roese 	region->length = 64;
94*c40c7a99SStefan Roese 
95*c40c7a99SStefan Roese 	return 0;
96*c40c7a99SStefan Roese }
97*c40c7a99SStefan Roese 
98*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section,
99*c40c7a99SStefan Roese 					struct mtd_oob_region *region)
100*c40c7a99SStefan Roese {
101*c40c7a99SStefan Roese 	if (section)
102*c40c7a99SStefan Roese 		return -ERANGE;
103*c40c7a99SStefan Roese 
104*c40c7a99SStefan Roese 	/* Reserve 1 bytes for the BBM. */
105*c40c7a99SStefan Roese 	region->offset = 1;
106*c40c7a99SStefan Roese 	region->length = 63;
107*c40c7a99SStefan Roese 
108*c40c7a99SStefan Roese 	return 0;
109*c40c7a99SStefan Roese }
110*c40c7a99SStefan Roese 
111*c40c7a99SStefan Roese static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
112*c40c7a99SStefan Roese 					u8 status)
113*c40c7a99SStefan Roese {
114*c40c7a99SStefan Roese 	u8 status2;
115*c40c7a99SStefan Roese 	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
116*c40c7a99SStefan Roese 						      &status2);
117*c40c7a99SStefan Roese 	int ret;
118*c40c7a99SStefan Roese 
119*c40c7a99SStefan Roese 	switch (status & STATUS_ECC_MASK) {
120*c40c7a99SStefan Roese 	case STATUS_ECC_NO_BITFLIPS:
121*c40c7a99SStefan Roese 		return 0;
122*c40c7a99SStefan Roese 
123*c40c7a99SStefan Roese 	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
124*c40c7a99SStefan Roese 		/*
125*c40c7a99SStefan Roese 		 * Read status2 register to determine a more fine grained
126*c40c7a99SStefan Roese 		 * bit error status
127*c40c7a99SStefan Roese 		 */
128*c40c7a99SStefan Roese 		ret = spi_mem_exec_op(spinand->spimem, &op);
129*c40c7a99SStefan Roese 		if (ret)
130*c40c7a99SStefan Roese 			return ret;
131*c40c7a99SStefan Roese 
132*c40c7a99SStefan Roese 		/*
133*c40c7a99SStefan Roese 		 * 4 ... 7 bits are flipped (1..4 can't be detected, so
134*c40c7a99SStefan Roese 		 * report the maximum of 4 in this case
135*c40c7a99SStefan Roese 		 */
136*c40c7a99SStefan Roese 		/* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
137*c40c7a99SStefan Roese 		return ((status & STATUS_ECC_MASK) >> 2) |
138*c40c7a99SStefan Roese 			((status2 & STATUS_ECC_MASK) >> 4);
139*c40c7a99SStefan Roese 
140*c40c7a99SStefan Roese 	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
141*c40c7a99SStefan Roese 		return 8;
142*c40c7a99SStefan Roese 
143*c40c7a99SStefan Roese 	case STATUS_ECC_UNCOR_ERROR:
144*c40c7a99SStefan Roese 		return -EBADMSG;
145*c40c7a99SStefan Roese 
146*c40c7a99SStefan Roese 	default:
147*c40c7a99SStefan Roese 		break;
148*c40c7a99SStefan Roese 	}
149*c40c7a99SStefan Roese 
150*c40c7a99SStefan Roese 	return -EINVAL;
151*c40c7a99SStefan Roese }
152*c40c7a99SStefan Roese 
153c93c6132SChuanhong Guo static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
154c93c6132SChuanhong Guo 	.ecc = gd5fxgq4xa_ooblayout_ecc,
155c93c6132SChuanhong Guo 	.free = gd5fxgq4xa_ooblayout_free,
156c93c6132SChuanhong Guo };
157c93c6132SChuanhong Guo 
158*c40c7a99SStefan Roese static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = {
159*c40c7a99SStefan Roese 	.ecc = gd5fxgq4uexxg_ooblayout_ecc,
160*c40c7a99SStefan Roese 	.free = gd5fxgq4uexxg_ooblayout_free,
161*c40c7a99SStefan Roese };
162*c40c7a99SStefan Roese 
163c93c6132SChuanhong Guo static const struct spinand_info gigadevice_spinand_table[] = {
164c93c6132SChuanhong Guo 	SPINAND_INFO("GD5F1GQ4xA", 0xF1,
165c93c6132SChuanhong Guo 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
166c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
167c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
168c93c6132SChuanhong Guo 					      &write_cache_variants,
169c93c6132SChuanhong Guo 					      &update_cache_variants),
170c93c6132SChuanhong Guo 		     0,
171c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
172c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
173c93c6132SChuanhong Guo 	SPINAND_INFO("GD5F2GQ4xA", 0xF2,
174c93c6132SChuanhong Guo 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
175c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
176c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
177c93c6132SChuanhong Guo 					      &write_cache_variants,
178c93c6132SChuanhong Guo 					      &update_cache_variants),
179c93c6132SChuanhong Guo 		     0,
180c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
181c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
182c93c6132SChuanhong Guo 	SPINAND_INFO("GD5F4GQ4xA", 0xF4,
183c93c6132SChuanhong Guo 		     NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
184c93c6132SChuanhong Guo 		     NAND_ECCREQ(8, 512),
185c93c6132SChuanhong Guo 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
186c93c6132SChuanhong Guo 					      &write_cache_variants,
187c93c6132SChuanhong Guo 					      &update_cache_variants),
188c93c6132SChuanhong Guo 		     0,
189c93c6132SChuanhong Guo 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
190c93c6132SChuanhong Guo 				     gd5fxgq4xa_ecc_get_status)),
191*c40c7a99SStefan Roese 	SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
192*c40c7a99SStefan Roese 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
193*c40c7a99SStefan Roese 		     NAND_ECCREQ(8, 512),
194*c40c7a99SStefan Roese 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
195*c40c7a99SStefan Roese 					      &write_cache_variants,
196*c40c7a99SStefan Roese 					      &update_cache_variants),
197*c40c7a99SStefan Roese 		     0,
198*c40c7a99SStefan Roese 		     SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout,
199*c40c7a99SStefan Roese 				     gd5fxgq4uexxg_ecc_get_status)),
200c93c6132SChuanhong Guo };
201c93c6132SChuanhong Guo 
202c93c6132SChuanhong Guo static int gigadevice_spinand_detect(struct spinand_device *spinand)
203c93c6132SChuanhong Guo {
204c93c6132SChuanhong Guo 	u8 *id = spinand->id.data;
205c93c6132SChuanhong Guo 	int ret;
206c93c6132SChuanhong Guo 
207c93c6132SChuanhong Guo 	/*
208c93c6132SChuanhong Guo 	 * For GD NANDs, There is an address byte needed to shift in before IDs
209c93c6132SChuanhong Guo 	 * are read out, so the first byte in raw_id is dummy.
210c93c6132SChuanhong Guo 	 */
211c93c6132SChuanhong Guo 	if (id[1] != SPINAND_MFR_GIGADEVICE)
212c93c6132SChuanhong Guo 		return 0;
213c93c6132SChuanhong Guo 
214c93c6132SChuanhong Guo 	ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
215c93c6132SChuanhong Guo 				     ARRAY_SIZE(gigadevice_spinand_table),
216c93c6132SChuanhong Guo 				     id[2]);
217c93c6132SChuanhong Guo 	if (ret)
218c93c6132SChuanhong Guo 		return ret;
219c93c6132SChuanhong Guo 
220c93c6132SChuanhong Guo 	return 1;
221c93c6132SChuanhong Guo }
222c93c6132SChuanhong Guo 
223c93c6132SChuanhong Guo static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
224c93c6132SChuanhong Guo 	.detect = gigadevice_spinand_detect,
225c93c6132SChuanhong Guo };
226c93c6132SChuanhong Guo 
227c93c6132SChuanhong Guo const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
228c93c6132SChuanhong Guo 	.id = SPINAND_MFR_GIGADEVICE,
229c93c6132SChuanhong Guo 	.name = "GigaDevice",
230c93c6132SChuanhong Guo 	.ops = &gigadevice_spinand_manuf_ops,
231c93c6132SChuanhong Guo };
232