xref: /linux/drivers/mtd/nand/spi/fmsh.c (revision edd2b9832d604a234b60a4910c7496f351cd1e12)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
4  *
5  * Author: Dingqiang Lin <jon.lin@rock-chips.com>
6  */
7 
8 #include <linux/device.h>
9 #include <linux/kernel.h>
10 #include <linux/mtd/spinand.h>
11 
12 #define FM25S01BI3_STATUS_ECC_MASK		(7 << 4)
13 	#define FM25S01BI3_STATUS_ECC_NO_BITFLIPS	(0 << 4)
14 	#define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS	(1 << 4)
15 	#define FM25S01BI3_STATUS_ECC_UNCOR_ERROR	(2 << 4)
16 	#define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS	(3 << 4)
17 	#define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS	(5 << 4)
18 
19 #define SPINAND_MFR_FMSH		0xA1
20 
21 static SPINAND_OP_VARIANTS(read_cache_variants,
22 		SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0),
23 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
24 		SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
25 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
26 		SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
27 		SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
28 
29 static SPINAND_OP_VARIANTS(write_cache_variants,
30 		SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
31 		SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
32 
33 static SPINAND_OP_VARIANTS(update_cache_variants,
34 		SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
35 		SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
36 
37 static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
38 				  struct mtd_oob_region *region)
39 {
40 	return -ERANGE;
41 }
42 
43 static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
44 				   struct mtd_oob_region *region)
45 {
46 	if (section)
47 		return -ERANGE;
48 
49 	region->offset = 2;
50 	region->length = 62;
51 
52 	return 0;
53 }
54 
55 static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
56 				     u8 status)
57 {
58 	switch (status & FM25S01BI3_STATUS_ECC_MASK) {
59 	case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
60 		return 0;
61 
62 	case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
63 		return -EBADMSG;
64 
65 	case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
66 		return 3;
67 
68 	case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
69 		return 6;
70 
71 	case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
72 		return 8;
73 
74 	default:
75 		break;
76 	}
77 
78 	return -EINVAL;
79 }
80 
81 static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
82 				    struct mtd_oob_region *region)
83 {
84 	if (section)
85 		return -ERANGE;
86 
87 	region->offset = 64;
88 	region->length = 64;
89 
90 	return 0;
91 }
92 
93 static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
94 				     struct mtd_oob_region *region)
95 {
96 	if (section > 3)
97 		return -ERANGE;
98 
99 	region->offset = (16 * section) + 4;
100 	region->length = 12;
101 
102 	return 0;
103 }
104 
105 static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
106 	.ecc = fm25s01a_ooblayout_ecc,
107 	.free = fm25s01a_ooblayout_free,
108 };
109 
110 static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
111 	.ecc = fm25s01bi3_ooblayout_ecc,
112 	.free = fm25s01bi3_ooblayout_free,
113 };
114 
115 static const struct spinand_info fmsh_spinand_table[] = {
116 	SPINAND_INFO("FM25S01A",
117 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
118 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
119 		     NAND_ECCREQ(1, 512),
120 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
121 					      &write_cache_variants,
122 					      &update_cache_variants),
123 		     0,
124 		     SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
125 	SPINAND_INFO("FM25S01BI3",
126 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
127 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
128 		     NAND_ECCREQ(8, 512),
129 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
130 					      &write_cache_variants,
131 					      &update_cache_variants),
132 		     SPINAND_HAS_QE_BIT,
133 		     SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
134 				      fm25s01bi3_ecc_get_status)),
135 };
136 
137 static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
138 };
139 
140 const struct spinand_manufacturer fmsh_spinand_manufacturer = {
141 	.id = SPINAND_MFR_FMSH,
142 	.name = "Fudan Micro",
143 	.chips = fmsh_spinand_table,
144 	.nchips = ARRAY_SIZE(fmsh_spinand_table),
145 	.ops = &fmsh_spinand_manuf_ops,
146 };
147