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