blob: e9239cd59c0da325bb1a01800045334f3ef07bdd [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 ASR Micro Limited
4 *
5 * Authors:
6 * Fei Lv <feilv@asrmicro.com>
7 */
8
9#include <linux/device.h>
10#include <linux/kernel.h>
11#include <linux/mtd/spinand.h>
12
13#define SPINAND_MFR_DOSILICON 0xE5
14
15#define DS35XXGBXXX_STATUS_ECC_MASK GENMASK(6, 4)
16#define DS35XXGBXXX_STATUS_ECC_NO_BITFLIPS (0 << 4)
17#define DS35XXGBXXX_STATUS_ECC_1_3_BITFLIPS (1 << 4)
18#define DS35XXGBXXX_STATUS_ECC_UNCOR_ERROR (2 << 4)
19#define DS35XXGBXXX_STATUS_ECC_4_6_BITFLIPS (3 << 4)
20#define DS35XXGBXXX_STATUS_ECC_7_8_BITFLIPS (5 << 4)
21
22static SPINAND_OP_VARIANTS(read_cache_variants,
23 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
24 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
25 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
26 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
27
28static SPINAND_OP_VARIANTS(write_cache_variants,
29 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
30 SPINAND_PROG_LOAD(true, 0, NULL, 0));
31
32static SPINAND_OP_VARIANTS(update_cache_variants,
33 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
34 SPINAND_PROG_LOAD(false, 0, NULL, 0));
35
36static int ds35xxgaxx_ooblayout_ecc(struct mtd_info *mtd, int section,
37 struct mtd_oob_region *region)
38{
39 if (section > 3)
40 return -ERANGE;
41
42 region->offset = (16 * section) + 8;
43 region->length = 8;
44
45 return 0;
46}
47
48static int ds35xxgaxx_ooblayout_free(struct mtd_info *mtd, int section,
49 struct mtd_oob_region *region)
50{
51 if (section > 3)
52 return -ERANGE;
53
54 region->offset = (16 * section) + 2;
55 region->length = 8;
56
57 return 0;
58}
59
60static const struct mtd_ooblayout_ops ds35xxgaxx_ooblayout = {
61 .ecc = ds35xxgaxx_ooblayout_ecc,
62 .free = ds35xxgaxx_ooblayout_free,
63};
64
65
66static int ds35xxgbxxx_ooblayout_ecc(struct mtd_info *mtd, int section,
67 struct mtd_oob_region *region)
68{
69 if (section > 0)
70 return -ERANGE;
71
72 region->offset = 64;
73 region->length = 64;
74
75 return 0;
76}
77
78static int ds35xxgbxxx_ooblayout_free(struct mtd_info *mtd, int section,
79 struct mtd_oob_region *region)
80{
81 if (section > 0)
82 return -ERANGE;
83
84 /* reserve 2 bytes for the bad block mask */
85 region->offset = 2;
86 region->length = 62;
87
88 return 0;
89}
90
91static int ds35xxgbxxx_ecc_get_status(struct spinand_device *spinand, u8 status)
92{
93 switch (status & DS35XXGBXXX_STATUS_ECC_MASK) {
94 case DS35XXGBXXX_STATUS_ECC_NO_BITFLIPS:
95 return 0;
96
97 case DS35XXGBXXX_STATUS_ECC_1_3_BITFLIPS:
98 return 3;
99
100 case DS35XXGBXXX_STATUS_ECC_4_6_BITFLIPS:
101 return 6;
102
103 case DS35XXGBXXX_STATUS_ECC_7_8_BITFLIPS:
104 return 8;
105
106 case DS35XXGBXXX_STATUS_ECC_UNCOR_ERROR:
107 return -EBADMSG;
108
109 default:
110 break;
111 }
112
113 return -EINVAL;
114}
115
116static const struct mtd_ooblayout_ops ds35xxgbxxx_ooblayout = {
117 .ecc = ds35xxgbxxx_ooblayout_ecc,
118 .free = ds35xxgbxxx_ooblayout_free,
119};
120
121static const struct spinand_info dosilicon_spinand_table[] = {
122 SPINAND_INFO("DS35M1GAxx", 0x21,
123 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
124 NAND_ECCREQ(4, 512),
125 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
126 &write_cache_variants,
127 &update_cache_variants),
128 SPINAND_HAS_QE_BIT | SPINAND_RDM_CMD_HAS_LIMIT,
129 SPINAND_ECCINFO(&ds35xxgaxx_ooblayout, NULL)),
130 SPINAND_INFO("DS35M2GAxx", 0x22,
131 NAND_MEMORG(1, 2048, 64, 64, 2048, 20, 2, 1, 1),
132 NAND_ECCREQ(4, 512),
133 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
134 &write_cache_variants,
135 &update_cache_variants),
136 SPINAND_HAS_QE_BIT | SPINAND_RDM_CMD_HAS_LIMIT,
137 SPINAND_ECCINFO(&ds35xxgaxx_ooblayout, NULL)),
138 SPINAND_INFO("DS35M2GBxx", 0xA2,
139 NAND_MEMORG(1, 2048, 128, 64, 2048, 20, 2, 1, 1),
140 NAND_ECCREQ(8, 512),
141 SPINAND_INFO_OP_VARIANTS_TIMING(
142 &read_cache_variants,
143 &write_cache_variants,
144 &update_cache_variants,
145 10, 2, 2, 78000000),
146 SPINAND_HAS_QE_BIT | SPINAND_RDM_CMD_HAS_LIMIT,
147 SPINAND_ECCINFO(&ds35xxgbxxx_ooblayout,
148 ds35xxgbxxx_ecc_get_status)),
149 SPINAND_INFO("DS35M4GMxx", 0xA4,
150 NAND_MEMORG(1, 2048, 128, 64, 4096, 20, 2, 1, 1),
151 NAND_ECCREQ(8, 512),
152 SPINAND_INFO_OP_VARIANTS_TIMING(
153 &read_cache_variants,
154 &write_cache_variants,
155 &update_cache_variants,
156 10, 2, 2, 78000000),
157 SPINAND_HAS_QE_BIT | SPINAND_RDM_CMD_HAS_LIMIT,
158 SPINAND_ECCINFO(&ds35xxgbxxx_ooblayout,
159 ds35xxgbxxx_ecc_get_status)),
160};
161
162/**
163 * dosilicon_spinand_detect - initialize device related part in spinand_device
164 * struct if it is a Dosilicon device.
165 * @spinand: SPI NAND device structure
166 */
167static int dosilicon_spinand_detect(struct spinand_device *spinand)
168{
169 u8 *id = spinand->id.data;
170 int ret;
171
172 if (id[1] != SPINAND_MFR_DOSILICON)
173 return 0;
174
175 ret = spinand_match_and_init(spinand, dosilicon_spinand_table,
176 ARRAY_SIZE(dosilicon_spinand_table), id[2]);
177 if (ret)
178 return ret;
179
180 return 1;
181}
182
183static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {
184 .detect = dosilicon_spinand_detect,
185};
186
187const struct spinand_manufacturer dosilicon_spinand_manufacturer = {
188 .id = SPINAND_MFR_DOSILICON,
189 .name = "Dosilicon",
190 .ops = &dosilicon_spinand_manuf_ops,
191};