xref: /openbmc/u-boot/drivers/mtd/nand/spi/macronix.c (revision 6f041ccabb03bea16c2f21f3254dc9c1cb38425c)
1*6f041ccaSBoris Brezillon // SPDX-License-Identifier: GPL-2.0
2*6f041ccaSBoris Brezillon /*
3*6f041ccaSBoris Brezillon  * Copyright (c) 2018 Macronix
4*6f041ccaSBoris Brezillon  *
5*6f041ccaSBoris Brezillon  * Author: Boris Brezillon <boris.brezillon@bootlin.com>
6*6f041ccaSBoris Brezillon  */
7*6f041ccaSBoris Brezillon 
8*6f041ccaSBoris Brezillon #ifndef __UBOOT__
9*6f041ccaSBoris Brezillon #include <linux/device.h>
10*6f041ccaSBoris Brezillon #include <linux/kernel.h>
11*6f041ccaSBoris Brezillon #endif
12*6f041ccaSBoris Brezillon #include <linux/mtd/spinand.h>
13*6f041ccaSBoris Brezillon 
14*6f041ccaSBoris Brezillon #define SPINAND_MFR_MACRONIX		0xC2
15*6f041ccaSBoris Brezillon 
16*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(read_cache_variants,
17*6f041ccaSBoris Brezillon 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
18*6f041ccaSBoris Brezillon 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
19*6f041ccaSBoris Brezillon 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
20*6f041ccaSBoris Brezillon 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
21*6f041ccaSBoris Brezillon 
22*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(write_cache_variants,
23*6f041ccaSBoris Brezillon 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
24*6f041ccaSBoris Brezillon 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
25*6f041ccaSBoris Brezillon 
26*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(update_cache_variants,
27*6f041ccaSBoris Brezillon 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
28*6f041ccaSBoris Brezillon 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
29*6f041ccaSBoris Brezillon 
30*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
31*6f041ccaSBoris Brezillon 				      struct mtd_oob_region *region)
32*6f041ccaSBoris Brezillon {
33*6f041ccaSBoris Brezillon 	return -ERANGE;
34*6f041ccaSBoris Brezillon }
35*6f041ccaSBoris Brezillon 
36*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section,
37*6f041ccaSBoris Brezillon 				       struct mtd_oob_region *region)
38*6f041ccaSBoris Brezillon {
39*6f041ccaSBoris Brezillon 	if (section)
40*6f041ccaSBoris Brezillon 		return -ERANGE;
41*6f041ccaSBoris Brezillon 
42*6f041ccaSBoris Brezillon 	region->offset = 2;
43*6f041ccaSBoris Brezillon 	region->length = mtd->oobsize - 2;
44*6f041ccaSBoris Brezillon 
45*6f041ccaSBoris Brezillon 	return 0;
46*6f041ccaSBoris Brezillon }
47*6f041ccaSBoris Brezillon 
48*6f041ccaSBoris Brezillon static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = {
49*6f041ccaSBoris Brezillon 	.ecc = mx35lf1ge4ab_ooblayout_ecc,
50*6f041ccaSBoris Brezillon 	.free = mx35lf1ge4ab_ooblayout_free,
51*6f041ccaSBoris Brezillon };
52*6f041ccaSBoris Brezillon 
53*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
54*6f041ccaSBoris Brezillon {
55*6f041ccaSBoris Brezillon 	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
56*6f041ccaSBoris Brezillon 					  SPI_MEM_OP_NO_ADDR,
57*6f041ccaSBoris Brezillon 					  SPI_MEM_OP_DUMMY(1, 1),
58*6f041ccaSBoris Brezillon 					  SPI_MEM_OP_DATA_IN(1, eccsr, 1));
59*6f041ccaSBoris Brezillon 
60*6f041ccaSBoris Brezillon 	return spi_mem_exec_op(spinand->slave, &op);
61*6f041ccaSBoris Brezillon }
62*6f041ccaSBoris Brezillon 
63*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
64*6f041ccaSBoris Brezillon 				       u8 status)
65*6f041ccaSBoris Brezillon {
66*6f041ccaSBoris Brezillon 	struct nand_device *nand = spinand_to_nand(spinand);
67*6f041ccaSBoris Brezillon 	u8 eccsr;
68*6f041ccaSBoris Brezillon 
69*6f041ccaSBoris Brezillon 	switch (status & STATUS_ECC_MASK) {
70*6f041ccaSBoris Brezillon 	case STATUS_ECC_NO_BITFLIPS:
71*6f041ccaSBoris Brezillon 		return 0;
72*6f041ccaSBoris Brezillon 
73*6f041ccaSBoris Brezillon 	case STATUS_ECC_UNCOR_ERROR:
74*6f041ccaSBoris Brezillon 		return -EBADMSG;
75*6f041ccaSBoris Brezillon 
76*6f041ccaSBoris Brezillon 	case STATUS_ECC_HAS_BITFLIPS:
77*6f041ccaSBoris Brezillon 		/*
78*6f041ccaSBoris Brezillon 		 * Let's try to retrieve the real maximum number of bitflips
79*6f041ccaSBoris Brezillon 		 * in order to avoid forcing the wear-leveling layer to move
80*6f041ccaSBoris Brezillon 		 * data around if it's not necessary.
81*6f041ccaSBoris Brezillon 		 */
82*6f041ccaSBoris Brezillon 		if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
83*6f041ccaSBoris Brezillon 			return nand->eccreq.strength;
84*6f041ccaSBoris Brezillon 
85*6f041ccaSBoris Brezillon 		if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr))
86*6f041ccaSBoris Brezillon 			return nand->eccreq.strength;
87*6f041ccaSBoris Brezillon 
88*6f041ccaSBoris Brezillon 		return eccsr;
89*6f041ccaSBoris Brezillon 
90*6f041ccaSBoris Brezillon 	default:
91*6f041ccaSBoris Brezillon 		break;
92*6f041ccaSBoris Brezillon 	}
93*6f041ccaSBoris Brezillon 
94*6f041ccaSBoris Brezillon 	return -EINVAL;
95*6f041ccaSBoris Brezillon }
96*6f041ccaSBoris Brezillon 
97*6f041ccaSBoris Brezillon static const struct spinand_info macronix_spinand_table[] = {
98*6f041ccaSBoris Brezillon 	SPINAND_INFO("MX35LF1GE4AB", 0x12,
99*6f041ccaSBoris Brezillon 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
100*6f041ccaSBoris Brezillon 		     NAND_ECCREQ(4, 512),
101*6f041ccaSBoris Brezillon 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
102*6f041ccaSBoris Brezillon 					      &write_cache_variants,
103*6f041ccaSBoris Brezillon 					      &update_cache_variants),
104*6f041ccaSBoris Brezillon 		     SPINAND_HAS_QE_BIT,
105*6f041ccaSBoris Brezillon 		     SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout,
106*6f041ccaSBoris Brezillon 				     mx35lf1ge4ab_ecc_get_status)),
107*6f041ccaSBoris Brezillon };
108*6f041ccaSBoris Brezillon 
109*6f041ccaSBoris Brezillon static int macronix_spinand_detect(struct spinand_device *spinand)
110*6f041ccaSBoris Brezillon {
111*6f041ccaSBoris Brezillon 	u8 *id = spinand->id.data;
112*6f041ccaSBoris Brezillon 	int ret;
113*6f041ccaSBoris Brezillon 
114*6f041ccaSBoris Brezillon 	/*
115*6f041ccaSBoris Brezillon 	 * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
116*6f041ccaSBoris Brezillon 	 * raw_id is garbage.
117*6f041ccaSBoris Brezillon 	 */
118*6f041ccaSBoris Brezillon 	if (id[1] != SPINAND_MFR_MACRONIX)
119*6f041ccaSBoris Brezillon 		return 0;
120*6f041ccaSBoris Brezillon 
121*6f041ccaSBoris Brezillon 	ret = spinand_match_and_init(spinand, macronix_spinand_table,
122*6f041ccaSBoris Brezillon 				     ARRAY_SIZE(macronix_spinand_table),
123*6f041ccaSBoris Brezillon 				     id[2]);
124*6f041ccaSBoris Brezillon 	if (ret)
125*6f041ccaSBoris Brezillon 		return ret;
126*6f041ccaSBoris Brezillon 
127*6f041ccaSBoris Brezillon 	return 1;
128*6f041ccaSBoris Brezillon }
129*6f041ccaSBoris Brezillon 
130*6f041ccaSBoris Brezillon static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
131*6f041ccaSBoris Brezillon 	.detect = macronix_spinand_detect,
132*6f041ccaSBoris Brezillon };
133*6f041ccaSBoris Brezillon 
134*6f041ccaSBoris Brezillon const struct spinand_manufacturer macronix_spinand_manufacturer = {
135*6f041ccaSBoris Brezillon 	.id = SPINAND_MFR_MACRONIX,
136*6f041ccaSBoris Brezillon 	.name = "Macronix",
137*6f041ccaSBoris Brezillon 	.ops = &macronix_spinand_manuf_ops,
138*6f041ccaSBoris Brezillon };
139