1bb28334dSJon Lin // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
29c409da6SJon Lin /*
3bb28334dSJon Lin * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
49c409da6SJon Lin */
59c409da6SJon Lin
69c409da6SJon Lin #ifndef __UBOOT__
79c409da6SJon Lin #include <linux/device.h>
89c409da6SJon Lin #include <linux/kernel.h>
99c409da6SJon Lin #endif
109c409da6SJon Lin #include <linux/mtd/spinand.h>
119c409da6SJon Lin
129c409da6SJon Lin #define SPINAND_MFR_ETRON 0xD5
139c409da6SJon Lin
149c409da6SJon Lin static SPINAND_OP_VARIANTS(read_cache_variants,
159c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
169c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
179c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
189c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
199c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
209c409da6SJon Lin SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
219c409da6SJon Lin
229c409da6SJon Lin static SPINAND_OP_VARIANTS(write_cache_variants,
239c409da6SJon Lin SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
249c409da6SJon Lin SPINAND_PROG_LOAD(true, 0, NULL, 0));
259c409da6SJon Lin
269c409da6SJon Lin static SPINAND_OP_VARIANTS(update_cache_variants,
279c409da6SJon Lin SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
289c409da6SJon Lin SPINAND_PROG_LOAD(false, 0, NULL, 0));
299c409da6SJon Lin
em73c044vcf_oh_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)309c409da6SJon Lin static int em73c044vcf_oh_ooblayout_ecc(struct mtd_info *mtd, int section,
319c409da6SJon Lin struct mtd_oob_region *region)
329c409da6SJon Lin {
339c409da6SJon Lin if (section > 3)
349c409da6SJon Lin return -ERANGE;
359c409da6SJon Lin
369c409da6SJon Lin region->offset = (16 * section) + 8;
379c409da6SJon Lin region->length = 8;
389c409da6SJon Lin
399c409da6SJon Lin return 0;
409c409da6SJon Lin }
419c409da6SJon Lin
em73c044vcf_oh_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)429c409da6SJon Lin static int em73c044vcf_oh_ooblayout_free(struct mtd_info *mtd, int section,
439c409da6SJon Lin struct mtd_oob_region *region)
449c409da6SJon Lin {
459c409da6SJon Lin if (section > 3)
469c409da6SJon Lin return -ERANGE;
479c409da6SJon Lin
489c409da6SJon Lin region->offset = (16 * section) + 2;
499c409da6SJon Lin region->length = 6;
509c409da6SJon Lin
519c409da6SJon Lin return 0;
529c409da6SJon Lin }
539c409da6SJon Lin
549c409da6SJon Lin static const struct mtd_ooblayout_ops em73c044vcf_oh_ooblayout = {
559c409da6SJon Lin .ecc = em73c044vcf_oh_ooblayout_ecc,
569c409da6SJon Lin .rfree = em73c044vcf_oh_ooblayout_free,
579c409da6SJon Lin };
589c409da6SJon Lin
em73c044vcf_oh_ecc_get_status(struct spinand_device * spinand,u8 status)599c409da6SJon Lin static int em73c044vcf_oh_ecc_get_status(struct spinand_device *spinand,
609c409da6SJon Lin u8 status)
619c409da6SJon Lin {
629c409da6SJon Lin struct nand_device *nand = spinand_to_nand(spinand);
639c409da6SJon Lin
649c409da6SJon Lin switch (status & STATUS_ECC_MASK) {
659c409da6SJon Lin case STATUS_ECC_NO_BITFLIPS:
669c409da6SJon Lin return 0;
679c409da6SJon Lin
689c409da6SJon Lin case STATUS_ECC_UNCOR_ERROR:
699c409da6SJon Lin return -EBADMSG;
709c409da6SJon Lin
719c409da6SJon Lin case STATUS_ECC_HAS_BITFLIPS:
729c409da6SJon Lin return 1;
739c409da6SJon Lin
749c409da6SJon Lin default:
759c409da6SJon Lin return nand->eccreq.strength;
769c409da6SJon Lin }
779c409da6SJon Lin
789c409da6SJon Lin return -EINVAL;
799c409da6SJon Lin }
809c409da6SJon Lin
em73e044vce_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)812be1152dSJon Lin static int em73e044vce_ooblayout_ecc(struct mtd_info *mtd, int section,
822be1152dSJon Lin struct mtd_oob_region *region)
832be1152dSJon Lin {
842be1152dSJon Lin if (section)
852be1152dSJon Lin return -ERANGE;
862be1152dSJon Lin
872be1152dSJon Lin region->offset = mtd->oobsize / 2;
882be1152dSJon Lin region->length = mtd->oobsize / 2;
892be1152dSJon Lin
902be1152dSJon Lin return 0;
912be1152dSJon Lin }
922be1152dSJon Lin
em73e044vce_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)932be1152dSJon Lin static int em73e044vce_ooblayout_free(struct mtd_info *mtd, int section,
942be1152dSJon Lin struct mtd_oob_region *region)
952be1152dSJon Lin {
962be1152dSJon Lin if (section)
972be1152dSJon Lin return -ERANGE;
982be1152dSJon Lin
992be1152dSJon Lin region->offset = 2;
1002be1152dSJon Lin region->length = mtd->oobsize / 2 - 2;
1012be1152dSJon Lin
1022be1152dSJon Lin return 0;
1032be1152dSJon Lin }
1042be1152dSJon Lin
1052be1152dSJon Lin static const struct mtd_ooblayout_ops em73e044vce_ooblayout = {
1062be1152dSJon Lin .ecc = em73e044vce_ooblayout_ecc,
1072be1152dSJon Lin .rfree = em73e044vce_ooblayout_free,
1082be1152dSJon Lin };
1092be1152dSJon Lin
em73e044vce_oh_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)110*382644d4SJon Lin static int em73e044vce_oh_ooblayout_ecc(struct mtd_info *mtd, int section,
111*382644d4SJon Lin struct mtd_oob_region *region)
112*382644d4SJon Lin {
113*382644d4SJon Lin if (section > 3)
114*382644d4SJon Lin return -ERANGE;
115*382644d4SJon Lin
116*382644d4SJon Lin region->offset = (32 * section) + 18;
117*382644d4SJon Lin region->length = 14;
118*382644d4SJon Lin
119*382644d4SJon Lin return 0;
120*382644d4SJon Lin }
121*382644d4SJon Lin
em73e044vce_oh_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)122*382644d4SJon Lin static int em73e044vce_oh_ooblayout_free(struct mtd_info *mtd, int section,
123*382644d4SJon Lin struct mtd_oob_region *region)
124*382644d4SJon Lin {
125*382644d4SJon Lin if (section > 3)
126*382644d4SJon Lin return -ERANGE;
127*382644d4SJon Lin
128*382644d4SJon Lin region->offset = (32 * section) + 2;
129*382644d4SJon Lin region->length = 16;
130*382644d4SJon Lin
131*382644d4SJon Lin return 0;
132*382644d4SJon Lin }
133*382644d4SJon Lin
134*382644d4SJon Lin static const struct mtd_ooblayout_ops em73e044vce_oh_ooblayout = {
135*382644d4SJon Lin .ecc = em73e044vce_oh_ooblayout_ecc,
136*382644d4SJon Lin .rfree = em73e044vce_oh_ooblayout_free,
137*382644d4SJon Lin };
138*382644d4SJon Lin
1399c409da6SJon Lin static const struct spinand_info etron_spinand_table[] = {
14081afcfe1SJon Lin SPINAND_INFO("EM73C044VCF-0H",
14120cb2c76SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x36),
1429c409da6SJon Lin NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
1439c409da6SJon Lin NAND_ECCREQ(4, 512),
1449c409da6SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
1459c409da6SJon Lin &write_cache_variants,
1469c409da6SJon Lin &update_cache_variants),
1479c409da6SJon Lin SPINAND_HAS_QE_BIT,
1489c409da6SJon Lin SPINAND_ECCINFO(&em73c044vcf_oh_ooblayout,
1499c409da6SJon Lin em73c044vcf_oh_ecc_get_status)),
1502be1152dSJon Lin SPINAND_INFO("EM73E044VCE-H",
1512be1152dSJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x3B),
1522be1152dSJon Lin NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
1532be1152dSJon Lin NAND_ECCREQ(8, 512),
1542be1152dSJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
1552be1152dSJon Lin &write_cache_variants,
1562be1152dSJon Lin &update_cache_variants),
1572be1152dSJon Lin SPINAND_HAS_QE_BIT,
1582be1152dSJon Lin SPINAND_ECCINFO(&em73e044vce_ooblayout, em73c044vcf_oh_ecc_get_status)),
159*382644d4SJon Lin SPINAND_INFO("EM73E044VCE-OH",
160*382644d4SJon Lin SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x40),
161*382644d4SJon Lin NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
162*382644d4SJon Lin NAND_ECCREQ(8, 512),
163*382644d4SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
164*382644d4SJon Lin &write_cache_variants,
165*382644d4SJon Lin &update_cache_variants),
166*382644d4SJon Lin SPINAND_HAS_QE_BIT,
167*382644d4SJon Lin SPINAND_ECCINFO(&em73e044vce_oh_ooblayout, em73c044vcf_oh_ecc_get_status)),
1689c409da6SJon Lin };
1699c409da6SJon Lin
1709c409da6SJon Lin static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
1719c409da6SJon Lin };
1729c409da6SJon Lin
1739c409da6SJon Lin const struct spinand_manufacturer etron_spinand_manufacturer = {
1749c409da6SJon Lin .id = SPINAND_MFR_ETRON,
1759c409da6SJon Lin .name = "Etron",
17681afcfe1SJon Lin .chips = etron_spinand_table,
17781afcfe1SJon Lin .nchips = ARRAY_SIZE(etron_spinand_table),
1789c409da6SJon Lin .ops = &etron_spinand_manuf_ops,
1799c409da6SJon Lin };
180