1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2020 Rockchip Electronics Co., Ltd 4 * 5 * Authors: 6 * Dingqiang Lin <jon.lin@rock-chips.com> 7 */ 8 9 #ifndef __UBOOT__ 10 #include <linux/device.h> 11 #include <linux/kernel.h> 12 #endif 13 #include <linux/mtd/spinand.h> 14 15 #define SPINAND_MFR_DOSILICON 0xE5 16 17 #define DOSICON_STATUS_ECC_MASK GENMASK(7, 4) 18 #define DOSICON_STATUS_ECC_NO_BITFLIPS (0 << 4) 19 #define DOSICON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) 20 #define DOSICON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) 21 #define DOSICON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) 22 23 static SPINAND_OP_VARIANTS(read_cache_variants, 24 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 25 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 26 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 27 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 28 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 29 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 30 31 static SPINAND_OP_VARIANTS(write_cache_variants, 32 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 33 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 34 35 static SPINAND_OP_VARIANTS(update_cache_variants, 36 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 37 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 38 39 static int ds35xxga_ooblayout_ecc(struct mtd_info *mtd, int section, 40 struct mtd_oob_region *region) 41 { 42 if (section > 3) 43 return -ERANGE; 44 45 region->offset = (16 * section) + 8; 46 region->length = 8; 47 48 return 0; 49 } 50 51 static int ds35xxga_ooblayout_free(struct mtd_info *mtd, int section, 52 struct mtd_oob_region *region) 53 { 54 if (section > 3) 55 return -ERANGE; 56 57 region->offset = (16 * section) + 2; 58 region->length = 6; 59 60 return 0; 61 } 62 63 static const struct mtd_ooblayout_ops ds35xxga_ooblayout = { 64 .ecc = ds35xxga_ooblayout_ecc, 65 .rfree = ds35xxga_ooblayout_free, 66 }; 67 68 static int ds35xxgb_ooblayout_ecc(struct mtd_info *mtd, int section, 69 struct mtd_oob_region *region) 70 { 71 if (section) 72 return -ERANGE; 73 74 region->offset = 64; 75 region->length = 64; 76 77 return 0; 78 } 79 80 static int ds35xxgb_ooblayout_free(struct mtd_info *mtd, int section, 81 struct mtd_oob_region *region) 82 { 83 if (section) 84 return -ERANGE; 85 86 /* Reserve 1 bytes for the BBM. */ 87 region->offset = 1; 88 region->length = 63; 89 90 return 0; 91 } 92 93 static const struct mtd_ooblayout_ops ds35xxgb_ooblayout = { 94 .ecc = ds35xxgb_ooblayout_ecc, 95 .rfree = ds35xxgb_ooblayout_free, 96 }; 97 98 static int ds35xxgb_ecc_get_status(struct spinand_device *spinand, 99 u8 status) 100 { 101 switch (status & DOSICON_STATUS_ECC_MASK) { 102 case STATUS_ECC_NO_BITFLIPS: 103 return 0; 104 105 case STATUS_ECC_UNCOR_ERROR: 106 return -EBADMSG; 107 108 case DOSICON_STATUS_ECC_1TO3_BITFLIPS: 109 return 3; 110 111 case DOSICON_STATUS_ECC_4TO6_BITFLIPS: 112 return 6; 113 114 case DOSICON_STATUS_ECC_7TO8_BITFLIPS: 115 return 8; 116 117 default: 118 break; 119 } 120 121 return -EINVAL; 122 } 123 124 static const struct spinand_info dosilicon_spinand_table[] = { 125 SPINAND_INFO("DS35X1GA", 0x71, 126 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 127 NAND_ECCREQ(4, 512), 128 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 129 &write_cache_variants, 130 &update_cache_variants), 131 SPINAND_HAS_QE_BIT, 132 SPINAND_ECCINFO(&ds35xxga_ooblayout, NULL)), 133 SPINAND_INFO("DS35Q2GA", 0x72, 134 NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), 135 NAND_ECCREQ(4, 512), 136 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 137 &write_cache_variants, 138 &update_cache_variants), 139 SPINAND_HAS_QE_BIT, 140 SPINAND_ECCINFO(&ds35xxga_ooblayout, NULL)), 141 SPINAND_INFO("DS35M1GA", 0x21, 142 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 143 NAND_ECCREQ(4, 512), 144 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 145 &write_cache_variants, 146 &update_cache_variants), 147 SPINAND_HAS_QE_BIT, 148 SPINAND_ECCINFO(&ds35xxga_ooblayout, NULL)), 149 SPINAND_INFO("DS35Q2GB", 0xF2, 150 NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), 151 NAND_ECCREQ(8, 512), 152 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 153 &write_cache_variants, 154 &update_cache_variants), 155 SPINAND_HAS_QE_BIT, 156 SPINAND_ECCINFO(&ds35xxgb_ooblayout, 157 ds35xxgb_ecc_get_status)), 158 }; 159 160 /** 161 * dosilicon_spinand_detect - initialize device related part in spinand_device 162 * struct if it is a dosilicon device. 163 * @spinand: SPI NAND device structure 164 */ 165 static int dosilicon_spinand_detect(struct spinand_device *spinand) 166 { 167 u8 *id = spinand->id.data; 168 int ret; 169 170 /* 171 * dosilicon SPI NAND read ID need a dummy byte, 172 * so the first byte in raw_id is dummy. 173 */ 174 if (id[1] != SPINAND_MFR_DOSILICON) 175 return 0; 176 177 ret = spinand_match_and_init(spinand, dosilicon_spinand_table, 178 ARRAY_SIZE(dosilicon_spinand_table), 179 id[2]); 180 if (ret) 181 return ret; 182 183 return 1; 184 } 185 186 static int dosilicon_spinand_init(struct spinand_device *spinand) 187 { 188 return 0; 189 } 190 191 static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = { 192 .detect = dosilicon_spinand_detect, 193 .init = dosilicon_spinand_init, 194 }; 195 196 const struct spinand_manufacturer dosilicon_spinand_manufacturer = { 197 .id = SPINAND_MFR_DOSILICON, 198 .name = "dosilicon", 199 .ops = &dosilicon_spinand_manuf_ops, 200 }; 201