1*f4c5c7f9SFelix Matouschek // SPDX-License-Identifier: GPL-2.0 2*f4c5c7f9SFelix Matouschek /* 3*f4c5c7f9SFelix Matouschek * Author: 4*f4c5c7f9SFelix Matouschek * Felix Matouschek <felix@matouschek.org> 5*f4c5c7f9SFelix Matouschek */ 6*f4c5c7f9SFelix Matouschek 7*f4c5c7f9SFelix Matouschek #include <linux/device.h> 8*f4c5c7f9SFelix Matouschek #include <linux/kernel.h> 9*f4c5c7f9SFelix Matouschek #include <linux/mtd/spinand.h> 10*f4c5c7f9SFelix Matouschek 11*f4c5c7f9SFelix Matouschek #define SPINAND_MFR_XTX 0x0B 12*f4c5c7f9SFelix Matouschek 13*f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2) 14*f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2) 15*f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) 16*f4c5c7f9SFelix Matouschek #define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4) 17*f4c5c7f9SFelix Matouschek 18*f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(read_cache_variants, 19*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), 20*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 21*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 22*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 23*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 24*f4c5c7f9SFelix Matouschek SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 25*f4c5c7f9SFelix Matouschek 26*f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(write_cache_variants, 27*f4c5c7f9SFelix Matouschek SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 28*f4c5c7f9SFelix Matouschek SPINAND_PROG_LOAD(true, 0, NULL, 0)); 29*f4c5c7f9SFelix Matouschek 30*f4c5c7f9SFelix Matouschek static SPINAND_OP_VARIANTS(update_cache_variants, 31*f4c5c7f9SFelix Matouschek SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 32*f4c5c7f9SFelix Matouschek SPINAND_PROG_LOAD(false, 0, NULL, 0)); 33*f4c5c7f9SFelix Matouschek 34*f4c5c7f9SFelix Matouschek static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, 35*f4c5c7f9SFelix Matouschek struct mtd_oob_region *region) 36*f4c5c7f9SFelix Matouschek { 37*f4c5c7f9SFelix Matouschek if (section) 38*f4c5c7f9SFelix Matouschek return -ERANGE; 39*f4c5c7f9SFelix Matouschek 40*f4c5c7f9SFelix Matouschek region->offset = 48; 41*f4c5c7f9SFelix Matouschek region->length = 16; 42*f4c5c7f9SFelix Matouschek 43*f4c5c7f9SFelix Matouschek return 0; 44*f4c5c7f9SFelix Matouschek } 45*f4c5c7f9SFelix Matouschek 46*f4c5c7f9SFelix Matouschek static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, 47*f4c5c7f9SFelix Matouschek struct mtd_oob_region *region) 48*f4c5c7f9SFelix Matouschek { 49*f4c5c7f9SFelix Matouschek if (section) 50*f4c5c7f9SFelix Matouschek return -ERANGE; 51*f4c5c7f9SFelix Matouschek 52*f4c5c7f9SFelix Matouschek region->offset = 1; 53*f4c5c7f9SFelix Matouschek region->length = 47; 54*f4c5c7f9SFelix Matouschek 55*f4c5c7f9SFelix Matouschek return 0; 56*f4c5c7f9SFelix Matouschek } 57*f4c5c7f9SFelix Matouschek 58*f4c5c7f9SFelix Matouschek static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { 59*f4c5c7f9SFelix Matouschek .ecc = xt26g0xa_ooblayout_ecc, 60*f4c5c7f9SFelix Matouschek .free = xt26g0xa_ooblayout_free, 61*f4c5c7f9SFelix Matouschek }; 62*f4c5c7f9SFelix Matouschek 63*f4c5c7f9SFelix Matouschek static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, 64*f4c5c7f9SFelix Matouschek u8 status) 65*f4c5c7f9SFelix Matouschek { 66*f4c5c7f9SFelix Matouschek status = status & XT26G0XA_STATUS_ECC_MASK; 67*f4c5c7f9SFelix Matouschek 68*f4c5c7f9SFelix Matouschek switch (status) { 69*f4c5c7f9SFelix Matouschek case XT26G0XA_STATUS_ECC_NO_DETECTED: 70*f4c5c7f9SFelix Matouschek return 0; 71*f4c5c7f9SFelix Matouschek case XT26G0XA_STATUS_ECC_8_CORRECTED: 72*f4c5c7f9SFelix Matouschek return 8; 73*f4c5c7f9SFelix Matouschek case XT26G0XA_STATUS_ECC_UNCOR_ERROR: 74*f4c5c7f9SFelix Matouschek return -EBADMSG; 75*f4c5c7f9SFelix Matouschek default: 76*f4c5c7f9SFelix Matouschek break; 77*f4c5c7f9SFelix Matouschek } 78*f4c5c7f9SFelix Matouschek 79*f4c5c7f9SFelix Matouschek /* At this point values greater than (2 << 4) are invalid */ 80*f4c5c7f9SFelix Matouschek if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR) 81*f4c5c7f9SFelix Matouschek return -EINVAL; 82*f4c5c7f9SFelix Matouschek 83*f4c5c7f9SFelix Matouschek /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ 84*f4c5c7f9SFelix Matouschek return status >> 2; 85*f4c5c7f9SFelix Matouschek } 86*f4c5c7f9SFelix Matouschek 87*f4c5c7f9SFelix Matouschek static const struct spinand_info xtx_spinand_table[] = { 88*f4c5c7f9SFelix Matouschek SPINAND_INFO("XT26G01A", 89*f4c5c7f9SFelix Matouschek SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1), 90*f4c5c7f9SFelix Matouschek NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 91*f4c5c7f9SFelix Matouschek NAND_ECCREQ(8, 512), 92*f4c5c7f9SFelix Matouschek SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 93*f4c5c7f9SFelix Matouschek &write_cache_variants, 94*f4c5c7f9SFelix Matouschek &update_cache_variants), 95*f4c5c7f9SFelix Matouschek SPINAND_HAS_QE_BIT, 96*f4c5c7f9SFelix Matouschek SPINAND_ECCINFO(&xt26g0xa_ooblayout, 97*f4c5c7f9SFelix Matouschek xt26g0xa_ecc_get_status)), 98*f4c5c7f9SFelix Matouschek SPINAND_INFO("XT26G02A", 99*f4c5c7f9SFelix Matouschek SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2), 100*f4c5c7f9SFelix Matouschek NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), 101*f4c5c7f9SFelix Matouschek NAND_ECCREQ(8, 512), 102*f4c5c7f9SFelix Matouschek SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 103*f4c5c7f9SFelix Matouschek &write_cache_variants, 104*f4c5c7f9SFelix Matouschek &update_cache_variants), 105*f4c5c7f9SFelix Matouschek SPINAND_HAS_QE_BIT, 106*f4c5c7f9SFelix Matouschek SPINAND_ECCINFO(&xt26g0xa_ooblayout, 107*f4c5c7f9SFelix Matouschek xt26g0xa_ecc_get_status)), 108*f4c5c7f9SFelix Matouschek SPINAND_INFO("XT26G04A", 109*f4c5c7f9SFelix Matouschek SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3), 110*f4c5c7f9SFelix Matouschek NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1), 111*f4c5c7f9SFelix Matouschek NAND_ECCREQ(8, 512), 112*f4c5c7f9SFelix Matouschek SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 113*f4c5c7f9SFelix Matouschek &write_cache_variants, 114*f4c5c7f9SFelix Matouschek &update_cache_variants), 115*f4c5c7f9SFelix Matouschek SPINAND_HAS_QE_BIT, 116*f4c5c7f9SFelix Matouschek SPINAND_ECCINFO(&xt26g0xa_ooblayout, 117*f4c5c7f9SFelix Matouschek xt26g0xa_ecc_get_status)), 118*f4c5c7f9SFelix Matouschek }; 119*f4c5c7f9SFelix Matouschek 120*f4c5c7f9SFelix Matouschek static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { 121*f4c5c7f9SFelix Matouschek }; 122*f4c5c7f9SFelix Matouschek 123*f4c5c7f9SFelix Matouschek const struct spinand_manufacturer xtx_spinand_manufacturer = { 124*f4c5c7f9SFelix Matouschek .id = SPINAND_MFR_XTX, 125*f4c5c7f9SFelix Matouschek .name = "XTX", 126*f4c5c7f9SFelix Matouschek .chips = xtx_spinand_table, 127*f4c5c7f9SFelix Matouschek .nchips = ARRAY_SIZE(xtx_spinand_table), 128*f4c5c7f9SFelix Matouschek .ops = &xtx_spinand_manuf_ops, 129*f4c5c7f9SFelix Matouschek }; 130