blob: 6749abc54ce5011484c3896285a22c7bae2e7888 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 ASR Micro Limited
4 *
5 * Authors:
6 * Fei Lv <feilv@asrmicro.com>
7 */
8
9#include <linux/device.h>
10#include <linux/kernel.h>
11#include <linux/mtd/spinand.h>
12
13#define SPINAND_MFR_XTX 0xA1
14#define SPINAND_MFR_XTX2 0x0B
15#define SPINAND_MFR_XTX3 0x2C
16
17#define XT26Q01D_STATUS_ECC_MASK (0xF << 4)
18#define XT26Q01D_STATUS_ECC_NO_BITFLIPS (0 << 4)
19#define XT26Q01D_STATUS_ECC_1_4_BITFLIPS (1 << 4)
20#define XT26Q01D_STATUS_ECC_5_BITFLIPS (5 << 4)
21#define XT26Q01D_STATUS_ECC_6_BITFLIPS (9 << 4)
22#define XT26Q01D_STATUS_ECC_7_BITFLIPS (0xD << 4)
23#define XT26Q01D_STATUS_ECC_8_BITFLIPS (3 << 4)
24
25#define XT26Q01E_STATUS_ECC_MASK GENMASK(6, 4)
26#define XT26Q01E_STATUS_ECC_NO_BITFLIPS (0 << 4)
27#define XT26Q01E_STATUS_ECC_1_3_BITFLIPS (1 << 4)
28#define XT26Q01E_STATUS_ECC_UNCOR_ERROR (2 << 4)
29#define XT26Q01E_STATUS_ECC_4_6_BITFLIPS (3 << 4)
30#define XT26Q01E_STATUS_ECC_7_8_BITFLIPS (5 << 4)
31
32static SPINAND_OP_VARIANTS(read_cache_variants,
33 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
34 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
35 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
36 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
37 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
38 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
39
40static SPINAND_OP_VARIANTS(read_cache_variants2,
41 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
42 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
43 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
44 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
45 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
46 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
47
48static SPINAND_OP_VARIANTS(write_cache_variants,
49 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
50 SPINAND_PROG_LOAD(true, 0, NULL, 0));
51
52static SPINAND_OP_VARIANTS(update_cache_variants,
53 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
54 SPINAND_PROG_LOAD(false, 0, NULL, 0));
55
56static int pn26q01a_ooblayout_ecc(struct mtd_info *mtd, int section,
57 struct mtd_oob_region *region)
58{
59 if (section > 3)
60 return -ERANGE;
61
62 region->offset = (9 * section) + 6;
63 region->length = 13;
64
65 return 0;
66}
67
68static int pn26q01a_ooblayout_free(struct mtd_info *mtd, int section,
69 struct mtd_oob_region *region)
70{
71 if (section > 3)
72 return -ERANGE;
73
74 region->offset = (16 * section) + 64;
75 region->length = 16;
76
77 return 0;
78}
79
80static const struct mtd_ooblayout_ops pn26q01a_ooblayout = {
81 .ecc = pn26q01a_ooblayout_ecc,
82 .free = pn26q01a_ooblayout_free,
83};
84
85static int pn26q01a_get_ecc_status(struct spinand_device *spinand, u8 status)
86{
87 switch (status & STATUS_ECC_MASK) {
88 case STATUS_ECC_NO_BITFLIPS:
89 return 0;
90
91 case STATUS_ECC_HAS_BITFLIPS:
92 return 7;
93
94 case STATUS_ECC_UNCOR_ERROR:
95 return -EBADMSG;
96
97 default:
98 return 8;
99 }
100
101 return -EINVAL;
102}
103
104static int xt26q01d_ooblayout_ecc(struct mtd_info *mtd, int section,
105 struct mtd_oob_region *region)
106{
107 return -ERANGE;
108}
109
110static int xt26q01d_ooblayout_free(struct mtd_info *mtd, int section,
111 struct mtd_oob_region *region)
112{
113 if (section > 3)
114 return -ERANGE;
115
116 if (section == 0) {
117 region->offset = 2;
118 region->length = 14;
119 } else {
120 region->offset = 16 * section;
121 region->length = 16;
122 }
123
124 return 0;
125}
126
127static const struct mtd_ooblayout_ops xt26q01d_ooblayout = {
128 .ecc = xt26q01d_ooblayout_ecc,
129 .free = xt26q01d_ooblayout_free,
130};
131
132static int xt26q01d_get_ecc_status(struct spinand_device *spinand, u8 status)
133{
134 switch (status & STATUS_ECC_MASK) {
135 case STATUS_ECC_NO_BITFLIPS:
136 return 0;
137
138 case STATUS_ECC_HAS_BITFLIPS:
139 switch (status & XT26Q01D_STATUS_ECC_MASK) {
140 case XT26Q01D_STATUS_ECC_1_4_BITFLIPS:
141 return 4;
142 case XT26Q01D_STATUS_ECC_5_BITFLIPS:
143 return 5;
144 case XT26Q01D_STATUS_ECC_6_BITFLIPS:
145 return 6;
146 case XT26Q01D_STATUS_ECC_7_BITFLIPS:
147 return 7;
148 }
149 break;
150
151 case STATUS_ECC_UNCOR_ERROR:
152 return -EBADMSG;
153
154 default:
155 return 8;
156 }
157
158 return -EINVAL;
159}
160
161static int xt26q01e_ecc_get_status(struct spinand_device *spinand, u8 status)
162{
163 switch (status & XT26Q01E_STATUS_ECC_MASK) {
164 case XT26Q01E_STATUS_ECC_NO_BITFLIPS:
165 return 0;
166
167 case XT26Q01E_STATUS_ECC_1_3_BITFLIPS:
168 return 3;
169
170 case XT26Q01E_STATUS_ECC_4_6_BITFLIPS:
171 return 6;
172
173 case XT26Q01E_STATUS_ECC_7_8_BITFLIPS:
174 return 8;
175
176 case XT26Q01E_STATUS_ECC_UNCOR_ERROR:
177 return -EBADMSG;
178
179 default:
180 break;
181 }
182
183 return -EINVAL;
184}
185
186static const struct spinand_info xtx_spinand_table[] = {
187 SPINAND_INFO("PN26Q01AWSIUG", 0xC1,
188 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
189 NAND_ECCREQ(8, 512),
190 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
191 &write_cache_variants,
192 &update_cache_variants),
193 SPINAND_HAS_QE_BIT | SPINAND_RDM_CMD_LOAD_PAGE | \
194 SPINAND_ECC_EN_IN_FEATURE,
195 SPINAND_ECCINFO(&pn26q01a_ooblayout,
196 pn26q01a_get_ecc_status)),
197 SPINAND_INFO("XT26Q01D-BE", 0x51,
198 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
199 NAND_ECCREQ(8, 512),
200 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
201 &write_cache_variants,
202 &update_cache_variants),
203 SPINAND_HAS_QE_BIT,
204 SPINAND_ECCINFO(&xt26q01d_ooblayout,
205 xt26q01d_get_ecc_status)),
206 SPINAND_INFO("XT26Q02D", 0x52,
207 NAND_MEMORG(1, 2048, 64, 64, 2048, 20, 1, 1, 1),
208 NAND_ECCREQ(8, 512),
209 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
210 &write_cache_variants,
211 &update_cache_variants),
212 SPINAND_HAS_QE_BIT,
213 SPINAND_ECCINFO(&xt26q01d_ooblayout,
214 xt26q01d_get_ecc_status)),
215 SPINAND_INFO("XT26Q02E", 0x25,
216 NAND_MEMORG(1, 2048, 64, 64, 2048, 20, 2, 1, 1),
217 NAND_ECCREQ(8, 512),
218 SPINAND_INFO_OP_VARIANTS_TIMING(
219 &read_cache_variants2,
220 &write_cache_variants,
221 &update_cache_variants,
222 8, 2, 2, 52000000),
223 SPINAND_HAS_QE_BIT,
224 SPINAND_ECCINFO(&xt26q01d_ooblayout,
225 xt26q01e_ecc_get_status)),
226};
227
228/**
229 * xtx_spinand_detect - initialize device related part in spinand_device
230 * struct if it is a Winbond device.
231 * @spinand: SPI NAND device structure
232 */
233static int xtx_spinand_detect(struct spinand_device *spinand)
234{
235 u8 *id = spinand->id.data;
236 int ret;
237
238 if (id[1] != SPINAND_MFR_XTX && id[1] != SPINAND_MFR_XTX2 &&
239 id[1] != SPINAND_MFR_XTX3)
240 return 0;
241
242 ret = spinand_match_and_init(spinand, xtx_spinand_table,
243 ARRAY_SIZE(xtx_spinand_table), id[2]);
244 if (ret)
245 return ret;
246
247 return 1;
248}
249
250static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
251 .detect = xtx_spinand_detect,
252};
253
254const struct spinand_manufacturer xtx_spinand_manufacturer = {
255 .id = SPINAND_MFR_XTX,
256 .name = "XTX",
257 .ops = &xtx_spinand_manuf_ops,
258};
259
260const struct spinand_manufacturer xtx2_spinand_manufacturer = {
261 .id = SPINAND_MFR_XTX2,
262 .name = "XTX2",
263 .ops = &xtx_spinand_manuf_ops,
264};
265
266const struct spinand_manufacturer xtx3_spinand_manufacturer = {
267 .id = SPINAND_MFR_XTX3,
268 .name = "XTX3",
269 .ops = &xtx_spinand_manuf_ops,
270};