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 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 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 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 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 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