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