xref: /rk3399_rockchip-uboot/drivers/mtd/nand/spi/xincun.c (revision 2104c6455f13a313fcc0c86cd762914297ff2920)
118a6bef8SJon Lin /* SPDX-License-Identifier: GPL-2.0 */
218a6bef8SJon Lin /*
318a6bef8SJon Lin  * Copyright (c) 2021 Rockchip Electronics Co., Ltd
418a6bef8SJon Lin  *
518a6bef8SJon Lin  * Authors:
618a6bef8SJon Lin  *	Dingqiang Lin <jon.lin@rock-chips.com>
718a6bef8SJon Lin  */
818a6bef8SJon Lin 
918a6bef8SJon Lin #ifndef __UBOOT__
1018a6bef8SJon Lin #include <linux/device.h>
1118a6bef8SJon Lin #include <linux/kernel.h>
1218a6bef8SJon Lin #endif
1318a6bef8SJon Lin #include <linux/mtd/spinand.h>
1418a6bef8SJon Lin 
1518a6bef8SJon Lin #define SPINAND_MFR_XINCUN		0x8C
1618a6bef8SJon Lin #define XINCUN_STATUS_ECC_HAS_BITFLIPS_T	(3 << 4)
1718a6bef8SJon Lin 
1818a6bef8SJon Lin static SPINAND_OP_VARIANTS(read_cache_variants,
1918a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
2018a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
2118a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
2218a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
2318a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
2418a6bef8SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
2518a6bef8SJon Lin 
2618a6bef8SJon Lin static SPINAND_OP_VARIANTS(write_cache_variants,
2718a6bef8SJon Lin 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
2818a6bef8SJon Lin 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
2918a6bef8SJon Lin 
3018a6bef8SJon Lin static SPINAND_OP_VARIANTS(update_cache_variants,
3118a6bef8SJon Lin 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
3218a6bef8SJon Lin 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
3318a6bef8SJon Lin 
xcsp2aapk_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)3418a6bef8SJon Lin static int xcsp2aapk_ooblayout_ecc(struct mtd_info *mtd, int section,
3518a6bef8SJon Lin 				   struct mtd_oob_region *region)
3618a6bef8SJon Lin {
3718a6bef8SJon Lin 	if (section)
3818a6bef8SJon Lin 		return -ERANGE;
3918a6bef8SJon Lin 
4018a6bef8SJon Lin 	region->offset = mtd->oobsize / 2;
4118a6bef8SJon Lin 	region->length = mtd->oobsize / 2;
4218a6bef8SJon Lin 
4318a6bef8SJon Lin 	return 0;
4418a6bef8SJon Lin }
4518a6bef8SJon Lin 
xcsp2aapk_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)4618a6bef8SJon Lin static int xcsp2aapk_ooblayout_free(struct mtd_info *mtd, int section,
4718a6bef8SJon Lin 				    struct mtd_oob_region *region)
4818a6bef8SJon Lin {
4918a6bef8SJon Lin 	if (section)
5018a6bef8SJon Lin 		return -ERANGE;
5118a6bef8SJon Lin 
5218a6bef8SJon Lin 	/* Reserve 2 bytes for the BBM. */
5318a6bef8SJon Lin 	region->offset = 2;
5418a6bef8SJon Lin 	region->length = mtd->oobsize / 2 - 2;
5518a6bef8SJon Lin 
5618a6bef8SJon Lin 	return 0;
5718a6bef8SJon Lin }
5818a6bef8SJon Lin 
5918a6bef8SJon Lin static const struct mtd_ooblayout_ops xcsp2aapk_ooblayout = {
6018a6bef8SJon Lin 	.ecc = xcsp2aapk_ooblayout_ecc,
6118a6bef8SJon Lin 	.rfree = xcsp2aapk_ooblayout_free,
6218a6bef8SJon Lin };
6318a6bef8SJon Lin 
xcsp2aapk_ecc_get_status(struct spinand_device * spinand,u8 status)6418a6bef8SJon Lin static int xcsp2aapk_ecc_get_status(struct spinand_device *spinand,
6518a6bef8SJon Lin 				    u8 status)
6618a6bef8SJon Lin {
6718a6bef8SJon Lin 	struct nand_device *nand = spinand_to_nand(spinand);
6818a6bef8SJon Lin 
6918a6bef8SJon Lin 	switch (status & STATUS_ECC_MASK) {
7018a6bef8SJon Lin 	case STATUS_ECC_NO_BITFLIPS:
7118a6bef8SJon Lin 		return 0;
7218a6bef8SJon Lin 
7318a6bef8SJon Lin 	case STATUS_ECC_UNCOR_ERROR:
7418a6bef8SJon Lin 		return -EBADMSG;
7518a6bef8SJon Lin 
7618a6bef8SJon Lin 	case STATUS_ECC_HAS_BITFLIPS:
7718a6bef8SJon Lin 		return 0;
7818a6bef8SJon Lin 	case XINCUN_STATUS_ECC_HAS_BITFLIPS_T:
7918a6bef8SJon Lin 		return nand->eccreq.strength;
8018a6bef8SJon Lin 	default:
8118a6bef8SJon Lin 		break;
8218a6bef8SJon Lin 	}
8318a6bef8SJon Lin 
8418a6bef8SJon Lin 	return -EINVAL;
8518a6bef8SJon Lin }
8618a6bef8SJon Lin 
8718a6bef8SJon Lin static const struct spinand_info xincun_spinand_table[] = {
8818a6bef8SJon Lin 	SPINAND_INFO("XCSP2AAPK",
8918a6bef8SJon Lin 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xA1),
9018a6bef8SJon Lin 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
9118a6bef8SJon Lin 		     NAND_ECCREQ(8, 512),
9218a6bef8SJon Lin 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
9318a6bef8SJon Lin 					      &write_cache_variants,
9418a6bef8SJon Lin 					      &update_cache_variants),
9518a6bef8SJon Lin 		     SPINAND_HAS_QE_BIT,
9618a6bef8SJon Lin 		     SPINAND_ECCINFO(&xcsp2aapk_ooblayout, xcsp2aapk_ecc_get_status)),
971ee4a0e5SJon Lin 	SPINAND_INFO("XCSP1AAPK",
98*2104c645SJon Lin 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
991ee4a0e5SJon Lin 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
1001ee4a0e5SJon Lin 		     NAND_ECCREQ(8, 512),
1011ee4a0e5SJon Lin 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
1021ee4a0e5SJon Lin 					      &write_cache_variants,
1031ee4a0e5SJon Lin 					      &update_cache_variants),
1041ee4a0e5SJon Lin 		     SPINAND_HAS_QE_BIT,
1051ee4a0e5SJon Lin 		     SPINAND_ECCINFO(&xcsp2aapk_ooblayout, xcsp2aapk_ecc_get_status)),
10618a6bef8SJon Lin };
10718a6bef8SJon Lin 
10818a6bef8SJon Lin static const struct spinand_manufacturer_ops xincun_spinand_manuf_ops = {
10918a6bef8SJon Lin };
11018a6bef8SJon Lin 
11118a6bef8SJon Lin const struct spinand_manufacturer xincun_spinand_manufacturer = {
11218a6bef8SJon Lin 	.id = SPINAND_MFR_XINCUN,
11318a6bef8SJon Lin 	.name = "XINCUN",
11418a6bef8SJon Lin 	.chips = xincun_spinand_table,
11518a6bef8SJon Lin 	.nchips = ARRAY_SIZE(xincun_spinand_table),
11618a6bef8SJon Lin 	.ops = &xincun_spinand_manuf_ops,
11718a6bef8SJon Lin };
118