xref: /linux/drivers/mtd/nand/spi/fmsh.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
4  *
5  * Author: Dingqiang Lin <jon.lin@rock-chips.com>
6  */
7 
8 #include <linux/device.h>
9 #include <linux/kernel.h>
10 #include <linux/mtd/spinand.h>
11 
12 #define FM25G01B_STATUS_ECC_MASK		(7 << 4)
13 	#define FM25G01B_STATUS_ECC_NO_BITFLIPS		(0 << 4)
14 	#define FM25G01B_STATUS_ECC_1_3_BITFLIPS	(1 << 4)
15 	#define FM25G01B_STATUS_ECC_4_BITFLIPS		(2 << 4)
16 	#define FM25G01B_STATUS_ECC_5_BITFLIPS		(3 << 4)
17 	#define FM25G01B_STATUS_ECC_6_BITFLIPS		(4 << 4)
18 	#define FM25G01B_STATUS_ECC_7_BITFLIPS		(5 << 4)
19 	#define FM25G01B_STATUS_ECC_8_BITFLIPS		(6 << 4)
20 	#define FM25G01B_STATUS_ECC_UNCOR_ERROR		(7 << 4)
21 
22 #define FM25S01BI3_STATUS_ECC_MASK		(7 << 4)
23 	#define FM25S01BI3_STATUS_ECC_NO_BITFLIPS	(0 << 4)
24 	#define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS	(1 << 4)
25 	#define FM25S01BI3_STATUS_ECC_UNCOR_ERROR	(2 << 4)
26 	#define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS	(3 << 4)
27 	#define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS	(5 << 4)
28 
29 #define SPINAND_MFR_FMSH		0xA1
30 
31 static SPINAND_OP_VARIANTS(read_cache_variants,
32 		SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0),
33 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
34 		SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
35 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
36 		SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
37 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
38 
39 static SPINAND_OP_VARIANTS(write_cache_variants,
40 		SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
41 		SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
42 
43 static SPINAND_OP_VARIANTS(update_cache_variants,
44 		SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
45 		SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
46 
47 static SPINAND_OP_VARIANTS(fm25g_read_cache_variants,
48 		SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
49 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
50 		SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
51 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
52 		SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
53 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
54 
55 static int fm25g01b_ooblayout_ecc(struct mtd_info *mtd, int section,
56 				  struct mtd_oob_region *region)
57 {
58 	if (section)
59 		return -ERANGE;
60 
61 	region->offset = 64;
62 	region->length = 64;
63 
64 	return 0;
65 }
66 
67 static int fm25g01b_ooblayout_free(struct mtd_info *mtd, int section,
68 				   struct mtd_oob_region *region)
69 {
70 	if (section)
71 		return -ERANGE;
72 
73 	/* reserve 2 bytes for the BBM */
74 	region->offset = 2;
75 	region->length = 62;
76 
77 	return 0;
78 }
79 
80 static int fm25g01b_ecc_get_status(struct spinand_device *spinand,
81 				   u8 status)
82 {
83 	switch (status & FM25G01B_STATUS_ECC_MASK) {
84 	case FM25G01B_STATUS_ECC_NO_BITFLIPS:
85 		return 0;
86 
87 	case FM25G01B_STATUS_ECC_1_3_BITFLIPS:
88 		return 3;
89 
90 	case FM25G01B_STATUS_ECC_4_BITFLIPS:
91 		return 4;
92 
93 	case FM25G01B_STATUS_ECC_5_BITFLIPS:
94 		return 5;
95 
96 	case FM25G01B_STATUS_ECC_6_BITFLIPS:
97 		return 6;
98 
99 	case FM25G01B_STATUS_ECC_7_BITFLIPS:
100 		return 7;
101 
102 	case FM25G01B_STATUS_ECC_8_BITFLIPS:
103 		return 8;
104 
105 	case FM25G01B_STATUS_ECC_UNCOR_ERROR:
106 		return -EBADMSG;
107 
108 	default:
109 		break;
110 	}
111 
112 	return -EINVAL;
113 }
114 
115 static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
116 				  struct mtd_oob_region *region)
117 {
118 	return -ERANGE;
119 }
120 
121 static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
122 				   struct mtd_oob_region *region)
123 {
124 	if (section)
125 		return -ERANGE;
126 
127 	region->offset = 2;
128 	region->length = 62;
129 
130 	return 0;
131 }
132 
133 static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
134 				     u8 status)
135 {
136 	switch (status & FM25S01BI3_STATUS_ECC_MASK) {
137 	case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
138 		return 0;
139 
140 	case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
141 		return -EBADMSG;
142 
143 	case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
144 		return 3;
145 
146 	case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
147 		return 6;
148 
149 	case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
150 		return 8;
151 
152 	default:
153 		break;
154 	}
155 
156 	return -EINVAL;
157 }
158 
159 static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
160 				    struct mtd_oob_region *region)
161 {
162 	if (section)
163 		return -ERANGE;
164 
165 	region->offset = 64;
166 	region->length = 64;
167 
168 	return 0;
169 }
170 
171 static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
172 				     struct mtd_oob_region *region)
173 {
174 	if (section > 3)
175 		return -ERANGE;
176 
177 	region->offset = (16 * section) + 4;
178 	region->length = 12;
179 
180 	return 0;
181 }
182 
183 static const struct mtd_ooblayout_ops fm25g01b_ooblayout = {
184 	.ecc = fm25g01b_ooblayout_ecc,
185 	.free = fm25g01b_ooblayout_free,
186 };
187 
188 static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
189 	.ecc = fm25s01a_ooblayout_ecc,
190 	.free = fm25s01a_ooblayout_free,
191 };
192 
193 static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
194 	.ecc = fm25s01bi3_ooblayout_ecc,
195 	.free = fm25s01bi3_ooblayout_free,
196 };
197 
198 static const struct spinand_info fmsh_spinand_table[] = {
199 	SPINAND_INFO("FM25G01B",
200 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd1),
201 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1),
202 		     NAND_ECCREQ(8, 528),
203 		     SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
204 					      &write_cache_variants,
205 					      &update_cache_variants),
206 		     SPINAND_HAS_QE_BIT,
207 		     SPINAND_ECCINFO(&fm25g01b_ooblayout,
208 				     fm25g01b_ecc_get_status)),
209 	SPINAND_INFO("FM25G02B",
210 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd2),
211 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1),
212 		     NAND_ECCREQ(8, 528),
213 		     SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
214 					      &write_cache_variants,
215 					      &update_cache_variants),
216 		     SPINAND_HAS_QE_BIT,
217 		     SPINAND_ECCINFO(&fm25g01b_ooblayout,
218 				     fm25g01b_ecc_get_status)),
219 	SPINAND_INFO("FM25S01A",
220 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
221 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
222 		     NAND_ECCREQ(1, 512),
223 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
224 					      &write_cache_variants,
225 					      &update_cache_variants),
226 		     0,
227 		     SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
228 	SPINAND_INFO("FM25S01BI3",
229 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
230 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
231 		     NAND_ECCREQ(8, 512),
232 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
233 					      &write_cache_variants,
234 					      &update_cache_variants),
235 		     SPINAND_HAS_QE_BIT,
236 		     SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
237 				      fm25s01bi3_ecc_get_status)),
238 };
239 
240 static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
241 };
242 
243 const struct spinand_manufacturer fmsh_spinand_manufacturer = {
244 	.id = SPINAND_MFR_FMSH,
245 	.name = "Fudan Micro",
246 	.chips = fmsh_spinand_table,
247 	.nchips = ARRAY_SIZE(fmsh_spinand_table),
248 	.ops = &fmsh_spinand_manuf_ops,
249 };
250