1b00e662dSJon Lin // SPDX-License-Identifier: GPL-2.0
2b00e662dSJon Lin /*
3b00e662dSJon Lin * Copyright (c) 2021 Rockchip Electronics Co., Ltd
4b00e662dSJon Lin *
5b00e662dSJon Lin * Authors:
6b00e662dSJon Lin * Dingqiang Lin <jon.lin@rock-chips.com>
7b00e662dSJon Lin */
8b00e662dSJon Lin
9b00e662dSJon Lin #ifndef __UBOOT__
10b00e662dSJon Lin #include <linux/device.h>
11b00e662dSJon Lin #include <linux/kernel.h>
12b00e662dSJon Lin #endif
13b00e662dSJon Lin #include <linux/mtd/spinand.h>
14b00e662dSJon Lin
1524e784d8SJon Lin #define SPINAND_MFR_UNIM_ZL 0xA1
1624e784d8SJon Lin #define SPINAND_MFR_UNIM 0xB0
17b00e662dSJon Lin
18b00e662dSJon Lin static SPINAND_OP_VARIANTS(read_cache_variants,
19b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
20b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
21b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
22b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
23b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
24b00e662dSJon Lin SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
25b00e662dSJon Lin
26b00e662dSJon Lin static SPINAND_OP_VARIANTS(write_cache_variants,
27b00e662dSJon Lin SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
28b00e662dSJon Lin SPINAND_PROG_LOAD(true, 0, NULL, 0));
29b00e662dSJon Lin
30b00e662dSJon Lin static SPINAND_OP_VARIANTS(update_cache_variants,
315f8f61dbSJon Lin SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
325f8f61dbSJon Lin SPINAND_PROG_LOAD(true, 0, NULL, 0));
33b00e662dSJon Lin
tx25g01_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)34b00e662dSJon Lin static int tx25g01_ooblayout_ecc(struct mtd_info *mtd, int section,
35b00e662dSJon Lin struct mtd_oob_region *region)
36b00e662dSJon Lin {
37b00e662dSJon Lin if (section > 3)
38b00e662dSJon Lin return -ERANGE;
39b00e662dSJon Lin
40b00e662dSJon Lin region->offset = (16 * section) + 8;
41b00e662dSJon Lin region->length = 8;
42b00e662dSJon Lin
43b00e662dSJon Lin return 0;
44b00e662dSJon Lin }
45b00e662dSJon Lin
tx25g01_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)46b00e662dSJon Lin static int tx25g01_ooblayout_free(struct mtd_info *mtd, int section,
47b00e662dSJon Lin struct mtd_oob_region *region)
48b00e662dSJon Lin {
49b00e662dSJon Lin if (section > 3)
50b00e662dSJon Lin return -ERANGE;
51b00e662dSJon Lin
52b00e662dSJon Lin region->offset = (16 * section) + 2;
53b00e662dSJon Lin region->length = 6;
54b00e662dSJon Lin
55b00e662dSJon Lin return 0;
56b00e662dSJon Lin }
57b00e662dSJon Lin
58b00e662dSJon Lin static const struct mtd_ooblayout_ops tx25g01_ooblayout = {
59b00e662dSJon Lin .ecc = tx25g01_ooblayout_ecc,
60b00e662dSJon Lin .rfree = tx25g01_ooblayout_free,
61b00e662dSJon Lin };
62b00e662dSJon Lin
6324e784d8SJon Lin
um19a0xisw_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)6424e784d8SJon Lin static int um19a0xisw_ooblayout_ecc(struct mtd_info *mtd, int section,
6524e784d8SJon Lin struct mtd_oob_region *region)
6624e784d8SJon Lin {
6724e784d8SJon Lin return -ERANGE;
6824e784d8SJon Lin }
6924e784d8SJon Lin
um19a0xisw_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)7024e784d8SJon Lin static int um19a0xisw_ooblayout_free(struct mtd_info *mtd, int section,
7124e784d8SJon Lin struct mtd_oob_region *region)
7224e784d8SJon Lin {
7324e784d8SJon Lin if (section)
7424e784d8SJon Lin return -ERANGE;
7524e784d8SJon Lin
7624e784d8SJon Lin region->offset = 2;
7724e784d8SJon Lin region->length = 62;
7824e784d8SJon Lin
7924e784d8SJon Lin return 0;
8024e784d8SJon Lin }
8124e784d8SJon Lin
8224e784d8SJon Lin static const struct mtd_ooblayout_ops um19a0xisw_ooblayout = {
8324e784d8SJon Lin .ecc = um19a0xisw_ooblayout_ecc,
8424e784d8SJon Lin .rfree = um19a0xisw_ooblayout_free,
8524e784d8SJon Lin };
8624e784d8SJon Lin
um19a1xisw_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)8724e784d8SJon Lin static int um19a1xisw_ooblayout_ecc(struct mtd_info *mtd, int section,
8824e784d8SJon Lin struct mtd_oob_region *region)
8924e784d8SJon Lin {
9024e784d8SJon Lin if (section)
9124e784d8SJon Lin return -ERANGE;
9224e784d8SJon Lin
9324e784d8SJon Lin region->offset = 64;
9424e784d8SJon Lin region->length = 64;
9524e784d8SJon Lin
9624e784d8SJon Lin return 0;
9724e784d8SJon Lin }
9824e784d8SJon Lin
um19a1xisw_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)9924e784d8SJon Lin static int um19a1xisw_ooblayout_free(struct mtd_info *mtd, int section,
10024e784d8SJon Lin struct mtd_oob_region *region)
10124e784d8SJon Lin {
10224e784d8SJon Lin if (section)
10324e784d8SJon Lin return -ERANGE;
10424e784d8SJon Lin
10524e784d8SJon Lin region->offset = 2;
10624e784d8SJon Lin region->length = 62;
10724e784d8SJon Lin
10824e784d8SJon Lin return 0;
10924e784d8SJon Lin }
11024e784d8SJon Lin
11124e784d8SJon Lin static const struct mtd_ooblayout_ops um19a1xisw_ooblayout = {
11224e784d8SJon Lin .ecc = um19a1xisw_ooblayout_ecc,
11324e784d8SJon Lin .rfree = um19a1xisw_ooblayout_free,
11424e784d8SJon Lin };
11524e784d8SJon Lin
116b00e662dSJon Lin /*
117b00e662dSJon Lin * ecc bits: 0xC0[4,6]
118b00e662dSJon Lin * [0b000], No bit errors were detected;
119b00e662dSJon Lin * [0b001, 0b011], 1~3 Bit errors were detected and corrected. Not
120b00e662dSJon Lin * reach Flipping Bits;
121b00e662dSJon Lin * [0b100], Bit error count equals the bit flip
122b00e662dSJon Lin * detection threshold
123b00e662dSJon Lin * others, Reserved.
124b00e662dSJon Lin */
tx25g01_ecc_get_status(struct spinand_device * spinand,u8 status)125b00e662dSJon Lin static int tx25g01_ecc_get_status(struct spinand_device *spinand,
126b00e662dSJon Lin u8 status)
127b00e662dSJon Lin {
128ed68cf16SJon Lin struct nand_device *nand = spinand_to_nand(spinand);
129ed68cf16SJon Lin u8 eccsr = (status & GENMASK(6, 4)) >> 4;
130b00e662dSJon Lin
131ed68cf16SJon Lin if (eccsr < 4)
132b00e662dSJon Lin return eccsr;
133ed68cf16SJon Lin else if (eccsr == 4)
134ed68cf16SJon Lin return nand->eccreq.strength;
135b00e662dSJon Lin else
136b00e662dSJon Lin return -EBADMSG;
137b00e662dSJon Lin }
138b00e662dSJon Lin
13924e784d8SJon Lin /*
14024e784d8SJon Lin * ecc bits: 0xC0[4,6]
14124e784d8SJon Lin * [0b000], No bit errors were detected;
14224e784d8SJon Lin * [0b001] and [0b011], 1~6 Bit errors were detected and corrected. Not
14324e784d8SJon Lin * reach Flipping Bits;
14424e784d8SJon Lin * [0b101], Bit error count equals the bit flip
14524e784d8SJon Lin * detection threshold
14624e784d8SJon Lin * [0b010], Multiple bit errors were detected and
14724e784d8SJon Lin * not corrected.
14824e784d8SJon Lin * others, Reserved.
14924e784d8SJon Lin */
um19axxisw_ecc_ecc_get_status(struct spinand_device * spinand,u8 status)15024e784d8SJon Lin static int um19axxisw_ecc_ecc_get_status(struct spinand_device *spinand,
15124e784d8SJon Lin u8 status)
15224e784d8SJon Lin {
15324e784d8SJon Lin struct nand_device *nand = spinand_to_nand(spinand);
15424e784d8SJon Lin u8 eccsr = (status & GENMASK(6, 4)) >> 4;
15524e784d8SJon Lin
15624e784d8SJon Lin if (eccsr <= 1 || eccsr == 3)
15724e784d8SJon Lin return eccsr;
15824e784d8SJon Lin else if (eccsr == 5)
15924e784d8SJon Lin return nand->eccreq.strength;
16024e784d8SJon Lin else
16124e784d8SJon Lin return -EBADMSG;
16224e784d8SJon Lin }
16324e784d8SJon Lin
164*63157c44SJon Lin /*
165*63157c44SJon Lin * ecc bits: 0xC0[4,5]
166*63157c44SJon Lin * 0b00, No bit errors were detected
167*63157c44SJon Lin * 0b01, Bit errors were detected and corrected.
168*63157c44SJon Lin * 0b10, Multiple bit errors were detected and not corrected.
169*63157c44SJon Lin * 0b11, Bits errors were detected and corrected, bit error count
170*63157c44SJon Lin * reach the bit flip detection threshold
171*63157c44SJon Lin */
um19a9xisw_ecc_get_status(struct spinand_device * spinand,u8 status)172*63157c44SJon Lin static int um19a9xisw_ecc_get_status(struct spinand_device *spinand,
173*63157c44SJon Lin u8 status)
174*63157c44SJon Lin {
175*63157c44SJon Lin struct nand_device *nand = spinand_to_nand(spinand);
176*63157c44SJon Lin
177*63157c44SJon Lin switch (status & STATUS_ECC_MASK) {
178*63157c44SJon Lin case STATUS_ECC_NO_BITFLIPS:
179*63157c44SJon Lin case STATUS_ECC_HAS_BITFLIPS:
180*63157c44SJon Lin return 0;
181*63157c44SJon Lin case STATUS_ECC_UNCOR_ERROR:
182*63157c44SJon Lin return -EBADMSG;
183*63157c44SJon Lin default:
184*63157c44SJon Lin return nand->eccreq.strength;
185*63157c44SJon Lin }
186*63157c44SJon Lin }
187*63157c44SJon Lin
18824e784d8SJon Lin static const struct spinand_info unim_zl_spinand_table[] = {
18981afcfe1SJon Lin SPINAND_INFO("TX25G01",
19081afcfe1SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xF1),
191b00e662dSJon Lin NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
192b00e662dSJon Lin NAND_ECCREQ(4, 512),
193b00e662dSJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
194b00e662dSJon Lin &write_cache_variants,
195b00e662dSJon Lin &update_cache_variants),
196b00e662dSJon Lin SPINAND_HAS_QE_BIT,
197b00e662dSJon Lin SPINAND_ECCINFO(&tx25g01_ooblayout, tx25g01_ecc_get_status)),
198b00e662dSJon Lin };
199b00e662dSJon Lin
20024e784d8SJon Lin static const struct spinand_info unim_spinand_table[] = {
20124e784d8SJon Lin SPINAND_INFO("UM19A1HISW",
20224e784d8SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
20324e784d8SJon Lin NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
20424e784d8SJon Lin NAND_ECCREQ(8, 512),
20524e784d8SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
20624e784d8SJon Lin &write_cache_variants,
20724e784d8SJon Lin &update_cache_variants),
20824e784d8SJon Lin SPINAND_HAS_QE_BIT,
20924e784d8SJon Lin SPINAND_ECCINFO(&um19a1xisw_ooblayout, um19axxisw_ecc_ecc_get_status)),
21024e784d8SJon Lin SPINAND_INFO("UM19A0HCSW",
21124e784d8SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
21224e784d8SJon Lin NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
21324e784d8SJon Lin NAND_ECCREQ(8, 512),
21424e784d8SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
21524e784d8SJon Lin &write_cache_variants,
21624e784d8SJon Lin &update_cache_variants),
21724e784d8SJon Lin SPINAND_HAS_QE_BIT,
21824e784d8SJon Lin SPINAND_ECCINFO(&um19a0xisw_ooblayout, um19axxisw_ecc_ecc_get_status)),
21924e784d8SJon Lin SPINAND_INFO("UM19A0LCSW",
22024e784d8SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
22124e784d8SJon Lin NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
22224e784d8SJon Lin NAND_ECCREQ(8, 512),
22324e784d8SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
22424e784d8SJon Lin &write_cache_variants,
22524e784d8SJon Lin &update_cache_variants),
22624e784d8SJon Lin SPINAND_HAS_QE_BIT,
22724e784d8SJon Lin SPINAND_ECCINFO(&um19a0xisw_ooblayout, um19axxisw_ecc_ecc_get_status)),
22824e784d8SJon Lin SPINAND_INFO("UM19A1LISW",
22924e784d8SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
23024e784d8SJon Lin NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
23124e784d8SJon Lin NAND_ECCREQ(8, 512),
23224e784d8SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
23324e784d8SJon Lin &write_cache_variants,
23424e784d8SJon Lin &update_cache_variants),
23524e784d8SJon Lin SPINAND_HAS_QE_BIT,
23624e784d8SJon Lin SPINAND_ECCINFO(&um19a1xisw_ooblayout, um19axxisw_ecc_ecc_get_status)),
237*63157c44SJon Lin SPINAND_INFO("UM19A9LISW",
238*63157c44SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x0D),
239*63157c44SJon Lin NAND_MEMORG(1, 2048, 128, 64, 512, 1, 1, 1),
240*63157c44SJon Lin NAND_ECCREQ(4, 512),
241*63157c44SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
242*63157c44SJon Lin &write_cache_variants,
243*63157c44SJon Lin &update_cache_variants),
244*63157c44SJon Lin SPINAND_HAS_QE_BIT,
245*63157c44SJon Lin SPINAND_ECCINFO(&um19a1xisw_ooblayout, um19a9xisw_ecc_get_status)),
246*63157c44SJon Lin SPINAND_INFO("UM19A9HISW",
247*63157c44SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x0C),
248*63157c44SJon Lin NAND_MEMORG(1, 2048, 128, 64, 512, 1, 1, 1),
249*63157c44SJon Lin NAND_ECCREQ(4, 512),
250*63157c44SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
251*63157c44SJon Lin &write_cache_variants,
252*63157c44SJon Lin &update_cache_variants),
253*63157c44SJon Lin SPINAND_HAS_QE_BIT,
254*63157c44SJon Lin SPINAND_ECCINFO(&um19a1xisw_ooblayout, um19a9xisw_ecc_get_status)),
25524e784d8SJon Lin };
25624e784d8SJon Lin
257b00e662dSJon Lin static const struct spinand_manufacturer_ops unim_spinand_manuf_ops = {
258b00e662dSJon Lin };
259b00e662dSJon Lin
26024e784d8SJon Lin const struct spinand_manufacturer unim_zl_spinand_manufacturer = {
26124e784d8SJon Lin .id = SPINAND_MFR_UNIM_ZL,
26224e784d8SJon Lin .name = "UNIM_ZL",
26324e784d8SJon Lin .chips = unim_zl_spinand_table,
26424e784d8SJon Lin .nchips = ARRAY_SIZE(unim_zl_spinand_table),
26524e784d8SJon Lin .ops = &unim_spinand_manuf_ops,
26624e784d8SJon Lin };
26724e784d8SJon Lin
268b00e662dSJon Lin const struct spinand_manufacturer unim_spinand_manufacturer = {
269b00e662dSJon Lin .id = SPINAND_MFR_UNIM,
270b00e662dSJon Lin .name = "UNIM",
27181afcfe1SJon Lin .chips = unim_spinand_table,
27281afcfe1SJon Lin .nchips = ARRAY_SIZE(unim_spinand_table),
273b00e662dSJon Lin .ops = &unim_spinand_manuf_ops,
274b00e662dSJon Lin };
275