1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Author:
4 * Chuanhong Guo <gch981213@gmail.com> - the main driver logic
5 * Martin Kurbanov <mmkurbanov@sberdevices.ru> - OOB layout
6 */
7
8 #include <linux/device.h>
9 #include <linux/kernel.h>
10 #include <linux/mtd/spinand.h>
11 #include <linux/spi/spi-mem.h>
12
13 /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
14 #define SPINAND_MFR_ESMT_C8 0xc8
15
16 #define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7)
17 #define ESMT_F50L1G41LB_CFG_OTP_LOCK \
18 (CFG_OTP_ENABLE | ESMT_F50L1G41LB_CFG_OTP_PROTECT)
19
20 static SPINAND_OP_VARIANTS(read_cache_variants,
21 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
22 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
23 SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0),
24 SPINAND_PAGE_READ_FROM_CACHE_OP(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 /*
35 * OOB spare area map (64 bytes)
36 *
37 * Bad Block Markers
38 * filled by HW and kernel Reserved
39 * | +-----------------------+-----------------------+
40 * | | | |
41 * | | OOB free data Area |non ECC protected |
42 * | +-------------|-----+-----------------|-----+-----------------|-----+
43 * | | | | | | | |
44 * +-|---|----------+--|-----|--------------+--|-----|--------------+--|-----|--------------+
45 * | | | section0 | | | section1 | | | section2 | | | section3 |
46 * +-v-+-v-+---+----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+
47 * | | | | | | | | | | | | | | | | |
48 * |0:1|2:3|4:7|8:15|16:17|18:19|20:23|24:31|32:33|34:35|36:39|40:47|48:49|50:51|52:55|56:63|
49 * | | | | | | | | | | | | | | | | |
50 * +---+---+-^-+--^-+-----+-----+--^--+--^--+-----+-----+--^--+--^--+-----+-----+--^--+--^--+
51 * | | | | | | | |
52 * | +----------------|-----+-----------------|-----+-----------------|-----+
53 * | ECC Area|(Main + Spare) - filled|by ESMT NAND HW |
54 * | | | |
55 * +---------------------+-----------------------+-----------------------+
56 * OOB ECC protected Area - not used due to
57 * partial programming from some filesystems
58 * (like JFFS2 with cleanmarkers)
59 */
60
61 #define ESMT_OOB_SECTION_COUNT 4
62 #define ESMT_OOB_SECTION_SIZE(nand) \
63 (nanddev_per_page_oobsize(nand) / ESMT_OOB_SECTION_COUNT)
64 #define ESMT_OOB_FREE_SIZE(nand) \
65 (ESMT_OOB_SECTION_SIZE(nand) / 2)
66 #define ESMT_OOB_ECC_SIZE(nand) \
67 (ESMT_OOB_SECTION_SIZE(nand) - ESMT_OOB_FREE_SIZE(nand))
68 #define ESMT_OOB_BBM_SIZE 2
69
f50l1g41lb_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)70 static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section,
71 struct mtd_oob_region *region)
72 {
73 struct nand_device *nand = mtd_to_nanddev(mtd);
74
75 if (section >= ESMT_OOB_SECTION_COUNT)
76 return -ERANGE;
77
78 region->offset = section * ESMT_OOB_SECTION_SIZE(nand) +
79 ESMT_OOB_FREE_SIZE(nand);
80 region->length = ESMT_OOB_ECC_SIZE(nand);
81
82 return 0;
83 }
84
f50l1g41lb_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)85 static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section,
86 struct mtd_oob_region *region)
87 {
88 struct nand_device *nand = mtd_to_nanddev(mtd);
89
90 if (section >= ESMT_OOB_SECTION_COUNT)
91 return -ERANGE;
92
93 /*
94 * Reserve space for bad blocks markers (section0) and
95 * reserved bytes (sections 1-3)
96 */
97 region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + 2;
98
99 /* Use only 2 non-protected ECC bytes per each OOB section */
100 region->length = 2;
101
102 return 0;
103 }
104
105 static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
106 .ecc = f50l1g41lb_ooblayout_ecc,
107 .free = f50l1g41lb_ooblayout_free,
108 };
109
f50l1g41lb_otp_info(struct spinand_device * spinand,size_t len,struct otp_info * buf,size_t * retlen,bool user)110 static int f50l1g41lb_otp_info(struct spinand_device *spinand, size_t len,
111 struct otp_info *buf, size_t *retlen, bool user)
112 {
113 if (len < sizeof(*buf))
114 return -EINVAL;
115
116 buf->locked = 0;
117 buf->start = 0;
118 buf->length = user ? spinand_user_otp_size(spinand) :
119 spinand_fact_otp_size(spinand);
120
121 *retlen = sizeof(*buf);
122 return 0;
123 }
124
f50l1g41lb_fact_otp_info(struct spinand_device * spinand,size_t len,struct otp_info * buf,size_t * retlen)125 static int f50l1g41lb_fact_otp_info(struct spinand_device *spinand, size_t len,
126 struct otp_info *buf, size_t *retlen)
127 {
128 return f50l1g41lb_otp_info(spinand, len, buf, retlen, false);
129 }
130
f50l1g41lb_user_otp_info(struct spinand_device * spinand,size_t len,struct otp_info * buf,size_t * retlen)131 static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len,
132 struct otp_info *buf, size_t *retlen)
133 {
134 return f50l1g41lb_otp_info(spinand, len, buf, retlen, true);
135 }
136
f50l1g41lb_otp_lock(struct spinand_device * spinand,loff_t from,size_t len)137 static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
138 size_t len)
139 {
140 struct spi_mem_op write_op = SPINAND_WR_EN_DIS_OP(true);
141 struct spi_mem_op exec_op = SPINAND_PROG_EXEC_OP(0);
142 u8 status;
143 int ret;
144
145 ret = spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK,
146 ESMT_F50L1G41LB_CFG_OTP_LOCK);
147 if (!ret)
148 return ret;
149
150 ret = spi_mem_exec_op(spinand->spimem, &write_op);
151 if (!ret)
152 goto out;
153
154 ret = spi_mem_exec_op(spinand->spimem, &exec_op);
155 if (!ret)
156 goto out;
157
158 ret = spinand_wait(spinand,
159 SPINAND_WRITE_INITIAL_DELAY_US,
160 SPINAND_WRITE_POLL_DELAY_US,
161 &status);
162 if (!ret && (status & STATUS_PROG_FAILED))
163 ret = -EIO;
164
165 out:
166 if (spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, 0)) {
167 dev_warn(&spinand_to_mtd(spinand)->dev,
168 "Can not disable OTP mode\n");
169 ret = -EIO;
170 }
171
172 return ret;
173 }
174
175 static const struct spinand_user_otp_ops f50l1g41lb_user_otp_ops = {
176 .info = f50l1g41lb_user_otp_info,
177 .lock = f50l1g41lb_otp_lock,
178 .read = spinand_user_otp_read,
179 .write = spinand_user_otp_write,
180 };
181
182 static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = {
183 .info = f50l1g41lb_fact_otp_info,
184 .read = spinand_fact_otp_read,
185 };
186
187 static const struct spinand_info esmt_c8_spinand_table[] = {
188 SPINAND_INFO("F50L1G41LB",
189 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
190 0x7f, 0x7f),
191 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
192 NAND_ECCREQ(1, 512),
193 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
194 &write_cache_variants,
195 &update_cache_variants),
196 0,
197 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
198 SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops),
199 SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
200 SPINAND_INFO("F50D1G41LB",
201 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
202 0x7f, 0x7f),
203 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
204 NAND_ECCREQ(1, 512),
205 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
206 &write_cache_variants,
207 &update_cache_variants),
208 0,
209 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
210 SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops),
211 SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
212 SPINAND_INFO("F50D2G41KA",
213 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f,
214 0x7f, 0x7f),
215 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
216 NAND_ECCREQ(8, 512),
217 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
218 &write_cache_variants,
219 &update_cache_variants),
220 0,
221 SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
222 };
223
224 static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
225 };
226
227 const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
228 .id = SPINAND_MFR_ESMT_C8,
229 .name = "ESMT",
230 .chips = esmt_c8_spinand_table,
231 .nchips = ARRAY_SIZE(esmt_c8_spinand_table),
232 .ops = &esmt_spinand_manuf_ops,
233 };
234