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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |