xref: /openbmc/linux/drivers/mtd/nand/spi/gigadevice.c (revision c67e8ec0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Author:
4  *	Chuanhong Guo <gch981213@gmail.com>
5  */
6 
7 #include <linux/device.h>
8 #include <linux/kernel.h>
9 #include <linux/mtd/spinand.h>
10 
11 #define SPINAND_MFR_GIGADEVICE			0xC8
12 #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4)
13 #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4)
14 
15 static SPINAND_OP_VARIANTS(read_cache_variants,
16 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
17 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
18 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
19 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
20 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
21 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
22 
23 static SPINAND_OP_VARIANTS(write_cache_variants,
24 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
25 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
26 
27 static SPINAND_OP_VARIANTS(update_cache_variants,
28 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
29 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
30 
31 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
32 				  struct mtd_oob_region *region)
33 {
34 	if (section > 3)
35 		return -ERANGE;
36 
37 	region->offset = (16 * section) + 8;
38 	region->length = 8;
39 
40 	return 0;
41 }
42 
43 static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
44 				   struct mtd_oob_region *region)
45 {
46 	if (section > 3)
47 		return -ERANGE;
48 
49 	if (section) {
50 		region->offset = 16 * section;
51 		region->length = 8;
52 	} else {
53 		/* section 0 has one byte reserved for bad block mark */
54 		region->offset = 1;
55 		region->length = 7;
56 	}
57 	return 0;
58 }
59 
60 static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
61 					 u8 status)
62 {
63 	switch (status & STATUS_ECC_MASK) {
64 	case STATUS_ECC_NO_BITFLIPS:
65 		return 0;
66 
67 	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
68 		/* 1-7 bits are flipped. return the maximum. */
69 		return 7;
70 
71 	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
72 		return 8;
73 
74 	case STATUS_ECC_UNCOR_ERROR:
75 		return -EBADMSG;
76 
77 	default:
78 		break;
79 	}
80 
81 	return -EINVAL;
82 }
83 
84 static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
85 	.ecc = gd5fxgq4xa_ooblayout_ecc,
86 	.free = gd5fxgq4xa_ooblayout_free,
87 };
88 
89 static const struct spinand_info gigadevice_spinand_table[] = {
90 	SPINAND_INFO("GD5F1GQ4xA", 0xF1,
91 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
92 		     NAND_ECCREQ(8, 512),
93 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
94 					      &write_cache_variants,
95 					      &update_cache_variants),
96 		     0,
97 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
98 				     gd5fxgq4xa_ecc_get_status)),
99 	SPINAND_INFO("GD5F2GQ4xA", 0xF2,
100 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
101 		     NAND_ECCREQ(8, 512),
102 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
103 					      &write_cache_variants,
104 					      &update_cache_variants),
105 		     0,
106 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
107 				     gd5fxgq4xa_ecc_get_status)),
108 	SPINAND_INFO("GD5F4GQ4xA", 0xF4,
109 		     NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
110 		     NAND_ECCREQ(8, 512),
111 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
112 					      &write_cache_variants,
113 					      &update_cache_variants),
114 		     0,
115 		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
116 				     gd5fxgq4xa_ecc_get_status)),
117 };
118 
119 static int gigadevice_spinand_detect(struct spinand_device *spinand)
120 {
121 	u8 *id = spinand->id.data;
122 	int ret;
123 
124 	/*
125 	 * For GD NANDs, There is an address byte needed to shift in before IDs
126 	 * are read out, so the first byte in raw_id is dummy.
127 	 */
128 	if (id[1] != SPINAND_MFR_GIGADEVICE)
129 		return 0;
130 
131 	ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
132 				     ARRAY_SIZE(gigadevice_spinand_table),
133 				     id[2]);
134 	if (ret)
135 		return ret;
136 
137 	return 1;
138 }
139 
140 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
141 	.detect = gigadevice_spinand_detect,
142 };
143 
144 const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
145 	.id = SPINAND_MFR_GIGADEVICE,
146 	.name = "GigaDevice",
147 	.ops = &gigadevice_spinand_manuf_ops,
148 };
149