16f041ccaSBoris Brezillon // SPDX-License-Identifier: GPL-2.0
26f041ccaSBoris Brezillon /*
36f041ccaSBoris Brezillon * Copyright (c) 2018 Macronix
46f041ccaSBoris Brezillon *
56f041ccaSBoris Brezillon * Author: Boris Brezillon <boris.brezillon@bootlin.com>
66f041ccaSBoris Brezillon */
76f041ccaSBoris Brezillon
86f041ccaSBoris Brezillon #ifndef __UBOOT__
96f041ccaSBoris Brezillon #include <linux/device.h>
106f041ccaSBoris Brezillon #include <linux/kernel.h>
116f041ccaSBoris Brezillon #endif
126f041ccaSBoris Brezillon #include <linux/mtd/spinand.h>
136f041ccaSBoris Brezillon
146f041ccaSBoris Brezillon #define SPINAND_MFR_MACRONIX 0xC2
156f041ccaSBoris Brezillon
166f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(read_cache_variants,
176f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
186f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
196f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
206f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
216f041ccaSBoris Brezillon
226f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(write_cache_variants,
236f041ccaSBoris Brezillon SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
246f041ccaSBoris Brezillon SPINAND_PROG_LOAD(true, 0, NULL, 0));
256f041ccaSBoris Brezillon
266f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(update_cache_variants,
276f041ccaSBoris Brezillon SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
286f041ccaSBoris Brezillon SPINAND_PROG_LOAD(false, 0, NULL, 0));
296f041ccaSBoris Brezillon
mx35lfxge4ab_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)30*515d0212SMiquel Raynal static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
316f041ccaSBoris Brezillon struct mtd_oob_region *region)
326f041ccaSBoris Brezillon {
336f041ccaSBoris Brezillon return -ERANGE;
346f041ccaSBoris Brezillon }
356f041ccaSBoris Brezillon
mx35lfxge4ab_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)36*515d0212SMiquel Raynal static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section,
376f041ccaSBoris Brezillon struct mtd_oob_region *region)
386f041ccaSBoris Brezillon {
396f041ccaSBoris Brezillon if (section)
406f041ccaSBoris Brezillon return -ERANGE;
416f041ccaSBoris Brezillon
426f041ccaSBoris Brezillon region->offset = 2;
436f041ccaSBoris Brezillon region->length = mtd->oobsize - 2;
446f041ccaSBoris Brezillon
456f041ccaSBoris Brezillon return 0;
466f041ccaSBoris Brezillon }
476f041ccaSBoris Brezillon
48*515d0212SMiquel Raynal static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = {
49*515d0212SMiquel Raynal .ecc = mx35lfxge4ab_ooblayout_ecc,
50*515d0212SMiquel Raynal .free = mx35lfxge4ab_ooblayout_free,
516f041ccaSBoris Brezillon };
526f041ccaSBoris Brezillon
mx35lf1ge4ab_get_eccsr(struct spinand_device * spinand,u8 * eccsr)536f041ccaSBoris Brezillon static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
546f041ccaSBoris Brezillon {
556f041ccaSBoris Brezillon struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
566f041ccaSBoris Brezillon SPI_MEM_OP_NO_ADDR,
576f041ccaSBoris Brezillon SPI_MEM_OP_DUMMY(1, 1),
586f041ccaSBoris Brezillon SPI_MEM_OP_DATA_IN(1, eccsr, 1));
596f041ccaSBoris Brezillon
606f041ccaSBoris Brezillon return spi_mem_exec_op(spinand->slave, &op);
616f041ccaSBoris Brezillon }
626f041ccaSBoris Brezillon
mx35lf1ge4ab_ecc_get_status(struct spinand_device * spinand,u8 status)636f041ccaSBoris Brezillon static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
646f041ccaSBoris Brezillon u8 status)
656f041ccaSBoris Brezillon {
666f041ccaSBoris Brezillon struct nand_device *nand = spinand_to_nand(spinand);
676f041ccaSBoris Brezillon u8 eccsr;
686f041ccaSBoris Brezillon
696f041ccaSBoris Brezillon switch (status & STATUS_ECC_MASK) {
706f041ccaSBoris Brezillon case STATUS_ECC_NO_BITFLIPS:
716f041ccaSBoris Brezillon return 0;
726f041ccaSBoris Brezillon
736f041ccaSBoris Brezillon case STATUS_ECC_UNCOR_ERROR:
746f041ccaSBoris Brezillon return -EBADMSG;
756f041ccaSBoris Brezillon
766f041ccaSBoris Brezillon case STATUS_ECC_HAS_BITFLIPS:
776f041ccaSBoris Brezillon /*
786f041ccaSBoris Brezillon * Let's try to retrieve the real maximum number of bitflips
796f041ccaSBoris Brezillon * in order to avoid forcing the wear-leveling layer to move
806f041ccaSBoris Brezillon * data around if it's not necessary.
816f041ccaSBoris Brezillon */
826f041ccaSBoris Brezillon if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
836f041ccaSBoris Brezillon return nand->eccreq.strength;
846f041ccaSBoris Brezillon
856f041ccaSBoris Brezillon if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr))
866f041ccaSBoris Brezillon return nand->eccreq.strength;
876f041ccaSBoris Brezillon
886f041ccaSBoris Brezillon return eccsr;
896f041ccaSBoris Brezillon
906f041ccaSBoris Brezillon default:
916f041ccaSBoris Brezillon break;
926f041ccaSBoris Brezillon }
936f041ccaSBoris Brezillon
946f041ccaSBoris Brezillon return -EINVAL;
956f041ccaSBoris Brezillon }
966f041ccaSBoris Brezillon
976f041ccaSBoris Brezillon static const struct spinand_info macronix_spinand_table[] = {
986f041ccaSBoris Brezillon SPINAND_INFO("MX35LF1GE4AB", 0x12,
996f041ccaSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
1006f041ccaSBoris Brezillon NAND_ECCREQ(4, 512),
1016f041ccaSBoris Brezillon SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
1026f041ccaSBoris Brezillon &write_cache_variants,
1036f041ccaSBoris Brezillon &update_cache_variants),
1046f041ccaSBoris Brezillon SPINAND_HAS_QE_BIT,
105*515d0212SMiquel Raynal SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
1066f041ccaSBoris Brezillon mx35lf1ge4ab_ecc_get_status)),
107*515d0212SMiquel Raynal SPINAND_INFO("MX35LF2GE4AB", 0x22,
108*515d0212SMiquel Raynal NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
109*515d0212SMiquel Raynal NAND_ECCREQ(4, 512),
110*515d0212SMiquel Raynal SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
111*515d0212SMiquel Raynal &write_cache_variants,
112*515d0212SMiquel Raynal &update_cache_variants),
113*515d0212SMiquel Raynal SPINAND_HAS_QE_BIT,
114*515d0212SMiquel Raynal SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
1156f041ccaSBoris Brezillon };
1166f041ccaSBoris Brezillon
macronix_spinand_detect(struct spinand_device * spinand)1176f041ccaSBoris Brezillon static int macronix_spinand_detect(struct spinand_device *spinand)
1186f041ccaSBoris Brezillon {
1196f041ccaSBoris Brezillon u8 *id = spinand->id.data;
1206f041ccaSBoris Brezillon int ret;
1216f041ccaSBoris Brezillon
1226f041ccaSBoris Brezillon /*
1236f041ccaSBoris Brezillon * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
1246f041ccaSBoris Brezillon * raw_id is garbage.
1256f041ccaSBoris Brezillon */
1266f041ccaSBoris Brezillon if (id[1] != SPINAND_MFR_MACRONIX)
1276f041ccaSBoris Brezillon return 0;
1286f041ccaSBoris Brezillon
1296f041ccaSBoris Brezillon ret = spinand_match_and_init(spinand, macronix_spinand_table,
1306f041ccaSBoris Brezillon ARRAY_SIZE(macronix_spinand_table),
1316f041ccaSBoris Brezillon id[2]);
1326f041ccaSBoris Brezillon if (ret)
1336f041ccaSBoris Brezillon return ret;
1346f041ccaSBoris Brezillon
1356f041ccaSBoris Brezillon return 1;
1366f041ccaSBoris Brezillon }
1376f041ccaSBoris Brezillon
1386f041ccaSBoris Brezillon static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
1396f041ccaSBoris Brezillon .detect = macronix_spinand_detect,
1406f041ccaSBoris Brezillon };
1416f041ccaSBoris Brezillon
1426f041ccaSBoris Brezillon const struct spinand_manufacturer macronix_spinand_manufacturer = {
1436f041ccaSBoris Brezillon .id = SPINAND_MFR_MACRONIX,
1446f041ccaSBoris Brezillon .name = "Macronix",
1456f041ccaSBoris Brezillon .ops = ¯onix_spinand_manuf_ops,
1466f041ccaSBoris Brezillon };
147