Merge tag 'for-5.3/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/devic...
[platform/kernel/linux-rpi.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 #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS      (1 << 4)
13 #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS        (3 << 4)
14
15 #define GD5FXGQ4UEXXG_REG_STATUS2               0xf0
16
17 static SPINAND_OP_VARIANTS(read_cache_variants,
18                 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
19                 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
20                 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
21                 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
22                 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
23                 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
24
25 static SPINAND_OP_VARIANTS(write_cache_variants,
26                 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
27                 SPINAND_PROG_LOAD(true, 0, NULL, 0));
28
29 static SPINAND_OP_VARIANTS(update_cache_variants,
30                 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
31                 SPINAND_PROG_LOAD(false, 0, NULL, 0));
32
33 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
34                                   struct mtd_oob_region *region)
35 {
36         if (section > 3)
37                 return -ERANGE;
38
39         region->offset = (16 * section) + 8;
40         region->length = 8;
41
42         return 0;
43 }
44
45 static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
46                                    struct mtd_oob_region *region)
47 {
48         if (section > 3)
49                 return -ERANGE;
50
51         if (section) {
52                 region->offset = 16 * section;
53                 region->length = 8;
54         } else {
55                 /* section 0 has one byte reserved for bad block mark */
56                 region->offset = 1;
57                 region->length = 7;
58         }
59         return 0;
60 }
61
62 static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
63                                          u8 status)
64 {
65         switch (status & STATUS_ECC_MASK) {
66         case STATUS_ECC_NO_BITFLIPS:
67                 return 0;
68
69         case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
70                 /* 1-7 bits are flipped. return the maximum. */
71                 return 7;
72
73         case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
74                 return 8;
75
76         case STATUS_ECC_UNCOR_ERROR:
77                 return -EBADMSG;
78
79         default:
80                 break;
81         }
82
83         return -EINVAL;
84 }
85
86 static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
87                                        struct mtd_oob_region *region)
88 {
89         if (section)
90                 return -ERANGE;
91
92         region->offset = 64;
93         region->length = 64;
94
95         return 0;
96 }
97
98 static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section,
99                                         struct mtd_oob_region *region)
100 {
101         if (section)
102                 return -ERANGE;
103
104         /* Reserve 1 bytes for the BBM. */
105         region->offset = 1;
106         region->length = 63;
107
108         return 0;
109 }
110
111 static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
112                                         u8 status)
113 {
114         u8 status2;
115         struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
116                                                       &status2);
117         int ret;
118
119         switch (status & STATUS_ECC_MASK) {
120         case STATUS_ECC_NO_BITFLIPS:
121                 return 0;
122
123         case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
124                 /*
125                  * Read status2 register to determine a more fine grained
126                  * bit error status
127                  */
128                 ret = spi_mem_exec_op(spinand->spimem, &op);
129                 if (ret)
130                         return ret;
131
132                 /*
133                  * 4 ... 7 bits are flipped (1..4 can't be detected, so
134                  * report the maximum of 4 in this case
135                  */
136                 /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
137                 return ((status & STATUS_ECC_MASK) >> 2) |
138                         ((status2 & STATUS_ECC_MASK) >> 4);
139
140         case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
141                 return 8;
142
143         case STATUS_ECC_UNCOR_ERROR:
144                 return -EBADMSG;
145
146         default:
147                 break;
148         }
149
150         return -EINVAL;
151 }
152
153 static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
154         .ecc = gd5fxgq4xa_ooblayout_ecc,
155         .free = gd5fxgq4xa_ooblayout_free,
156 };
157
158 static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = {
159         .ecc = gd5fxgq4uexxg_ooblayout_ecc,
160         .free = gd5fxgq4uexxg_ooblayout_free,
161 };
162
163 static const struct spinand_info gigadevice_spinand_table[] = {
164         SPINAND_INFO("GD5F1GQ4xA", 0xF1,
165                      NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
166                      NAND_ECCREQ(8, 512),
167                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
168                                               &write_cache_variants,
169                                               &update_cache_variants),
170                      0,
171                      SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
172                                      gd5fxgq4xa_ecc_get_status)),
173         SPINAND_INFO("GD5F2GQ4xA", 0xF2,
174                      NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 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(&gd5fxgq4xa_ooblayout,
181                                      gd5fxgq4xa_ecc_get_status)),
182         SPINAND_INFO("GD5F4GQ4xA", 0xF4,
183                      NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1),
184                      NAND_ECCREQ(8, 512),
185                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
186                                               &write_cache_variants,
187                                               &update_cache_variants),
188                      0,
189                      SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
190                                      gd5fxgq4xa_ecc_get_status)),
191         SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
192                      NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
193                      NAND_ECCREQ(8, 512),
194                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
195                                               &write_cache_variants,
196                                               &update_cache_variants),
197                      0,
198                      SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout,
199                                      gd5fxgq4uexxg_ecc_get_status)),
200 };
201
202 static int gigadevice_spinand_detect(struct spinand_device *spinand)
203 {
204         u8 *id = spinand->id.data;
205         int ret;
206
207         /*
208          * For GD NANDs, There is an address byte needed to shift in before IDs
209          * are read out, so the first byte in raw_id is dummy.
210          */
211         if (id[1] != SPINAND_MFR_GIGADEVICE)
212                 return 0;
213
214         ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
215                                      ARRAY_SIZE(gigadevice_spinand_table),
216                                      id[2]);
217         if (ret)
218                 return ret;
219
220         return 1;
221 }
222
223 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
224         .detect = gigadevice_spinand_detect,
225 };
226
227 const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
228         .id = SPINAND_MFR_GIGADEVICE,
229         .name = "GigaDevice",
230         .ops = &gigadevice_spinand_manuf_ops,
231 };