xref: /linux/drivers/mtd/nand/spi/heyangtek.c (revision df51bdc5e80dae43cab81f6cc641e98638303c88)
1*a8374683SAleksei Sviridkin // SPDX-License-Identifier: GPL-2.0
2*a8374683SAleksei Sviridkin /*
3*a8374683SAleksei Sviridkin  * Authors:
4*a8374683SAleksei Sviridkin  *	Andrey Zolotarev <andrey.zolotarev@keenetic.com> - the main driver logic
5*a8374683SAleksei Sviridkin  *	Aleksei Sviridkin <f@lex.la> - adaptation to the mainline Linux kernel
6*a8374683SAleksei Sviridkin  *
7*a8374683SAleksei Sviridkin  * Based on:
8*a8374683SAleksei Sviridkin  *	https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7
9*a8374683SAleksei Sviridkin  */
10*a8374683SAleksei Sviridkin 
11*a8374683SAleksei Sviridkin #include <linux/device.h>
12*a8374683SAleksei Sviridkin #include <linux/kernel.h>
13*a8374683SAleksei Sviridkin #include <linux/mtd/spinand.h>
14*a8374683SAleksei Sviridkin 
15*a8374683SAleksei Sviridkin #define SPINAND_MFR_HEYANGTEK			0xc9
16*a8374683SAleksei Sviridkin 
17*a8374683SAleksei Sviridkin #define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS	(3 << 4)
18*a8374683SAleksei Sviridkin 
19*a8374683SAleksei Sviridkin static SPINAND_OP_VARIANTS(read_cache_variants,
20*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
21*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
22*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
23*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
24*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
25*a8374683SAleksei Sviridkin 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
26*a8374683SAleksei Sviridkin 
27*a8374683SAleksei Sviridkin static SPINAND_OP_VARIANTS(write_cache_variants,
28*a8374683SAleksei Sviridkin 		SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
29*a8374683SAleksei Sviridkin 		SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
30*a8374683SAleksei Sviridkin 
31*a8374683SAleksei Sviridkin static SPINAND_OP_VARIANTS(update_cache_variants,
32*a8374683SAleksei Sviridkin 		SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
33*a8374683SAleksei Sviridkin 		SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
34*a8374683SAleksei Sviridkin 
35*a8374683SAleksei Sviridkin /*
36*a8374683SAleksei Sviridkin  * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken
37*a8374683SAleksei Sviridkin  * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each
38*a8374683SAleksei Sviridkin  * 16-byte section, the bad block marker sits in byte 0 and the remaining
39*a8374683SAleksei Sviridkin  * bytes are exposed as free.
40*a8374683SAleksei Sviridkin  */
41*a8374683SAleksei Sviridkin static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section,
42*a8374683SAleksei Sviridkin 				 struct mtd_oob_region *region)
43*a8374683SAleksei Sviridkin {
44*a8374683SAleksei Sviridkin 	if (section > 3)
45*a8374683SAleksei Sviridkin 		return -ERANGE;
46*a8374683SAleksei Sviridkin 
47*a8374683SAleksei Sviridkin 	region->offset = (16 * section) + 8;
48*a8374683SAleksei Sviridkin 	region->length = 8;
49*a8374683SAleksei Sviridkin 
50*a8374683SAleksei Sviridkin 	return 0;
51*a8374683SAleksei Sviridkin }
52*a8374683SAleksei Sviridkin 
53*a8374683SAleksei Sviridkin static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section,
54*a8374683SAleksei Sviridkin 				  struct mtd_oob_region *region)
55*a8374683SAleksei Sviridkin {
56*a8374683SAleksei Sviridkin 	if (section > 3)
57*a8374683SAleksei Sviridkin 		return -ERANGE;
58*a8374683SAleksei Sviridkin 
59*a8374683SAleksei Sviridkin 	if (section) {
60*a8374683SAleksei Sviridkin 		region->offset = 16 * section;
61*a8374683SAleksei Sviridkin 		region->length = 8;
62*a8374683SAleksei Sviridkin 	} else {
63*a8374683SAleksei Sviridkin 		/* section 0 has one byte reserved for the bad block marker */
64*a8374683SAleksei Sviridkin 		region->offset = 1;
65*a8374683SAleksei Sviridkin 		region->length = 7;
66*a8374683SAleksei Sviridkin 	}
67*a8374683SAleksei Sviridkin 
68*a8374683SAleksei Sviridkin 	return 0;
69*a8374683SAleksei Sviridkin }
70*a8374683SAleksei Sviridkin 
71*a8374683SAleksei Sviridkin static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = {
72*a8374683SAleksei Sviridkin 	.ecc = hyf1gq4_ooblayout_ecc,
73*a8374683SAleksei Sviridkin 	.free = hyf1gq4_ooblayout_free,
74*a8374683SAleksei Sviridkin };
75*a8374683SAleksei Sviridkin 
76*a8374683SAleksei Sviridkin static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status)
77*a8374683SAleksei Sviridkin {
78*a8374683SAleksei Sviridkin 	struct nand_device *nand = spinand_to_nand(spinand);
79*a8374683SAleksei Sviridkin 
80*a8374683SAleksei Sviridkin 	switch (status & STATUS_ECC_MASK) {
81*a8374683SAleksei Sviridkin 	case STATUS_ECC_NO_BITFLIPS:
82*a8374683SAleksei Sviridkin 		return 0;
83*a8374683SAleksei Sviridkin 
84*a8374683SAleksei Sviridkin 	case STATUS_ECC_UNCOR_ERROR:
85*a8374683SAleksei Sviridkin 		return -EBADMSG;
86*a8374683SAleksei Sviridkin 
87*a8374683SAleksei Sviridkin 	case STATUS_ECC_HAS_BITFLIPS:
88*a8374683SAleksei Sviridkin 		/*
89*a8374683SAleksei Sviridkin 		 * The die exposes only a coarse 2-bit ECC status and has no
90*a8374683SAleksei Sviridkin 		 * register for the exact bitflip count. This code means
91*a8374683SAleksei Sviridkin 		 * "corrected, below the refresh threshold", so report half of
92*a8374683SAleksei Sviridkin 		 * the ECC strength as a representative value.
93*a8374683SAleksei Sviridkin 		 */
94*a8374683SAleksei Sviridkin 		return nanddev_get_ecc_conf(nand)->strength / 2;
95*a8374683SAleksei Sviridkin 
96*a8374683SAleksei Sviridkin 	case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS:
97*a8374683SAleksei Sviridkin 		/*
98*a8374683SAleksei Sviridkin 		 * "Corrected, refresh recommended": report the full ECC
99*a8374683SAleksei Sviridkin 		 * strength so the upper layers relocate the data.
100*a8374683SAleksei Sviridkin 		 */
101*a8374683SAleksei Sviridkin 		return nanddev_get_ecc_conf(nand)->strength;
102*a8374683SAleksei Sviridkin 
103*a8374683SAleksei Sviridkin 	default:
104*a8374683SAleksei Sviridkin 		break;
105*a8374683SAleksei Sviridkin 	}
106*a8374683SAleksei Sviridkin 
107*a8374683SAleksei Sviridkin 	return -EINVAL;
108*a8374683SAleksei Sviridkin }
109*a8374683SAleksei Sviridkin 
110*a8374683SAleksei Sviridkin static const struct spinand_info heyangtek_spinand_table[] = {
111*a8374683SAleksei Sviridkin 	SPINAND_INFO("HYF1GQ4UDACAE",
112*a8374683SAleksei Sviridkin 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21),
113*a8374683SAleksei Sviridkin 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
114*a8374683SAleksei Sviridkin 		     NAND_ECCREQ(4, 512),
115*a8374683SAleksei Sviridkin 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
116*a8374683SAleksei Sviridkin 					      &write_cache_variants,
117*a8374683SAleksei Sviridkin 					      &update_cache_variants),
118*a8374683SAleksei Sviridkin 		     SPINAND_HAS_QE_BIT,
119*a8374683SAleksei Sviridkin 		     SPINAND_ECCINFO(&hyf1gq4_ooblayout,
120*a8374683SAleksei Sviridkin 				     hyf1gq4_ecc_get_status)),
121*a8374683SAleksei Sviridkin };
122*a8374683SAleksei Sviridkin 
123*a8374683SAleksei Sviridkin static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = {
124*a8374683SAleksei Sviridkin };
125*a8374683SAleksei Sviridkin 
126*a8374683SAleksei Sviridkin const struct spinand_manufacturer heyangtek_spinand_manufacturer = {
127*a8374683SAleksei Sviridkin 	.id = SPINAND_MFR_HEYANGTEK,
128*a8374683SAleksei Sviridkin 	.name = "HeYangTek",
129*a8374683SAleksei Sviridkin 	.chips = heyangtek_spinand_table,
130*a8374683SAleksei Sviridkin 	.nchips = ARRAY_SIZE(heyangtek_spinand_table),
131*a8374683SAleksei Sviridkin 	.ops = &heyangtek_spinand_manuf_ops,
132*a8374683SAleksei Sviridkin };
133