xref: /linux/drivers/mtd/nand/spi/xtx.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1f4c5c7f9SFelix Matouschek // SPDX-License-Identifier: GPL-2.0
2f4c5c7f9SFelix Matouschek /*
3f4c5c7f9SFelix Matouschek  * Author:
4f4c5c7f9SFelix Matouschek  * Felix Matouschek <felix@matouschek.org>
5f4c5c7f9SFelix Matouschek  */
6f4c5c7f9SFelix Matouschek 
7*d656610eSBruce Suen #include <linux/bitfield.h>
8f4c5c7f9SFelix Matouschek #include <linux/device.h>
9f4c5c7f9SFelix Matouschek #include <linux/kernel.h>
10f4c5c7f9SFelix Matouschek #include <linux/mtd/spinand.h>
11f4c5c7f9SFelix Matouschek 
12f4c5c7f9SFelix Matouschek #define SPINAND_MFR_XTX	0x0B
13f4c5c7f9SFelix Matouschek 
14f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_MASK	GENMASK(5, 2)
15f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_NO_DETECTED	(0 << 2)
16f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_8_CORRECTED	(3 << 4)
17f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_UNCOR_ERROR	(2 << 4)
18f4c5c7f9SFelix Matouschek 
19*d656610eSBruce Suen #define XT26XXXD_STATUS_ECC3_ECC2_MASK	    GENMASK(7, 6)
20*d656610eSBruce Suen #define XT26XXXD_STATUS_ECC_NO_DETECTED     (0)
21*d656610eSBruce Suen #define XT26XXXD_STATUS_ECC_1_7_CORRECTED   (1)
22*d656610eSBruce Suen #define XT26XXXD_STATUS_ECC_8_CORRECTED     (3)
23*d656610eSBruce Suen #define XT26XXXD_STATUS_ECC_UNCOR_ERROR     (2)
24*d656610eSBruce Suen 
25f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(read_cache_variants,
26f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
27f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
28f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
29f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
30f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
31f4c5c7f9SFelix Matouschek 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
32f4c5c7f9SFelix Matouschek 
33f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(write_cache_variants,
34f4c5c7f9SFelix Matouschek 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
35f4c5c7f9SFelix Matouschek 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
36f4c5c7f9SFelix Matouschek 
37f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(update_cache_variants,
38f4c5c7f9SFelix Matouschek 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
39f4c5c7f9SFelix Matouschek 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
40f4c5c7f9SFelix Matouschek 
xt26g0xa_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)41f4c5c7f9SFelix Matouschek static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
42f4c5c7f9SFelix Matouschek 				   struct mtd_oob_region *region)
43f4c5c7f9SFelix Matouschek {
44f4c5c7f9SFelix Matouschek 	if (section)
45f4c5c7f9SFelix Matouschek 		return -ERANGE;
46f4c5c7f9SFelix Matouschek 
47f4c5c7f9SFelix Matouschek 	region->offset = 48;
48f4c5c7f9SFelix Matouschek 	region->length = 16;
49f4c5c7f9SFelix Matouschek 
50f4c5c7f9SFelix Matouschek 	return 0;
51f4c5c7f9SFelix Matouschek }
52f4c5c7f9SFelix Matouschek 
xt26g0xa_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)53f4c5c7f9SFelix Matouschek static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
54f4c5c7f9SFelix Matouschek 				   struct mtd_oob_region *region)
55f4c5c7f9SFelix Matouschek {
56f4c5c7f9SFelix Matouschek 	if (section)
57f4c5c7f9SFelix Matouschek 		return -ERANGE;
58f4c5c7f9SFelix Matouschek 
59f4c5c7f9SFelix Matouschek 	region->offset = 1;
60f4c5c7f9SFelix Matouschek 	region->length = 47;
61f4c5c7f9SFelix Matouschek 
62f4c5c7f9SFelix Matouschek 	return 0;
63f4c5c7f9SFelix Matouschek }
64f4c5c7f9SFelix Matouschek 
65f4c5c7f9SFelix Matouschek static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
66f4c5c7f9SFelix Matouschek 	.ecc = xt26g0xa_ooblayout_ecc,
67f4c5c7f9SFelix Matouschek 	.free = xt26g0xa_ooblayout_free,
68f4c5c7f9SFelix Matouschek };
69f4c5c7f9SFelix Matouschek 
xt26g0xa_ecc_get_status(struct spinand_device * spinand,u8 status)70f4c5c7f9SFelix Matouschek static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
71f4c5c7f9SFelix Matouschek 					 u8 status)
72f4c5c7f9SFelix Matouschek {
73f4c5c7f9SFelix Matouschek 	status = status & XT26G0XA_STATUS_ECC_MASK;
74f4c5c7f9SFelix Matouschek 
75f4c5c7f9SFelix Matouschek 	switch (status) {
76f4c5c7f9SFelix Matouschek 	case XT26G0XA_STATUS_ECC_NO_DETECTED:
77f4c5c7f9SFelix Matouschek 		return 0;
78f4c5c7f9SFelix Matouschek 	case XT26G0XA_STATUS_ECC_8_CORRECTED:
79f4c5c7f9SFelix Matouschek 		return 8;
80f4c5c7f9SFelix Matouschek 	case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
81f4c5c7f9SFelix Matouschek 		return -EBADMSG;
82f4c5c7f9SFelix Matouschek 	default:
83f4c5c7f9SFelix Matouschek 		break;
84f4c5c7f9SFelix Matouschek 	}
85f4c5c7f9SFelix Matouschek 
86f4c5c7f9SFelix Matouschek 	/* At this point values greater than (2 << 4) are invalid  */
87f4c5c7f9SFelix Matouschek 	if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR)
88f4c5c7f9SFelix Matouschek 		return -EINVAL;
89f4c5c7f9SFelix Matouschek 
90f4c5c7f9SFelix Matouschek 	/* (1 << 2) through (7 << 2) are 1-7 corrected errors */
91f4c5c7f9SFelix Matouschek 	return status >> 2;
92f4c5c7f9SFelix Matouschek }
93f4c5c7f9SFelix Matouschek 
xt26xxxd_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)94*d656610eSBruce Suen static int xt26xxxd_ooblayout_ecc(struct mtd_info *mtd, int section,
95*d656610eSBruce Suen 				  struct mtd_oob_region *region)
96*d656610eSBruce Suen {
97*d656610eSBruce Suen 	if (section)
98*d656610eSBruce Suen 		return -ERANGE;
99*d656610eSBruce Suen 
100*d656610eSBruce Suen 	region->offset = mtd->oobsize / 2;
101*d656610eSBruce Suen 	region->length = mtd->oobsize / 2;
102*d656610eSBruce Suen 
103*d656610eSBruce Suen 	return 0;
104*d656610eSBruce Suen }
105*d656610eSBruce Suen 
xt26xxxd_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)106*d656610eSBruce Suen static int xt26xxxd_ooblayout_free(struct mtd_info *mtd, int section,
107*d656610eSBruce Suen 				   struct mtd_oob_region *region)
108*d656610eSBruce Suen {
109*d656610eSBruce Suen 	if (section)
110*d656610eSBruce Suen 		return -ERANGE;
111*d656610eSBruce Suen 
112*d656610eSBruce Suen 	region->offset = 2;
113*d656610eSBruce Suen 	region->length = mtd->oobsize / 2 - 2;
114*d656610eSBruce Suen 
115*d656610eSBruce Suen 	return 0;
116*d656610eSBruce Suen }
117*d656610eSBruce Suen 
118*d656610eSBruce Suen static const struct mtd_ooblayout_ops xt26xxxd_ooblayout = {
119*d656610eSBruce Suen 	.ecc = xt26xxxd_ooblayout_ecc,
120*d656610eSBruce Suen 	.free = xt26xxxd_ooblayout_free,
121*d656610eSBruce Suen };
122*d656610eSBruce Suen 
xt26xxxd_ecc_get_status(struct spinand_device * spinand,u8 status)123*d656610eSBruce Suen static int xt26xxxd_ecc_get_status(struct spinand_device *spinand,
124*d656610eSBruce Suen 				   u8 status)
125*d656610eSBruce Suen {
126*d656610eSBruce Suen 	switch (FIELD_GET(STATUS_ECC_MASK, status)) {
127*d656610eSBruce Suen 	case XT26XXXD_STATUS_ECC_NO_DETECTED:
128*d656610eSBruce Suen 		return 0;
129*d656610eSBruce Suen 	case XT26XXXD_STATUS_ECC_UNCOR_ERROR:
130*d656610eSBruce Suen 		return -EBADMSG;
131*d656610eSBruce Suen 	case XT26XXXD_STATUS_ECC_1_7_CORRECTED:
132*d656610eSBruce Suen 		return 4 + FIELD_GET(XT26XXXD_STATUS_ECC3_ECC2_MASK, status);
133*d656610eSBruce Suen 	case XT26XXXD_STATUS_ECC_8_CORRECTED:
134*d656610eSBruce Suen 		return 8;
135*d656610eSBruce Suen 	default:
136*d656610eSBruce Suen 		break;
137*d656610eSBruce Suen 	}
138*d656610eSBruce Suen 
139*d656610eSBruce Suen 	return -EINVAL;
140*d656610eSBruce Suen }
141f4c5c7f9SFelix Matouschek static const struct spinand_info xtx_spinand_table[] = {
142f4c5c7f9SFelix Matouschek 	SPINAND_INFO("XT26G01A",
143f4c5c7f9SFelix Matouschek 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
144f4c5c7f9SFelix Matouschek 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
145f4c5c7f9SFelix Matouschek 		     NAND_ECCREQ(8, 512),
146f4c5c7f9SFelix Matouschek 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
147f4c5c7f9SFelix Matouschek 					      &write_cache_variants,
148f4c5c7f9SFelix Matouschek 					      &update_cache_variants),
149f4c5c7f9SFelix Matouschek 		     SPINAND_HAS_QE_BIT,
150f4c5c7f9SFelix Matouschek 		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
151f4c5c7f9SFelix Matouschek 				     xt26g0xa_ecc_get_status)),
152f4c5c7f9SFelix Matouschek 	SPINAND_INFO("XT26G02A",
153f4c5c7f9SFelix Matouschek 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
154f4c5c7f9SFelix Matouschek 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
155f4c5c7f9SFelix Matouschek 		     NAND_ECCREQ(8, 512),
156f4c5c7f9SFelix Matouschek 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
157f4c5c7f9SFelix Matouschek 					      &write_cache_variants,
158f4c5c7f9SFelix Matouschek 					      &update_cache_variants),
159f4c5c7f9SFelix Matouschek 		     SPINAND_HAS_QE_BIT,
160f4c5c7f9SFelix Matouschek 		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
161f4c5c7f9SFelix Matouschek 				     xt26g0xa_ecc_get_status)),
162f4c5c7f9SFelix Matouschek 	SPINAND_INFO("XT26G04A",
163f4c5c7f9SFelix Matouschek 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
164f4c5c7f9SFelix Matouschek 		     NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
165f4c5c7f9SFelix Matouschek 		     NAND_ECCREQ(8, 512),
166f4c5c7f9SFelix Matouschek 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
167f4c5c7f9SFelix Matouschek 					      &write_cache_variants,
168f4c5c7f9SFelix Matouschek 					      &update_cache_variants),
169f4c5c7f9SFelix Matouschek 		     SPINAND_HAS_QE_BIT,
170f4c5c7f9SFelix Matouschek 		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
171f4c5c7f9SFelix Matouschek 				     xt26g0xa_ecc_get_status)),
172*d656610eSBruce Suen 	SPINAND_INFO("XT26G01D",
173*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x31),
174*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
175*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
176*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
177*d656610eSBruce Suen 					      &write_cache_variants,
178*d656610eSBruce Suen 					      &update_cache_variants),
179*d656610eSBruce Suen 		     0,
180*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
181*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
182*d656610eSBruce Suen 	SPINAND_INFO("XT26G11D",
183*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x34),
184*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
185*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
186*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
187*d656610eSBruce Suen 					      &write_cache_variants,
188*d656610eSBruce Suen 					      &update_cache_variants),
189*d656610eSBruce Suen 		     0,
190*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
191*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
192*d656610eSBruce Suen 	SPINAND_INFO("XT26Q01D",
193*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51),
194*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
195*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
196*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
197*d656610eSBruce Suen 					      &write_cache_variants,
198*d656610eSBruce Suen 					      &update_cache_variants),
199*d656610eSBruce Suen 		     0,
200*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
201*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
202*d656610eSBruce Suen 	SPINAND_INFO("XT26G02D",
203*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x32),
204*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
205*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
206*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
207*d656610eSBruce Suen 					      &write_cache_variants,
208*d656610eSBruce Suen 					      &update_cache_variants),
209*d656610eSBruce Suen 		     0,
210*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
211*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
212*d656610eSBruce Suen 	SPINAND_INFO("XT26G12D",
213*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x35),
214*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
215*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
216*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
217*d656610eSBruce Suen 					      &write_cache_variants,
218*d656610eSBruce Suen 					      &update_cache_variants),
219*d656610eSBruce Suen 		     0,
220*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
221*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
222*d656610eSBruce Suen 	SPINAND_INFO("XT26Q02D",
223*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52),
224*d656610eSBruce Suen 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
225*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
226*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
227*d656610eSBruce Suen 					      &write_cache_variants,
228*d656610eSBruce Suen 					      &update_cache_variants),
229*d656610eSBruce Suen 		     0,
230*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
231*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
232*d656610eSBruce Suen 	SPINAND_INFO("XT26G04D",
233*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x33),
234*d656610eSBruce Suen 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
235*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
236*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
237*d656610eSBruce Suen 					      &write_cache_variants,
238*d656610eSBruce Suen 					      &update_cache_variants),
239*d656610eSBruce Suen 		     0,
240*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
241*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
242*d656610eSBruce Suen 	SPINAND_INFO("XT26Q04D",
243*d656610eSBruce Suen 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x53),
244*d656610eSBruce Suen 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
245*d656610eSBruce Suen 		     NAND_ECCREQ(8, 512),
246*d656610eSBruce Suen 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
247*d656610eSBruce Suen 					      &write_cache_variants,
248*d656610eSBruce Suen 					      &update_cache_variants),
249*d656610eSBruce Suen 		     0,
250*d656610eSBruce Suen 		     SPINAND_ECCINFO(&xt26xxxd_ooblayout,
251*d656610eSBruce Suen 				     xt26xxxd_ecc_get_status)),
252f4c5c7f9SFelix Matouschek };
253f4c5c7f9SFelix Matouschek 
254f4c5c7f9SFelix Matouschek static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
255f4c5c7f9SFelix Matouschek };
256f4c5c7f9SFelix Matouschek 
257f4c5c7f9SFelix Matouschek const struct spinand_manufacturer xtx_spinand_manufacturer = {
258f4c5c7f9SFelix Matouschek 	.id = SPINAND_MFR_XTX,
259f4c5c7f9SFelix Matouschek 	.name = "XTX",
260f4c5c7f9SFelix Matouschek 	.chips = xtx_spinand_table,
261f4c5c7f9SFelix Matouschek 	.nchips = ARRAY_SIZE(xtx_spinand_table),
262f4c5c7f9SFelix Matouschek 	.ops = &xtx_spinand_manuf_ops,
263f4c5c7f9SFelix Matouschek };
264