File Index
Files and directories
Sign in
FILE

esmt.c

File preview with syntax highlighting.

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