1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2018 Stefan Roese <sr@denx.de>
4 *
5 * Derived from drivers/mtd/nand/spi/micron.c
6 * Copyright (c) 2016-2017 Micron Technology, Inc.
7 */
8
9 #ifndef __UBOOT__
10 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #endif
13 #include <linux/mtd/spinand.h>
14
15 #define SPINAND_MFR_GIGADEVICE 0xc8
16
17 #define GIGADEVICE_STATUS_ECC_MASK GENMASK(5, 4)
18 #define GIGADEVICE_STATUS_ECC_NO_BITFLIPS (0 << 4)
19 #define GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS (1 << 4)
20 #define GIGADEVICE_STATUS_ECC_8_BITFLIPS (3 << 4)
21
22 static SPINAND_OP_VARIANTS(read_cache_variants,
23 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
24 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
25 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
26 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
27 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
28 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
29
30 static SPINAND_OP_VARIANTS(write_cache_variants,
31 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
32 SPINAND_PROG_LOAD(true, 0, NULL, 0));
33
34 static SPINAND_OP_VARIANTS(update_cache_variants,
35 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
36 SPINAND_PROG_LOAD(false, 0, NULL, 0));
37
gd5f1gq4u_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)38 static int gd5f1gq4u_ooblayout_ecc(struct mtd_info *mtd, int section,
39 struct mtd_oob_region *region)
40 {
41 if (section)
42 return -ERANGE;
43
44 region->offset = 64;
45 region->length = 64;
46
47 return 0;
48 }
49
gd5f1gq4u_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)50 static int gd5f1gq4u_ooblayout_free(struct mtd_info *mtd, int section,
51 struct mtd_oob_region *region)
52 {
53 if (section)
54 return -ERANGE;
55
56 /* Reserve 2 bytes for the BBM. */
57 region->offset = 2;
58 region->length = 62;
59
60 return 0;
61 }
62
63 static const struct mtd_ooblayout_ops gd5f1gq4u_ooblayout = {
64 .ecc = gd5f1gq4u_ooblayout_ecc,
65 .free = gd5f1gq4u_ooblayout_free,
66 };
67
gd5f1gq4u_ecc_get_status(struct spinand_device * spinand,u8 status)68 static int gd5f1gq4u_ecc_get_status(struct spinand_device *spinand,
69 u8 status)
70 {
71 if (status)
72 debug("%s (%d): status=%02x\n", __func__, __LINE__, status);
73
74 switch (status & GIGADEVICE_STATUS_ECC_MASK) {
75 case STATUS_ECC_NO_BITFLIPS:
76 return 0;
77
78 case GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS:
79 return 7;
80
81 case GIGADEVICE_STATUS_ECC_8_BITFLIPS:
82 return 8;
83
84 case STATUS_ECC_UNCOR_ERROR:
85 return -EBADMSG;
86
87 default:
88 break;
89 }
90
91 return -EINVAL;
92 }
93
94 static const struct spinand_info gigadevice_spinand_table[] = {
95 SPINAND_INFO("GD5F1GQ4UC", 0xd1,
96 NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
97 NAND_ECCREQ(8, 2048),
98 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
99 &write_cache_variants,
100 &update_cache_variants),
101 0,
102 SPINAND_ECCINFO(&gd5f1gq4u_ooblayout,
103 gd5f1gq4u_ecc_get_status)),
104 };
105
gigadevice_spinand_detect(struct spinand_device * spinand)106 static int gigadevice_spinand_detect(struct spinand_device *spinand)
107 {
108 u8 *id = spinand->id.data;
109 int ret;
110
111 /*
112 * Gigadevice SPI NAND read ID need a dummy byte,
113 * so the first byte in raw_id is dummy.
114 */
115 if (id[1] != SPINAND_MFR_GIGADEVICE)
116 return 0;
117
118 ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
119 ARRAY_SIZE(gigadevice_spinand_table),
120 id[2]);
121 if (ret)
122 return ret;
123
124 return 1;
125 }
126
127 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
128 .detect = gigadevice_spinand_detect,
129 };
130
131 const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
132 .id = SPINAND_MFR_GIGADEVICE,
133 .name = "GigaDevice",
134 .ops = &gigadevice_spinand_manuf_ops,
135 };
136