1*1a50e361STakahiro Kuwano // SPDX-License-Identifier: GPL-2.0 2*1a50e361STakahiro Kuwano /* 3*1a50e361STakahiro Kuwano * Copyright (c) 2024 SkyHigh Memory Limited 4*1a50e361STakahiro Kuwano * 5*1a50e361STakahiro Kuwano * Author: Takahiro Kuwano <takahiro.kuwano@infineon.com> 6*1a50e361STakahiro Kuwano * Co-Author: KR Kim <kr.kim@skyhighmemory.com> 7*1a50e361STakahiro Kuwano */ 8*1a50e361STakahiro Kuwano 9*1a50e361STakahiro Kuwano #include <linux/device.h> 10*1a50e361STakahiro Kuwano #include <linux/kernel.h> 11*1a50e361STakahiro Kuwano #include <linux/mtd/spinand.h> 12*1a50e361STakahiro Kuwano 13*1a50e361STakahiro Kuwano #define SPINAND_MFR_SKYHIGH 0x01 14*1a50e361STakahiro Kuwano #define SKYHIGH_STATUS_ECC_1TO2_BITFLIPS (1 << 4) 15*1a50e361STakahiro Kuwano #define SKYHIGH_STATUS_ECC_3TO6_BITFLIPS (2 << 4) 16*1a50e361STakahiro Kuwano #define SKYHIGH_STATUS_ECC_UNCOR_ERROR (3 << 4) 17*1a50e361STakahiro Kuwano #define SKYHIGH_CONFIG_PROTECT_EN BIT(1) 18*1a50e361STakahiro Kuwano 19*1a50e361STakahiro Kuwano static SPINAND_OP_VARIANTS(read_cache_variants, 20*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), 21*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 22*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), 23*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 24*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 25*1a50e361STakahiro Kuwano SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 26*1a50e361STakahiro Kuwano 27*1a50e361STakahiro Kuwano static SPINAND_OP_VARIANTS(write_cache_variants, 28*1a50e361STakahiro Kuwano SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 29*1a50e361STakahiro Kuwano SPINAND_PROG_LOAD(true, 0, NULL, 0)); 30*1a50e361STakahiro Kuwano 31*1a50e361STakahiro Kuwano static SPINAND_OP_VARIANTS(update_cache_variants, 32*1a50e361STakahiro Kuwano SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 33*1a50e361STakahiro Kuwano SPINAND_PROG_LOAD(false, 0, NULL, 0)); 34*1a50e361STakahiro Kuwano 35*1a50e361STakahiro Kuwano static int skyhigh_spinand_ooblayout_ecc(struct mtd_info *mtd, int section, 36*1a50e361STakahiro Kuwano struct mtd_oob_region *region) 37*1a50e361STakahiro Kuwano { 38*1a50e361STakahiro Kuwano /* ECC bytes are stored in hidden area. */ 39*1a50e361STakahiro Kuwano return -ERANGE; 40*1a50e361STakahiro Kuwano } 41*1a50e361STakahiro Kuwano 42*1a50e361STakahiro Kuwano static int skyhigh_spinand_ooblayout_free(struct mtd_info *mtd, int section, 43*1a50e361STakahiro Kuwano struct mtd_oob_region *region) 44*1a50e361STakahiro Kuwano { 45*1a50e361STakahiro Kuwano if (section) 46*1a50e361STakahiro Kuwano return -ERANGE; 47*1a50e361STakahiro Kuwano 48*1a50e361STakahiro Kuwano /* ECC bytes are stored in hidden area. Reserve 2 bytes for the BBM. */ 49*1a50e361STakahiro Kuwano region->offset = 2; 50*1a50e361STakahiro Kuwano region->length = mtd->oobsize - 2; 51*1a50e361STakahiro Kuwano 52*1a50e361STakahiro Kuwano return 0; 53*1a50e361STakahiro Kuwano } 54*1a50e361STakahiro Kuwano 55*1a50e361STakahiro Kuwano static const struct mtd_ooblayout_ops skyhigh_spinand_ooblayout = { 56*1a50e361STakahiro Kuwano .ecc = skyhigh_spinand_ooblayout_ecc, 57*1a50e361STakahiro Kuwano .free = skyhigh_spinand_ooblayout_free, 58*1a50e361STakahiro Kuwano }; 59*1a50e361STakahiro Kuwano 60*1a50e361STakahiro Kuwano static int skyhigh_spinand_ecc_get_status(struct spinand_device *spinand, 61*1a50e361STakahiro Kuwano u8 status) 62*1a50e361STakahiro Kuwano { 63*1a50e361STakahiro Kuwano switch (status & STATUS_ECC_MASK) { 64*1a50e361STakahiro Kuwano case STATUS_ECC_NO_BITFLIPS: 65*1a50e361STakahiro Kuwano return 0; 66*1a50e361STakahiro Kuwano 67*1a50e361STakahiro Kuwano case SKYHIGH_STATUS_ECC_UNCOR_ERROR: 68*1a50e361STakahiro Kuwano return -EBADMSG; 69*1a50e361STakahiro Kuwano 70*1a50e361STakahiro Kuwano case SKYHIGH_STATUS_ECC_1TO2_BITFLIPS: 71*1a50e361STakahiro Kuwano return 2; 72*1a50e361STakahiro Kuwano 73*1a50e361STakahiro Kuwano case SKYHIGH_STATUS_ECC_3TO6_BITFLIPS: 74*1a50e361STakahiro Kuwano return 6; 75*1a50e361STakahiro Kuwano 76*1a50e361STakahiro Kuwano default: 77*1a50e361STakahiro Kuwano break; 78*1a50e361STakahiro Kuwano } 79*1a50e361STakahiro Kuwano 80*1a50e361STakahiro Kuwano return -EINVAL; 81*1a50e361STakahiro Kuwano } 82*1a50e361STakahiro Kuwano 83*1a50e361STakahiro Kuwano static const struct spinand_info skyhigh_spinand_table[] = { 84*1a50e361STakahiro Kuwano SPINAND_INFO("S35ML01G301", 85*1a50e361STakahiro Kuwano SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), 86*1a50e361STakahiro Kuwano NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 87*1a50e361STakahiro Kuwano NAND_ECCREQ(6, 32), 88*1a50e361STakahiro Kuwano SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 89*1a50e361STakahiro Kuwano &write_cache_variants, 90*1a50e361STakahiro Kuwano &update_cache_variants), 91*1a50e361STakahiro Kuwano SPINAND_NO_RAW_ACCESS, 92*1a50e361STakahiro Kuwano SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, 93*1a50e361STakahiro Kuwano skyhigh_spinand_ecc_get_status)), 94*1a50e361STakahiro Kuwano SPINAND_INFO("S35ML01G300", 95*1a50e361STakahiro Kuwano SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), 96*1a50e361STakahiro Kuwano NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 97*1a50e361STakahiro Kuwano NAND_ECCREQ(6, 32), 98*1a50e361STakahiro Kuwano SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 99*1a50e361STakahiro Kuwano &write_cache_variants, 100*1a50e361STakahiro Kuwano &update_cache_variants), 101*1a50e361STakahiro Kuwano SPINAND_NO_RAW_ACCESS, 102*1a50e361STakahiro Kuwano SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, 103*1a50e361STakahiro Kuwano skyhigh_spinand_ecc_get_status)), 104*1a50e361STakahiro Kuwano SPINAND_INFO("S35ML02G300", 105*1a50e361STakahiro Kuwano SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), 106*1a50e361STakahiro Kuwano NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), 107*1a50e361STakahiro Kuwano NAND_ECCREQ(6, 32), 108*1a50e361STakahiro Kuwano SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 109*1a50e361STakahiro Kuwano &write_cache_variants, 110*1a50e361STakahiro Kuwano &update_cache_variants), 111*1a50e361STakahiro Kuwano SPINAND_NO_RAW_ACCESS, 112*1a50e361STakahiro Kuwano SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, 113*1a50e361STakahiro Kuwano skyhigh_spinand_ecc_get_status)), 114*1a50e361STakahiro Kuwano SPINAND_INFO("S35ML04G300", 115*1a50e361STakahiro Kuwano SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), 116*1a50e361STakahiro Kuwano NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 2, 1, 1), 117*1a50e361STakahiro Kuwano NAND_ECCREQ(6, 32), 118*1a50e361STakahiro Kuwano SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 119*1a50e361STakahiro Kuwano &write_cache_variants, 120*1a50e361STakahiro Kuwano &update_cache_variants), 121*1a50e361STakahiro Kuwano SPINAND_NO_RAW_ACCESS, 122*1a50e361STakahiro Kuwano SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, 123*1a50e361STakahiro Kuwano skyhigh_spinand_ecc_get_status)), 124*1a50e361STakahiro Kuwano }; 125*1a50e361STakahiro Kuwano 126*1a50e361STakahiro Kuwano static int skyhigh_spinand_init(struct spinand_device *spinand) 127*1a50e361STakahiro Kuwano { 128*1a50e361STakahiro Kuwano /* 129*1a50e361STakahiro Kuwano * Config_Protect_En (bit 1 in Block Lock register) must be set to 1 130*1a50e361STakahiro Kuwano * before writing other bits. Do it here before core unlocks all blocks 131*1a50e361STakahiro Kuwano * by writing block protection bits. 132*1a50e361STakahiro Kuwano */ 133*1a50e361STakahiro Kuwano return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, 134*1a50e361STakahiro Kuwano SKYHIGH_CONFIG_PROTECT_EN); 135*1a50e361STakahiro Kuwano } 136*1a50e361STakahiro Kuwano 137*1a50e361STakahiro Kuwano static const struct spinand_manufacturer_ops skyhigh_spinand_manuf_ops = { 138*1a50e361STakahiro Kuwano .init = skyhigh_spinand_init, 139*1a50e361STakahiro Kuwano }; 140*1a50e361STakahiro Kuwano 141*1a50e361STakahiro Kuwano const struct spinand_manufacturer skyhigh_spinand_manufacturer = { 142*1a50e361STakahiro Kuwano .id = SPINAND_MFR_SKYHIGH, 143*1a50e361STakahiro Kuwano .name = "SkyHigh", 144*1a50e361STakahiro Kuwano .chips = skyhigh_spinand_table, 145*1a50e361STakahiro Kuwano .nchips = ARRAY_SIZE(skyhigh_spinand_table), 146*1a50e361STakahiro Kuwano .ops = &skyhigh_spinand_manuf_ops, 147*1a50e361STakahiro Kuwano }; 148