linux/drivers/mtd/nand/spi/gigadevice.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Author:
   4 *      Chuanhong Guo <gch981213@gmail.com>
   5 */
   6
   7#include <linux/device.h>
   8#include <linux/kernel.h>
   9#include <linux/mtd/spinand.h>
  10
  11#define SPINAND_MFR_GIGADEVICE                  0xC8
  12
  13#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS      (1 << 4)
  14#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS        (3 << 4)
  15
  16#define GD5FXGQ4UEXXG_REG_STATUS2               0xf0
  17
  18#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK          (7 << 4)
  19#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS   (0 << 4)
  20#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS  (1 << 4)
  21#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR   (7 << 4)
  22
  23static 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
  31static SPINAND_OP_VARIANTS(read_cache_variants_f,
  32                SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
  33                SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
  34                SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
  35                SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0),
  36                SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
  37                SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
  38
  39static SPINAND_OP_VARIANTS(write_cache_variants,
  40                SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
  41                SPINAND_PROG_LOAD(true, 0, NULL, 0));
  42
  43static SPINAND_OP_VARIANTS(update_cache_variants,
  44                SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
  45                SPINAND_PROG_LOAD(false, 0, NULL, 0));
  46
  47static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
  48                                  struct mtd_oob_region *region)
  49{
  50        if (section > 3)
  51                return -ERANGE;
  52
  53        region->offset = (16 * section) + 8;
  54        region->length = 8;
  55
  56        return 0;
  57}
  58
  59static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
  60                                   struct mtd_oob_region *region)
  61{
  62        if (section > 3)
  63                return -ERANGE;
  64
  65        if (section) {
  66                region->offset = 16 * section;
  67                region->length = 8;
  68        } else {
  69                /* section 0 has one byte reserved for bad block mark */
  70                region->offset = 1;
  71                region->length = 7;
  72        }
  73        return 0;
  74}
  75
  76static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
  77        .ecc = gd5fxgq4xa_ooblayout_ecc,
  78        .free = gd5fxgq4xa_ooblayout_free,
  79};
  80
  81static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
  82                                         u8 status)
  83{
  84        switch (status & STATUS_ECC_MASK) {
  85        case STATUS_ECC_NO_BITFLIPS:
  86                return 0;
  87
  88        case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
  89                /* 1-7 bits are flipped. return the maximum. */
  90                return 7;
  91
  92        case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
  93                return 8;
  94
  95        case STATUS_ECC_UNCOR_ERROR:
  96                return -EBADMSG;
  97
  98        default:
  99                break;
 100        }
 101
 102        return -EINVAL;
 103}
 104
 105static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
 106                                       struct mtd_oob_region *region)
 107{
 108        if (section)
 109                return -ERANGE;
 110
 111        region->offset = 64;
 112        region->length = 64;
 113
 114        return 0;
 115}
 116
 117static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
 118                                        struct mtd_oob_region *region)
 119{
 120        if (section)
 121                return -ERANGE;
 122
 123        /* Reserve 1 bytes for the BBM. */
 124        region->offset = 1;
 125        region->length = 63;
 126
 127        return 0;
 128}
 129
 130static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = {
 131        .ecc = gd5fxgq4_variant2_ooblayout_ecc,
 132        .free = gd5fxgq4_variant2_ooblayout_free,
 133};
 134
 135static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
 136                                        u8 status)
 137{
 138        u8 status2;
 139        struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
 140                                                      &status2);
 141        int ret;
 142
 143        switch (status & STATUS_ECC_MASK) {
 144        case STATUS_ECC_NO_BITFLIPS:
 145                return 0;
 146
 147        case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
 148                /*
 149                 * Read status2 register to determine a more fine grained
 150                 * bit error status
 151                 */
 152                ret = spi_mem_exec_op(spinand->spimem, &op);
 153                if (ret)
 154                        return ret;
 155
 156                /*
 157                 * 4 ... 7 bits are flipped (1..4 can't be detected, so
 158                 * report the maximum of 4 in this case
 159                 */
 160                /* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
 161                return ((status & STATUS_ECC_MASK) >> 2) |
 162                        ((status2 & STATUS_ECC_MASK) >> 4);
 163
 164        case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
 165                return 8;
 166
 167        case STATUS_ECC_UNCOR_ERROR:
 168                return -EBADMSG;
 169
 170        default:
 171                break;
 172        }
 173
 174        return -EINVAL;
 175}
 176
 177static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
 178                                        u8 status)
 179{
 180        switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
 181        case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
 182                return 0;
 183
 184        case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
 185                return 3;
 186
 187        case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
 188                return -EBADMSG;
 189
 190        default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
 191                return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
 192        }
 193
 194        return -EINVAL;
 195}
 196
 197static const struct spinand_info gigadevice_spinand_table[] = {
 198        SPINAND_INFO("GD5F1GQ4xA",
 199                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1),
 200                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
 201                     NAND_ECCREQ(8, 512),
 202                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 203                                              &write_cache_variants,
 204                                              &update_cache_variants),
 205                     0,
 206                     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
 207                                     gd5fxgq4xa_ecc_get_status)),
 208        SPINAND_INFO("GD5F2GQ4xA",
 209                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2),
 210                     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
 211                     NAND_ECCREQ(8, 512),
 212                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 213                                              &write_cache_variants,
 214                                              &update_cache_variants),
 215                     0,
 216                     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
 217                                     gd5fxgq4xa_ecc_get_status)),
 218        SPINAND_INFO("GD5F4GQ4xA",
 219                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4),
 220                     NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1),
 221                     NAND_ECCREQ(8, 512),
 222                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 223                                              &write_cache_variants,
 224                                              &update_cache_variants),
 225                     0,
 226                     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
 227                                     gd5fxgq4xa_ecc_get_status)),
 228        SPINAND_INFO("GD5F1GQ4UExxG",
 229                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1),
 230                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 231                     NAND_ECCREQ(8, 512),
 232                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 233                                              &write_cache_variants,
 234                                              &update_cache_variants),
 235                     0,
 236                     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
 237                                     gd5fxgq4uexxg_ecc_get_status)),
 238        SPINAND_INFO("GD5F1GQ4UFxxG",
 239                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
 240                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 241                     NAND_ECCREQ(8, 512),
 242                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
 243                                              &write_cache_variants,
 244                                              &update_cache_variants),
 245                     0,
 246                     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
 247                                     gd5fxgq4ufxxg_ecc_get_status)),
 248};
 249
 250static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
 251};
 252
 253const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
 254        .id = SPINAND_MFR_GIGADEVICE,
 255        .name = "GigaDevice",
 256        .chips = gigadevice_spinand_table,
 257        .nchips = ARRAY_SIZE(gigadevice_spinand_table),
 258        .ops = &gigadevice_spinand_manuf_ops,
 259};
 260