xref: /rk3399_rockchip-uboot/drivers/mtd/nand/spi/hyf.c (revision 2a3fb7bb049d69d96f3bc7dae8caa756fdc8a613)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020 exceet electronics GmbH
4  *
5  * Authors:
6  *	Frieder Schrempf <frieder.schrempf@exceet.de>
7  *	Boris Brezillon <boris.brezillon@bootlin.com>
8  */
9 
10 #ifndef __UBOOT__
11 #include <linux/device.h>
12 #include <linux/kernel.h>
13 #endif
14 #include <linux/mtd/spinand.h>
15 
16 #define SPINAND_MFR_HYF		0xC9
17 
18 static SPINAND_OP_VARIANTS(read_cache_variants,
19 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
20 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
21 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
22 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
23 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
24 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 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 static int hyf1gq4upacae_ooblayout_ecc(struct mtd_info *mtd, int section,
35 				       struct mtd_oob_region *region)
36 {
37 	if (section)
38 		return -ERANGE;
39 
40 	region->offset = 64;
41 	region->length = 64;
42 
43 	return 0;
44 }
45 
46 static int hyf1gq4upacae_ooblayout_free(struct mtd_info *mtd, int section,
47 					struct mtd_oob_region *region)
48 {
49 	if (section)
50 		return -ERANGE;
51 
52 	region->offset = 1;
53 	region->length = 63;
54 
55 	return 0;
56 }
57 
58 static const struct mtd_ooblayout_ops hyf1gq4upacae_ooblayout = {
59 	.ecc = hyf1gq4upacae_ooblayout_ecc,
60 	.rfree = hyf1gq4upacae_ooblayout_free,
61 };
62 
63 static int hyf1gq4udacae_ooblayout_ecc(struct mtd_info *mtd, int section,
64 				       struct mtd_oob_region *region)
65 {
66 	if (section > 3)
67 		return -ERANGE;
68 
69 	region->offset = (16 * section) + 8;
70 	region->length = 8;
71 
72 	return 0;
73 }
74 
75 static int hyf1gq4udacae_ooblayout_free(struct mtd_info *mtd, int section,
76 					struct mtd_oob_region *region)
77 {
78 	if (section > 3)
79 		return -ERANGE;
80 
81 	region->offset = (16 * section) + 4;
82 	region->length = 4;
83 
84 	return 0;
85 }
86 
87 static const struct mtd_ooblayout_ops hyf1gq4udacae_ooblayout = {
88 	.ecc = hyf1gq4udacae_ooblayout_ecc,
89 	.rfree = hyf1gq4udacae_ooblayout_free,
90 };
91 
92 static int hyf1gq4udacae_ecc_get_status(struct spinand_device *spinand,
93 					u8 status)
94 {
95 	struct nand_device *nand = spinand_to_nand(spinand);
96 
97 	switch (status & STATUS_ECC_MASK) {
98 	case STATUS_ECC_NO_BITFLIPS:
99 		return 0;
100 
101 	case STATUS_ECC_UNCOR_ERROR:
102 		return -EBADMSG;
103 
104 	case STATUS_ECC_HAS_BITFLIPS:
105 		return 1;
106 
107 	default:
108 		return nand->eccreq.strength;
109 	}
110 
111 	return -EINVAL;
112 }
113 
114 static const struct spinand_info hyf_spinand_table[] = {
115 	SPINAND_INFO("HYF1GQ4UPACAE", 0xA1,
116 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
117 		     NAND_ECCREQ(1, 512),
118 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
119 					      &write_cache_variants,
120 					      &update_cache_variants),
121 		     SPINAND_HAS_QE_BIT,
122 		     SPINAND_ECCINFO(&hyf1gq4upacae_ooblayout, NULL)),
123 	SPINAND_INFO("HYF1GQ4UDACAE", 0x21,
124 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 2, 1, 1),
125 		     NAND_ECCREQ(4, 512),
126 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
127 					      &write_cache_variants,
128 					      &update_cache_variants),
129 		     SPINAND_HAS_QE_BIT,
130 		     SPINAND_ECCINFO(&hyf1gq4udacae_ooblayout,
131 				     hyf1gq4udacae_ecc_get_status)),
132 };
133 
134 /**
135  * hyf_spinand_detect - initialize device related part in spinand_device
136  * struct if it is a hyf device.
137  * @spinand: SPI NAND device structure
138  */
139 static int hyf_spinand_detect(struct spinand_device *spinand)
140 {
141 	u8 *id = spinand->id.data;
142 	int ret;
143 
144 	/*
145 	 * hyf SPI NAND read ID needs a dummy byte, so the first byte in
146 	 * raw_id is garbage.
147 	 */
148 	if (id[1] != SPINAND_MFR_HYF)
149 		return 0;
150 
151 	ret = spinand_match_and_init(spinand, hyf_spinand_table,
152 				     ARRAY_SIZE(hyf_spinand_table),
153 				     id[2]);
154 	if (ret)
155 		return ret;
156 
157 	return 1;
158 }
159 
160 static const struct spinand_manufacturer_ops hyf_spinand_manuf_ops = {
161 	.detect = hyf_spinand_detect,
162 };
163 
164 const struct spinand_manufacturer hyf_spinand_manufacturer = {
165 	.id = SPINAND_MFR_HYF,
166 	.name = "hyf",
167 	.ops = &hyf_spinand_manuf_ops,
168 };
169