Commit cfd93d7c908e71d99996be93d2b031ad3fddc292

Authored by Jeff Kletsky
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