xref: /kernel/linux/linux-6.6/drivers/mtd/nand/spi/esmt.c (revision 62306a36)
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
12/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
13#define SPINAND_MFR_ESMT_C8			0xc8
14
15static SPINAND_OP_VARIANTS(read_cache_variants,
16			   SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
17			   SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
18			   SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
19			   SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
20
21static SPINAND_OP_VARIANTS(write_cache_variants,
22			   SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
23			   SPINAND_PROG_LOAD(true, 0, NULL, 0));
24
25static SPINAND_OP_VARIANTS(update_cache_variants,
26			   SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
27			   SPINAND_PROG_LOAD(false, 0, NULL, 0));
28
29/*
30 * OOB spare area map (64 bytes)
31 *
32 * Bad Block Markers
33 * filled by HW and kernel                 Reserved
34 *   |                 +-----------------------+-----------------------+
35 *   |                 |                       |                       |
36 *   |                 |    OOB free data Area |non ECC protected      |
37 *   |   +-------------|-----+-----------------|-----+-----------------|-----+
38 *   |   |             |     |                 |     |                 |     |
39 * +-|---|----------+--|-----|--------------+--|-----|--------------+--|-----|--------------+
40 * | |   | section0 |  |     |    section1  |  |     |    section2  |  |     |    section3  |
41 * +-v-+-v-+---+----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+
42 * |   |   |   |    |     |     |     |     |     |     |     |     |     |     |     |     |
43 * |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|
44 * |   |   |   |    |     |     |     |     |     |     |     |     |     |     |     |     |
45 * +---+---+-^-+--^-+-----+-----+--^--+--^--+-----+-----+--^--+--^--+-----+-----+--^--+--^--+
46 *           |    |                |     |                 |     |                 |     |
47 *           |    +----------------|-----+-----------------|-----+-----------------|-----+
48 *           |             ECC Area|(Main + Spare) - filled|by ESMT NAND HW        |
49 *           |                     |                       |                       |
50 *           +---------------------+-----------------------+-----------------------+
51 *                         OOB ECC protected Area - not used due to
52 *                         partial programming from some filesystems
53 *                             (like JFFS2 with cleanmarkers)
54 */
55
56#define ESMT_OOB_SECTION_COUNT			4
57#define ESMT_OOB_SECTION_SIZE(nand) \
58	(nanddev_per_page_oobsize(nand) / ESMT_OOB_SECTION_COUNT)
59#define ESMT_OOB_FREE_SIZE(nand) \
60	(ESMT_OOB_SECTION_SIZE(nand) / 2)
61#define ESMT_OOB_ECC_SIZE(nand) \
62	(ESMT_OOB_SECTION_SIZE(nand) - ESMT_OOB_FREE_SIZE(nand))
63#define ESMT_OOB_BBM_SIZE			2
64
65static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section,
66				    struct mtd_oob_region *region)
67{
68	struct nand_device *nand = mtd_to_nanddev(mtd);
69
70	if (section >= ESMT_OOB_SECTION_COUNT)
71		return -ERANGE;
72
73	region->offset = section * ESMT_OOB_SECTION_SIZE(nand) +
74			 ESMT_OOB_FREE_SIZE(nand);
75	region->length = ESMT_OOB_ECC_SIZE(nand);
76
77	return 0;
78}
79
80static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section,
81				     struct mtd_oob_region *region)
82{
83	struct nand_device *nand = mtd_to_nanddev(mtd);
84
85	if (section >= ESMT_OOB_SECTION_COUNT)
86		return -ERANGE;
87
88	/*
89	 * Reserve space for bad blocks markers (section0) and
90	 * reserved bytes (sections 1-3)
91	 */
92	region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + 2;
93
94	/* Use only 2 non-protected ECC bytes per each OOB section */
95	region->length = 2;
96
97	return 0;
98}
99
100static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
101	.ecc = f50l1g41lb_ooblayout_ecc,
102	.free = f50l1g41lb_ooblayout_free,
103};
104
105static const struct spinand_info esmt_c8_spinand_table[] = {
106	SPINAND_INFO("F50L1G41LB",
107		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
108				0x7f, 0x7f),
109		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
110		     NAND_ECCREQ(1, 512),
111		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
112					      &write_cache_variants,
113					      &update_cache_variants),
114		     0,
115		     SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
116	SPINAND_INFO("F50D1G41LB",
117		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
118				0x7f, 0x7f),
119		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
120		     NAND_ECCREQ(1, 512),
121		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
122					      &write_cache_variants,
123					      &update_cache_variants),
124		     0,
125		     SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
126	SPINAND_INFO("F50D2G41KA",
127		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f,
128				0x7f, 0x7f),
129		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
130		     NAND_ECCREQ(8, 512),
131		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
132					      &write_cache_variants,
133					      &update_cache_variants),
134		     0,
135		     SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
136};
137
138static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
139};
140
141const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
142	.id = SPINAND_MFR_ESMT_C8,
143	.name = "ESMT",
144	.chips = esmt_c8_spinand_table,
145	.nchips = ARRAY_SIZE(esmt_c8_spinand_table),
146	.ops = &esmt_spinand_manuf_ops,
147};
148