1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd. 4 * 5 * Author: Dingqiang Lin <jon.lin@rock-chips.com> 6 */ 7 8 #include <linux/device.h> 9 #include <linux/kernel.h> 10 #include <linux/mtd/spinand.h> 11 12 #define FM25S01BI3_STATUS_ECC_MASK (7 << 4) 13 #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4) 14 #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4) 15 #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4) 16 #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4) 17 #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4) 18 19 #define SPINAND_MFR_FMSH 0xA1 20 21 static SPINAND_OP_VARIANTS(read_cache_variants, 22 SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), 23 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), 24 SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), 25 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), 26 SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), 27 SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); 28 29 static SPINAND_OP_VARIANTS(write_cache_variants, 30 SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), 31 SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); 32 33 static SPINAND_OP_VARIANTS(update_cache_variants, 34 SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), 35 SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); 36 37 static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section, 38 struct mtd_oob_region *region) 39 { 40 return -ERANGE; 41 } 42 43 static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section, 44 struct mtd_oob_region *region) 45 { 46 if (section) 47 return -ERANGE; 48 49 region->offset = 2; 50 region->length = 62; 51 52 return 0; 53 } 54 55 static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand, 56 u8 status) 57 { 58 switch (status & FM25S01BI3_STATUS_ECC_MASK) { 59 case FM25S01BI3_STATUS_ECC_NO_BITFLIPS: 60 return 0; 61 62 case FM25S01BI3_STATUS_ECC_UNCOR_ERROR: 63 return -EBADMSG; 64 65 case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS: 66 return 3; 67 68 case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS: 69 return 6; 70 71 case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS: 72 return 8; 73 74 default: 75 break; 76 } 77 78 return -EINVAL; 79 } 80 81 static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section, 82 struct mtd_oob_region *region) 83 { 84 if (section) 85 return -ERANGE; 86 87 region->offset = 64; 88 region->length = 64; 89 90 return 0; 91 } 92 93 static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section, 94 struct mtd_oob_region *region) 95 { 96 if (section > 3) 97 return -ERANGE; 98 99 region->offset = (16 * section) + 4; 100 region->length = 12; 101 102 return 0; 103 } 104 105 static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { 106 .ecc = fm25s01a_ooblayout_ecc, 107 .free = fm25s01a_ooblayout_free, 108 }; 109 110 static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = { 111 .ecc = fm25s01bi3_ooblayout_ecc, 112 .free = fm25s01bi3_ooblayout_free, 113 }; 114 115 static const struct spinand_info fmsh_spinand_table[] = { 116 SPINAND_INFO("FM25S01A", 117 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), 118 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 119 NAND_ECCREQ(1, 512), 120 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 121 &write_cache_variants, 122 &update_cache_variants), 123 0, 124 SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), 125 SPINAND_INFO("FM25S01BI3", 126 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4), 127 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 128 NAND_ECCREQ(8, 512), 129 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 130 &write_cache_variants, 131 &update_cache_variants), 132 SPINAND_HAS_QE_BIT, 133 SPINAND_ECCINFO(&fm25s01bi3_ooblayout, 134 fm25s01bi3_ecc_get_status)), 135 }; 136 137 static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = { 138 }; 139 140 const struct spinand_manufacturer fmsh_spinand_manufacturer = { 141 .id = SPINAND_MFR_FMSH, 142 .name = "Fudan Micro", 143 .chips = fmsh_spinand_table, 144 .nchips = ARRAY_SIZE(fmsh_spinand_table), 145 .ops = &fmsh_spinand_manuf_ops, 146 }; 147