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