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