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