1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Stefan Roese <sr@denx.de> 4 * 5 * Derived from drivers/mtd/nand/spi/micron.c 6 * Copyright (c) 2016-2017 Micron Technology, Inc. 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_GIGADEVICE 0xc8 16 17 #define GIGADEVICE_STATUS_ECC_MASK GENMASK(5, 4) 18 #define GIGADEVICE_STATUS_ECC_NO_BITFLIPS (0 << 4) 19 #define GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS (1 << 4) 20 #define GIGADEVICE_STATUS_ECC_8_BITFLIPS (3 << 4) 21 22 static SPINAND_OP_VARIANTS(read_cache_variants, 23 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 24 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 25 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 26 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 27 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 28 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 29 30 static SPINAND_OP_VARIANTS(write_cache_variants, 31 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 32 SPINAND_PROG_LOAD(true, 0, NULL, 0)); 33 34 static SPINAND_OP_VARIANTS(update_cache_variants, 35 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 36 SPINAND_PROG_LOAD(false, 0, NULL, 0)); 37 38 static int gd5f1gq4u_ooblayout_ecc(struct mtd_info *mtd, int section, 39 struct mtd_oob_region *region) 40 { 41 if (section) 42 return -ERANGE; 43 44 region->offset = 64; 45 region->length = 64; 46 47 return 0; 48 } 49 50 static int gd5f1gq4u_ooblayout_free(struct mtd_info *mtd, int section, 51 struct mtd_oob_region *region) 52 { 53 if (section) 54 return -ERANGE; 55 56 /* Reserve 2 bytes for the BBM. */ 57 region->offset = 2; 58 region->length = 62; 59 60 return 0; 61 } 62 63 static const struct mtd_ooblayout_ops gd5f1gq4u_ooblayout = { 64 .ecc = gd5f1gq4u_ooblayout_ecc, 65 .free = gd5f1gq4u_ooblayout_free, 66 }; 67 68 static int gd5f1gq4u_ecc_get_status(struct spinand_device *spinand, 69 u8 status) 70 { 71 if (status) 72 debug("%s (%d): status=%02x\n", __func__, __LINE__, status); 73 74 switch (status & GIGADEVICE_STATUS_ECC_MASK) { 75 case STATUS_ECC_NO_BITFLIPS: 76 return 0; 77 78 case GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS: 79 return 7; 80 81 case GIGADEVICE_STATUS_ECC_8_BITFLIPS: 82 return 8; 83 84 case STATUS_ECC_UNCOR_ERROR: 85 return -EBADMSG; 86 87 default: 88 break; 89 } 90 91 return -EINVAL; 92 } 93 94 static const struct spinand_info gigadevice_spinand_table[] = { 95 SPINAND_INFO("GD5F1GQ4UC", 0xd1, 96 NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), 97 NAND_ECCREQ(8, 2048), 98 SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 99 &write_cache_variants, 100 &update_cache_variants), 101 0, 102 SPINAND_ECCINFO(&gd5f1gq4u_ooblayout, 103 gd5f1gq4u_ecc_get_status)), 104 }; 105 106 static int gigadevice_spinand_detect(struct spinand_device *spinand) 107 { 108 u8 *id = spinand->id.data; 109 int ret; 110 111 /* 112 * Gigadevice SPI NAND read ID need a dummy byte, 113 * so the first byte in raw_id is dummy. 114 */ 115 if (id[1] != SPINAND_MFR_GIGADEVICE) 116 return 0; 117 118 ret = spinand_match_and_init(spinand, gigadevice_spinand_table, 119 ARRAY_SIZE(gigadevice_spinand_table), 120 id[2]); 121 if (ret) 122 return ret; 123 124 return 1; 125 } 126 127 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { 128 .detect = gigadevice_spinand_detect, 129 }; 130 131 const struct spinand_manufacturer gigadevice_spinand_manufacturer = { 132 .id = SPINAND_MFR_GIGADEVICE, 133 .name = "GigaDevice", 134 .ops = &gigadevice_spinand_manuf_ops, 135 }; 136