Commit cfd93d7c908e71d99996be93d2b031ad3fddc292
Committed by
Miquel Raynal
1 parent
878844908e
mtd: spinand: Add support for GigaDevice GD5F1GQ4UFxxG
The GigaDevice GD5F1GQ4UFxxG SPI NAND is in current production devices and, while it has the same logical layout as the E-series devices, it differs in the SPI interfacing in significant ways. This support is contingent on previous commits to: * Add support for two-byte device IDs * Define macros for page-read ops with three-byte addresses http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ Signed-off-by: Jeff Kletsky <git-commits@allycomm.com> Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Showing 1 changed file with 64 additions and 15 deletions Side-by-side Diff
drivers/mtd/nand/spi/gigadevice.c
... | ... | @@ -9,11 +9,17 @@ |
9 | 9 | #include <linux/mtd/spinand.h> |
10 | 10 | |
11 | 11 | #define SPINAND_MFR_GIGADEVICE 0xC8 |
12 | + | |
12 | 13 | #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) |
13 | 14 | #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) |
14 | 15 | |
15 | 16 | #define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 |
16 | 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 | + | |
17 | 23 | static SPINAND_OP_VARIANTS(read_cache_variants, |
18 | 24 | SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), |
19 | 25 | SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), |
... | ... | @@ -22,6 +28,14 @@ |
22 | 28 | SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), |
23 | 29 | SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); |
24 | 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 | + | |
25 | 39 | static SPINAND_OP_VARIANTS(write_cache_variants, |
26 | 40 | SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), |
27 | 41 | SPINAND_PROG_LOAD(true, 0, NULL, 0)); |
... | ... | @@ -59,6 +73,11 @@ |
59 | 73 | return 0; |
60 | 74 | } |
61 | 75 | |
76 | +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | |
77 | + .ecc = gd5fxgq4xa_ooblayout_ecc, | |
78 | + .free = gd5fxgq4xa_ooblayout_free, | |
79 | +}; | |
80 | + | |
62 | 81 | static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, |
63 | 82 | u8 status) |
64 | 83 | { |
... | ... | @@ -83,7 +102,7 @@ |
83 | 102 | return -EINVAL; |
84 | 103 | } |
85 | 104 | |
86 | -static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, | |
105 | +static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, | |
87 | 106 | struct mtd_oob_region *region) |
88 | 107 | { |
89 | 108 | if (section) |
... | ... | @@ -95,7 +114,7 @@ |
95 | 114 | return 0; |
96 | 115 | } |
97 | 116 | |
98 | -static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, | |
117 | +static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, | |
99 | 118 | struct mtd_oob_region *region) |
100 | 119 | { |
101 | 120 | if (section) |
... | ... | @@ -108,6 +127,11 @@ |
108 | 127 | return 0; |
109 | 128 | } |
110 | 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 | + | |
111 | 135 | static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, |
112 | 136 | u8 status) |
113 | 137 | { |
114 | 138 | |
115 | 139 | |
... | ... | @@ -150,16 +174,26 @@ |
150 | 174 | return -EINVAL; |
151 | 175 | } |
152 | 176 | |
153 | -static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | |
154 | - .ecc = gd5fxgq4xa_ooblayout_ecc, | |
155 | - .free = gd5fxgq4xa_ooblayout_free, | |
156 | -}; | |
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; | |
157 | 183 | |
158 | -static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = { | |
159 | - .ecc = gd5fxgq4uexxg_ooblayout_ecc, | |
160 | - .free = gd5fxgq4uexxg_ooblayout_free, | |
161 | -}; | |
184 | + case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: | |
185 | + return 3; | |
162 | 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 | + | |
163 | 197 | static const struct spinand_info gigadevice_spinand_table[] = { |
164 | 198 | SPINAND_INFO("GD5F1GQ4xA", 0xF1, |
165 | 199 | NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), |
166 | 200 | |
167 | 201 | |
168 | 202 | |
169 | 203 | |
170 | 204 | |
... | ... | @@ -195,25 +229,40 @@ |
195 | 229 | &write_cache_variants, |
196 | 230 | &update_cache_variants), |
197 | 231 | 0, |
198 | - SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout, | |
232 | + SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, | |
199 | 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)), | |
200 | 243 | }; |
201 | 244 | |
202 | 245 | static int gigadevice_spinand_detect(struct spinand_device *spinand) |
203 | 246 | { |
204 | 247 | u8 *id = spinand->id.data; |
248 | + u16 did; | |
205 | 249 | int ret; |
206 | 250 | |
207 | 251 | /* |
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. | |
252 | + * Earlier GDF5-series devices (A,E) return [0][MID][DID] | |
253 | + * Later (F) devices return [MID][DID1][DID2] | |
210 | 254 | */ |
211 | - if (id[1] != SPINAND_MFR_GIGADEVICE) | |
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 | |
212 | 261 | return 0; |
213 | 262 | |
214 | 263 | ret = spinand_match_and_init(spinand, gigadevice_spinand_table, |
215 | 264 | ARRAY_SIZE(gigadevice_spinand_table), |
216 | - id[2]); | |
265 | + did); | |
217 | 266 | if (ret) |
218 | 267 | return ret; |
219 | 268 |