1*6f041ccaSBoris Brezillon // SPDX-License-Identifier: GPL-2.0 2*6f041ccaSBoris Brezillon /* 3*6f041ccaSBoris Brezillon * Copyright (c) 2018 Macronix 4*6f041ccaSBoris Brezillon * 5*6f041ccaSBoris Brezillon * Author: Boris Brezillon <boris.brezillon@bootlin.com> 6*6f041ccaSBoris Brezillon */ 7*6f041ccaSBoris Brezillon 8*6f041ccaSBoris Brezillon #ifndef __UBOOT__ 9*6f041ccaSBoris Brezillon #include <linux/device.h> 10*6f041ccaSBoris Brezillon #include <linux/kernel.h> 11*6f041ccaSBoris Brezillon #endif 12*6f041ccaSBoris Brezillon #include <linux/mtd/spinand.h> 13*6f041ccaSBoris Brezillon 14*6f041ccaSBoris Brezillon #define SPINAND_MFR_MACRONIX 0xC2 15*6f041ccaSBoris Brezillon 16*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(read_cache_variants, 17*6f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 18*6f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 19*6f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 20*6f041ccaSBoris Brezillon SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 21*6f041ccaSBoris Brezillon 22*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(write_cache_variants, 23*6f041ccaSBoris Brezillon SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 24*6f041ccaSBoris Brezillon SPINAND_PROG_LOAD(true, 0, NULL, 0)); 25*6f041ccaSBoris Brezillon 26*6f041ccaSBoris Brezillon static SPINAND_OP_VARIANTS(update_cache_variants, 27*6f041ccaSBoris Brezillon SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 28*6f041ccaSBoris Brezillon SPINAND_PROG_LOAD(false, 0, NULL, 0)); 29*6f041ccaSBoris Brezillon 30*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, 31*6f041ccaSBoris Brezillon struct mtd_oob_region *region) 32*6f041ccaSBoris Brezillon { 33*6f041ccaSBoris Brezillon return -ERANGE; 34*6f041ccaSBoris Brezillon } 35*6f041ccaSBoris Brezillon 36*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, 37*6f041ccaSBoris Brezillon struct mtd_oob_region *region) 38*6f041ccaSBoris Brezillon { 39*6f041ccaSBoris Brezillon if (section) 40*6f041ccaSBoris Brezillon return -ERANGE; 41*6f041ccaSBoris Brezillon 42*6f041ccaSBoris Brezillon region->offset = 2; 43*6f041ccaSBoris Brezillon region->length = mtd->oobsize - 2; 44*6f041ccaSBoris Brezillon 45*6f041ccaSBoris Brezillon return 0; 46*6f041ccaSBoris Brezillon } 47*6f041ccaSBoris Brezillon 48*6f041ccaSBoris Brezillon static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = { 49*6f041ccaSBoris Brezillon .ecc = mx35lf1ge4ab_ooblayout_ecc, 50*6f041ccaSBoris Brezillon .free = mx35lf1ge4ab_ooblayout_free, 51*6f041ccaSBoris Brezillon }; 52*6f041ccaSBoris Brezillon 53*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) 54*6f041ccaSBoris Brezillon { 55*6f041ccaSBoris Brezillon struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), 56*6f041ccaSBoris Brezillon SPI_MEM_OP_NO_ADDR, 57*6f041ccaSBoris Brezillon SPI_MEM_OP_DUMMY(1, 1), 58*6f041ccaSBoris Brezillon SPI_MEM_OP_DATA_IN(1, eccsr, 1)); 59*6f041ccaSBoris Brezillon 60*6f041ccaSBoris Brezillon return spi_mem_exec_op(spinand->slave, &op); 61*6f041ccaSBoris Brezillon } 62*6f041ccaSBoris Brezillon 63*6f041ccaSBoris Brezillon static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, 64*6f041ccaSBoris Brezillon u8 status) 65*6f041ccaSBoris Brezillon { 66*6f041ccaSBoris Brezillon struct nand_device *nand = spinand_to_nand(spinand); 67*6f041ccaSBoris Brezillon u8 eccsr; 68*6f041ccaSBoris Brezillon 69*6f041ccaSBoris Brezillon switch (status & STATUS_ECC_MASK) { 70*6f041ccaSBoris Brezillon case STATUS_ECC_NO_BITFLIPS: 71*6f041ccaSBoris Brezillon return 0; 72*6f041ccaSBoris Brezillon 73*6f041ccaSBoris Brezillon case STATUS_ECC_UNCOR_ERROR: 74*6f041ccaSBoris Brezillon return -EBADMSG; 75*6f041ccaSBoris Brezillon 76*6f041ccaSBoris Brezillon case STATUS_ECC_HAS_BITFLIPS: 77*6f041ccaSBoris Brezillon /* 78*6f041ccaSBoris Brezillon * Let's try to retrieve the real maximum number of bitflips 79*6f041ccaSBoris Brezillon * in order to avoid forcing the wear-leveling layer to move 80*6f041ccaSBoris Brezillon * data around if it's not necessary. 81*6f041ccaSBoris Brezillon */ 82*6f041ccaSBoris Brezillon if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) 83*6f041ccaSBoris Brezillon return nand->eccreq.strength; 84*6f041ccaSBoris Brezillon 85*6f041ccaSBoris Brezillon if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) 86*6f041ccaSBoris Brezillon return nand->eccreq.strength; 87*6f041ccaSBoris Brezillon 88*6f041ccaSBoris Brezillon return eccsr; 89*6f041ccaSBoris Brezillon 90*6f041ccaSBoris Brezillon default: 91*6f041ccaSBoris Brezillon break; 92*6f041ccaSBoris Brezillon } 93*6f041ccaSBoris Brezillon 94*6f041ccaSBoris Brezillon return -EINVAL; 95*6f041ccaSBoris Brezillon } 96*6f041ccaSBoris Brezillon 97*6f041ccaSBoris Brezillon static const struct spinand_info macronix_spinand_table[] = { 98*6f041ccaSBoris Brezillon SPINAND_INFO("MX35LF1GE4AB", 0x12, 99*6f041ccaSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 100*6f041ccaSBoris Brezillon NAND_ECCREQ(4, 512), 101*6f041ccaSBoris Brezillon SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 102*6f041ccaSBoris Brezillon &write_cache_variants, 103*6f041ccaSBoris Brezillon &update_cache_variants), 104*6f041ccaSBoris Brezillon SPINAND_HAS_QE_BIT, 105*6f041ccaSBoris Brezillon SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout, 106*6f041ccaSBoris Brezillon mx35lf1ge4ab_ecc_get_status)), 107*6f041ccaSBoris Brezillon }; 108*6f041ccaSBoris Brezillon 109*6f041ccaSBoris Brezillon static int macronix_spinand_detect(struct spinand_device *spinand) 110*6f041ccaSBoris Brezillon { 111*6f041ccaSBoris Brezillon u8 *id = spinand->id.data; 112*6f041ccaSBoris Brezillon int ret; 113*6f041ccaSBoris Brezillon 114*6f041ccaSBoris Brezillon /* 115*6f041ccaSBoris Brezillon * Macronix SPI NAND read ID needs a dummy byte, so the first byte in 116*6f041ccaSBoris Brezillon * raw_id is garbage. 117*6f041ccaSBoris Brezillon */ 118*6f041ccaSBoris Brezillon if (id[1] != SPINAND_MFR_MACRONIX) 119*6f041ccaSBoris Brezillon return 0; 120*6f041ccaSBoris Brezillon 121*6f041ccaSBoris Brezillon ret = spinand_match_and_init(spinand, macronix_spinand_table, 122*6f041ccaSBoris Brezillon ARRAY_SIZE(macronix_spinand_table), 123*6f041ccaSBoris Brezillon id[2]); 124*6f041ccaSBoris Brezillon if (ret) 125*6f041ccaSBoris Brezillon return ret; 126*6f041ccaSBoris Brezillon 127*6f041ccaSBoris Brezillon return 1; 128*6f041ccaSBoris Brezillon } 129*6f041ccaSBoris Brezillon 130*6f041ccaSBoris Brezillon static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { 131*6f041ccaSBoris Brezillon .detect = macronix_spinand_detect, 132*6f041ccaSBoris Brezillon }; 133*6f041ccaSBoris Brezillon 134*6f041ccaSBoris Brezillon const struct spinand_manufacturer macronix_spinand_manufacturer = { 135*6f041ccaSBoris Brezillon .id = SPINAND_MFR_MACRONIX, 136*6f041ccaSBoris Brezillon .name = "Macronix", 137*6f041ccaSBoris Brezillon .ops = ¯onix_spinand_manuf_ops, 138*6f041ccaSBoris Brezillon }; 139