xref: /rk3399_rockchip-uboot/drivers/mtd/nand/spi/hyf.c (revision 9d5c314b35defd1c678227deba13c3b4dfd4a0f3)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020 exceet electronics GmbH
4  *
5  * Authors:
6  *	Frieder Schrempf <frieder.schrempf@exceet.de>
7  *	Boris Brezillon <boris.brezillon@bootlin.com>
8  */
9 
10 #ifndef __UBOOT__
11 #include <linux/device.h>
12 #include <linux/kernel.h>
13 #endif
14 #include <linux/mtd/spinand.h>
15 
16 #define SPINAND_MFR_HYF		0xC9
17 
18 static SPINAND_OP_VARIANTS(read_cache_variants,
19 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
20 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
21 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
22 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
23 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
24 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
25 
26 static SPINAND_OP_VARIANTS(write_cache_variants,
27 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
28 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
29 
30 static SPINAND_OP_VARIANTS(update_cache_variants,
31 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
32 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
33 
34 static int hyf1gq4upacae_ooblayout_ecc(struct mtd_info *mtd, int section,
35 				       struct mtd_oob_region *region)
36 {
37 	if (section)
38 		return -ERANGE;
39 
40 	region->offset = 64;
41 	region->length = 64;
42 
43 	return 0;
44 }
45 
46 static int hyf1gq4upacae_ooblayout_free(struct mtd_info *mtd, int section,
47 					struct mtd_oob_region *region)
48 {
49 	if (section)
50 		return -ERANGE;
51 
52 	region->offset = 1;
53 	region->length = 63;
54 
55 	return 0;
56 }
57 
58 static const struct mtd_ooblayout_ops hyf1gq4upacae_ooblayout = {
59 	.ecc = hyf1gq4upacae_ooblayout_ecc,
60 	.rfree = hyf1gq4upacae_ooblayout_free,
61 };
62 
63 static int hyf1gq4udacae_ooblayout_ecc(struct mtd_info *mtd, int section,
64 				       struct mtd_oob_region *region)
65 {
66 	if (section > 3)
67 		return -ERANGE;
68 
69 	region->offset = (16 * section) + 8;
70 	region->length = 8;
71 
72 	return 0;
73 }
74 
75 static int hyf1gq4udacae_ooblayout_free(struct mtd_info *mtd, int section,
76 					struct mtd_oob_region *region)
77 {
78 	if (section > 3)
79 		return -ERANGE;
80 
81 	region->offset = (16 * section) + 4;
82 	region->length = 4;
83 
84 	return 0;
85 }
86 
87 static const struct mtd_ooblayout_ops hyf1gq4udacae_ooblayout = {
88 	.ecc = hyf1gq4udacae_ooblayout_ecc,
89 	.rfree = hyf1gq4udacae_ooblayout_free,
90 };
91 
92 static int hyf2gq4uaacae_ooblayout_ecc(struct mtd_info *mtd, int section,
93 				       struct mtd_oob_region *region)
94 {
95 	if (section > 3)
96 		return -ERANGE;
97 
98 	region->offset = (32 * section) + 8;
99 	region->length = 24;
100 
101 	return 0;
102 }
103 
104 static int hyf2gq4uaacae_ooblayout_free(struct mtd_info *mtd, int section,
105 					struct mtd_oob_region *region)
106 {
107 	if (section > 3)
108 		return -ERANGE;
109 
110 	region->offset = 16 * section;
111 	region->length = 8;
112 
113 	return 0;
114 }
115 
116 static const struct mtd_ooblayout_ops hyf2gq4uaacae_ooblayout = {
117 	.ecc = hyf2gq4uaacae_ooblayout_ecc,
118 	.rfree = hyf2gq4uaacae_ooblayout_free,
119 };
120 
121 static int hyf1gq4udacae_ecc_get_status(struct spinand_device *spinand,
122 					u8 status)
123 {
124 	struct nand_device *nand = spinand_to_nand(spinand);
125 
126 	switch (status & STATUS_ECC_MASK) {
127 	case STATUS_ECC_NO_BITFLIPS:
128 		return 0;
129 
130 	case STATUS_ECC_UNCOR_ERROR:
131 		return -EBADMSG;
132 
133 	case STATUS_ECC_HAS_BITFLIPS:
134 		return 1;
135 
136 	default:
137 		return nand->eccreq.strength;
138 	}
139 
140 	return -EINVAL;
141 }
142 
143 static const struct spinand_info hyf_spinand_table[] = {
144 	SPINAND_INFO("HYF1GQ4UPACAE", 0xA1,
145 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
146 		     NAND_ECCREQ(1, 512),
147 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
148 					      &write_cache_variants,
149 					      &update_cache_variants),
150 		     SPINAND_HAS_QE_BIT,
151 		     SPINAND_ECCINFO(&hyf1gq4upacae_ooblayout, NULL)),
152 	SPINAND_INFO("HYF1GQ4UDACAE", 0x21,
153 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
154 		     NAND_ECCREQ(4, 512),
155 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
156 					      &write_cache_variants,
157 					      &update_cache_variants),
158 		     SPINAND_HAS_QE_BIT,
159 		     SPINAND_ECCINFO(&hyf1gq4udacae_ooblayout,
160 				     hyf1gq4udacae_ecc_get_status)),
161 	SPINAND_INFO("HYF1GQ4UDACAE", 0x22,
162 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
163 		     NAND_ECCREQ(4, 512),
164 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
165 					      &write_cache_variants,
166 					      &update_cache_variants),
167 		     SPINAND_HAS_QE_BIT,
168 		     SPINAND_ECCINFO(&hyf1gq4udacae_ooblayout,
169 				     hyf1gq4udacae_ecc_get_status)),
170 	SPINAND_INFO("HYF2GQ4UAACAE", 0x52,
171 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
172 		     NAND_ECCREQ(14, 512),
173 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
174 					      &write_cache_variants,
175 					      &update_cache_variants),
176 		     SPINAND_HAS_QE_BIT,
177 		     SPINAND_ECCINFO(&hyf2gq4uaacae_ooblayout,
178 				     hyf1gq4udacae_ecc_get_status)),
179 	SPINAND_INFO("HYF2GQ4UHCCAE", 0x5A,
180 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
181 		     NAND_ECCREQ(14, 512),
182 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
183 					      &write_cache_variants,
184 					      &update_cache_variants),
185 		     SPINAND_HAS_QE_BIT,
186 		     SPINAND_ECCINFO(&hyf2gq4uaacae_ooblayout,
187 				     hyf1gq4udacae_ecc_get_status)),
188 	SPINAND_INFO("HYF4GQ4UAACBE", 0xD4,
189 		     NAND_MEMORG(1, 4096, 128, 64, 2048, 1, 1, 1),
190 		     NAND_ECCREQ(4, 512),
191 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
192 					      &write_cache_variants,
193 					      &update_cache_variants),
194 		     SPINAND_HAS_QE_BIT,
195 		     SPINAND_ECCINFO(&hyf2gq4uaacae_ooblayout,
196 				     hyf1gq4udacae_ecc_get_status)),
197 };
198 
199 /**
200  * hyf_spinand_detect - initialize device related part in spinand_device
201  * struct if it is a hyf device.
202  * @spinand: SPI NAND device structure
203  */
204 static int hyf_spinand_detect(struct spinand_device *spinand)
205 {
206 	u8 *id = spinand->id.data;
207 	int ret;
208 
209 	/*
210 	 * hyf SPI NAND read ID needs a dummy byte, so the first byte in
211 	 * raw_id is garbage.
212 	 */
213 	if (id[1] != SPINAND_MFR_HYF)
214 		return 0;
215 
216 	ret = spinand_match_and_init(spinand, hyf_spinand_table,
217 				     ARRAY_SIZE(hyf_spinand_table),
218 				     id[2]);
219 	if (ret)
220 		return ret;
221 
222 	return 1;
223 }
224 
225 static const struct spinand_manufacturer_ops hyf_spinand_manuf_ops = {
226 	.detect = hyf_spinand_detect,
227 };
228 
229 const struct spinand_manufacturer hyf_spinand_manufacturer = {
230 	.id = SPINAND_MFR_HYF,
231 	.name = "hyf",
232 	.ops = &hyf_spinand_manuf_ops,
233 };
234