Merge tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
[platform/kernel/linux-starfive.git] / drivers / mtd / nand / spi / gigadevice.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Author:
4  *      Chuanhong Guo <gch981213@gmail.com>
5  */
6
7 #include <linux/device.h>
8 #include <linux/kernel.h>
9 #include <linux/mtd/spinand.h>
10
11 #define SPINAND_MFR_GIGADEVICE                  0xC8
12
13 #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS      (1 << 4)
14 #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS        (3 << 4)
15
16 #define GD5FXGQ4UEXXG_REG_STATUS2               0xf0
17
18 #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK          (7 << 4)
19 #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS   (0 << 4)
20 #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS  (1 << 4)
21 #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR   (7 << 4)
22
23 static SPINAND_OP_VARIANTS(read_cache_variants,
24                 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
25                 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
26                 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
27                 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
28                 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
29                 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
30
31 static SPINAND_OP_VARIANTS(read_cache_variants_f,
32                 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
33                 SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
34                 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
35                 SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0),
36                 SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
37                 SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
38
39 static SPINAND_OP_VARIANTS(write_cache_variants,
40                 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
41                 SPINAND_PROG_LOAD(true, 0, NULL, 0));
42
43 static SPINAND_OP_VARIANTS(update_cache_variants,
44                 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
45                 SPINAND_PROG_LOAD(false, 0, NULL, 0));
46
47 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
48                                   struct mtd_oob_region *region)
49 {
50         if (section > 3)
51                 return -ERANGE;
52
53         region->offset = (16 * section) + 8;
54         region->length = 8;
55
56         return 0;
57 }
58
59 static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
60                                    struct mtd_oob_region *region)
61 {
62         if (section > 3)
63                 return -ERANGE;
64
65         if (section) {
66                 region->offset = 16 * section;
67                 region->length = 8;
68         } else {
69                 /* section 0 has one byte reserved for bad block mark */
70                 region->offset = 1;
71                 region->length = 7;
72         }
73         return 0;
74 }
75
76 static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
77         .ecc = gd5fxgq4xa_ooblayout_ecc,
78         .free = gd5fxgq4xa_ooblayout_free,
79 };
80
81 static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
82                                          u8 status)
83 {
84         switch (status & STATUS_ECC_MASK) {
85         case STATUS_ECC_NO_BITFLIPS:
86                 return 0;
87
88         case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
89                 /* 1-7 bits are flipped. return the maximum. */
90                 return 7;
91
92         case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
93                 return 8;
94
95         case STATUS_ECC_UNCOR_ERROR:
96                 return -EBADMSG;
97
98         default:
99                 break;
100         }
101
102         return -EINVAL;
103 }
104
105 static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
106                                        struct mtd_oob_region *region)
107 {
108         if (section)
109                 return -ERANGE;
110
111         region->offset = 64;
112         region->length = 64;
113
114         return 0;
115 }
116
117 static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
118                                         struct mtd_oob_region *region)
119 {
120         if (section)
121                 return -ERANGE;
122
123         /* Reserve 1 bytes for the BBM. */
124         region->offset = 1;
125         region->length = 63;
126
127         return 0;
128 }
129
130 static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = {
131         .ecc = gd5fxgq4_variant2_ooblayout_ecc,
132         .free = gd5fxgq4_variant2_ooblayout_free,
133 };
134
135 static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
136                                         u8 status)
137 {
138         u8 status2;
139         struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
140                                                       &status2);
141         int ret;
142
143         switch (status & STATUS_ECC_MASK) {
144         case STATUS_ECC_NO_BITFLIPS:
145                 return 0;
146
147         case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
148                 /*
149                  * Read status2 register to determine a more fine grained
150                  * bit error status
151                  */
152                 ret = spi_mem_exec_op(spinand->spimem, &op);
153                 if (ret)
154                         return ret;
155
156                 /*
157                  * 4 ... 7 bits are flipped (1..4 can't be detected, so
158                  * report the maximum of 4 in this case
159                  */
160                 /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
161                 return ((status & STATUS_ECC_MASK) >> 2) |
162                         ((status2 & STATUS_ECC_MASK) >> 4);
163
164         case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
165                 return 8;
166
167         case STATUS_ECC_UNCOR_ERROR:
168                 return -EBADMSG;
169
170         default:
171                 break;
172         }
173
174         return -EINVAL;
175 }
176
177 static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
178                                         u8 status)
179 {
180         switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
181         case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
182                 return 0;
183
184         case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
185                 return 3;
186
187         case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
188                 return -EBADMSG;
189
190         default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
191                 return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
192         }
193
194         return -EINVAL;
195 }
196
197 static const struct spinand_info gigadevice_spinand_table[] = {
198         SPINAND_INFO("GD5F1GQ4xA", 0xF1,
199                      NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
200                      NAND_ECCREQ(8, 512),
201                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
202                                               &write_cache_variants,
203                                               &update_cache_variants),
204                      0,
205                      SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
206                                      gd5fxgq4xa_ecc_get_status)),
207         SPINAND_INFO("GD5F2GQ4xA", 0xF2,
208                      NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
209                      NAND_ECCREQ(8, 512),
210                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
211                                               &write_cache_variants,
212                                               &update_cache_variants),
213                      0,
214                      SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
215                                      gd5fxgq4xa_ecc_get_status)),
216         SPINAND_INFO("GD5F4GQ4xA", 0xF4,
217                      NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1),
218                      NAND_ECCREQ(8, 512),
219                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
220                                               &write_cache_variants,
221                                               &update_cache_variants),
222                      0,
223                      SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
224                                      gd5fxgq4xa_ecc_get_status)),
225         SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
226                      NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
227                      NAND_ECCREQ(8, 512),
228                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
229                                               &write_cache_variants,
230                                               &update_cache_variants),
231                      0,
232                      SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
233                                      gd5fxgq4uexxg_ecc_get_status)),
234         SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148,
235                      NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
236                      NAND_ECCREQ(8, 512),
237                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
238                                               &write_cache_variants,
239                                               &update_cache_variants),
240                      0,
241                      SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
242                                      gd5fxgq4ufxxg_ecc_get_status)),
243 };
244
245 static int gigadevice_spinand_detect(struct spinand_device *spinand)
246 {
247         u8 *id = spinand->id.data;
248         u16 did;
249         int ret;
250
251         /*
252          * Earlier GDF5-series devices (A,E) return [0][MID][DID]
253          * Later (F) devices return [MID][DID1][DID2]
254          */
255
256         if (id[0] == SPINAND_MFR_GIGADEVICE)
257                 did = (id[1] << 8) + id[2];
258         else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE)
259                 did = id[2];
260         else
261                 return 0;
262
263         ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
264                                      ARRAY_SIZE(gigadevice_spinand_table),
265                                      did);
266         if (ret)
267                 return ret;
268
269         return 1;
270 }
271
272 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
273         .detect = gigadevice_spinand_detect,
274 };
275
276 const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
277         .id = SPINAND_MFR_GIGADEVICE,
278         .name = "GigaDevice",
279         .ops = &gigadevice_spinand_manuf_ops,
280 };