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