1*a508e887SPeter Pan // SPDX-License-Identifier: GPL-2.0 2*a508e887SPeter Pan /* 3*a508e887SPeter Pan * Copyright (c) 2016-2017 Micron Technology, Inc. 4*a508e887SPeter Pan * 5*a508e887SPeter Pan * Authors: 6*a508e887SPeter Pan * Peter Pan <peterpandong@micron.com> 7*a508e887SPeter Pan */ 8*a508e887SPeter Pan 9*a508e887SPeter Pan #include <linux/device.h> 10*a508e887SPeter Pan #include <linux/kernel.h> 11*a508e887SPeter Pan #include <linux/mtd/spinand.h> 12*a508e887SPeter Pan 13*a508e887SPeter Pan #define SPINAND_MFR_MICRON 0x2c 14*a508e887SPeter Pan 15*a508e887SPeter Pan #define MICRON_STATUS_ECC_MASK GENMASK(7, 4) 16*a508e887SPeter Pan #define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4) 17*a508e887SPeter Pan #define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) 18*a508e887SPeter Pan #define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) 19*a508e887SPeter Pan #define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) 20*a508e887SPeter Pan 21*a508e887SPeter Pan static SPINAND_OP_VARIANTS(read_cache_variants, 22*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 23*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 24*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 25*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 26*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 27*a508e887SPeter Pan SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 28*a508e887SPeter Pan 29*a508e887SPeter Pan static SPINAND_OP_VARIANTS(write_cache_variants, 30*a508e887SPeter Pan SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 31*a508e887SPeter Pan SPINAND_PROG_LOAD(true, 0, NULL, 0)); 32*a508e887SPeter Pan 33*a508e887SPeter Pan static SPINAND_OP_VARIANTS(update_cache_variants, 34*a508e887SPeter Pan SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 35*a508e887SPeter Pan SPINAND_PROG_LOAD(false, 0, NULL, 0)); 36*a508e887SPeter Pan 37*a508e887SPeter Pan static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, 38*a508e887SPeter Pan struct mtd_oob_region *region) 39*a508e887SPeter Pan { 40*a508e887SPeter Pan if (section) 41*a508e887SPeter Pan return -ERANGE; 42*a508e887SPeter Pan 43*a508e887SPeter Pan region->offset = 64; 44*a508e887SPeter Pan region->length = 64; 45*a508e887SPeter Pan 46*a508e887SPeter Pan return 0; 47*a508e887SPeter Pan } 48*a508e887SPeter Pan 49*a508e887SPeter Pan static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, 50*a508e887SPeter Pan struct mtd_oob_region *region) 51*a508e887SPeter Pan { 52*a508e887SPeter Pan if (section) 53*a508e887SPeter Pan return -ERANGE; 54*a508e887SPeter Pan 55*a508e887SPeter Pan /* Reserve 2 bytes for the BBM. */ 56*a508e887SPeter Pan region->offset = 2; 57*a508e887SPeter Pan region->length = 62; 58*a508e887SPeter Pan 59*a508e887SPeter Pan return 0; 60*a508e887SPeter Pan } 61*a508e887SPeter Pan 62*a508e887SPeter Pan static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { 63*a508e887SPeter Pan .ecc = mt29f2g01abagd_ooblayout_ecc, 64*a508e887SPeter Pan .free = mt29f2g01abagd_ooblayout_free, 65*a508e887SPeter Pan }; 66*a508e887SPeter Pan 67*a508e887SPeter Pan static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, 68*a508e887SPeter Pan u8 status) 69*a508e887SPeter Pan { 70*a508e887SPeter Pan switch (status & MICRON_STATUS_ECC_MASK) { 71*a508e887SPeter Pan case STATUS_ECC_NO_BITFLIPS: 72*a508e887SPeter Pan return 0; 73*a508e887SPeter Pan 74*a508e887SPeter Pan case STATUS_ECC_UNCOR_ERROR: 75*a508e887SPeter Pan return -EBADMSG; 76*a508e887SPeter Pan 77*a508e887SPeter Pan case MICRON_STATUS_ECC_1TO3_BITFLIPS: 78*a508e887SPeter Pan return 3; 79*a508e887SPeter Pan 80*a508e887SPeter Pan case MICRON_STATUS_ECC_4TO6_BITFLIPS: 81*a508e887SPeter Pan return 6; 82*a508e887SPeter Pan 83*a508e887SPeter Pan case MICRON_STATUS_ECC_7TO8_BITFLIPS: 84*a508e887SPeter Pan return 8; 85*a508e887SPeter Pan 86*a508e887SPeter Pan default: 87*a508e887SPeter Pan break; 88*a508e887SPeter Pan } 89*a508e887SPeter Pan 90*a508e887SPeter Pan return -EINVAL; 91*a508e887SPeter Pan } 92*a508e887SPeter Pan 93*a508e887SPeter Pan static const struct spinand_info micron_spinand_table[] = { 94*a508e887SPeter Pan SPINAND_INFO("MT29F2G01ABAGD", 0x24, 95*a508e887SPeter Pan NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), 96*a508e887SPeter Pan NAND_ECCREQ(8, 512), 97*a508e887SPeter Pan SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 98*a508e887SPeter Pan &write_cache_variants, 99*a508e887SPeter Pan &update_cache_variants), 100*a508e887SPeter Pan 0, 101*a508e887SPeter Pan SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, 102*a508e887SPeter Pan mt29f2g01abagd_ecc_get_status)), 103*a508e887SPeter Pan }; 104*a508e887SPeter Pan 105*a508e887SPeter Pan static int micron_spinand_detect(struct spinand_device *spinand) 106*a508e887SPeter Pan { 107*a508e887SPeter Pan u8 *id = spinand->id.data; 108*a508e887SPeter Pan int ret; 109*a508e887SPeter Pan 110*a508e887SPeter Pan /* 111*a508e887SPeter Pan * Micron SPI NAND read ID need a dummy byte, 112*a508e887SPeter Pan * so the first byte in raw_id is dummy. 113*a508e887SPeter Pan */ 114*a508e887SPeter Pan if (id[1] != SPINAND_MFR_MICRON) 115*a508e887SPeter Pan return 0; 116*a508e887SPeter Pan 117*a508e887SPeter Pan ret = spinand_match_and_init(spinand, micron_spinand_table, 118*a508e887SPeter Pan ARRAY_SIZE(micron_spinand_table), id[2]); 119*a508e887SPeter Pan if (ret) 120*a508e887SPeter Pan return ret; 121*a508e887SPeter Pan 122*a508e887SPeter Pan return 1; 123*a508e887SPeter Pan } 124*a508e887SPeter Pan 125*a508e887SPeter Pan static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { 126*a508e887SPeter Pan .detect = micron_spinand_detect, 127*a508e887SPeter Pan }; 128*a508e887SPeter Pan 129*a508e887SPeter Pan const struct spinand_manufacturer micron_spinand_manufacturer = { 130*a508e887SPeter Pan .id = SPINAND_MFR_MICRON, 131*a508e887SPeter Pan .name = "Micron", 132*a508e887SPeter Pan .ops = µn_spinand_manuf_ops, 133*a508e887SPeter Pan }; 134