linux/drivers/mtd/nand/spi/toshiba.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2018 exceet electronics GmbH
   4 * Copyright (c) 2018 Kontron Electronics GmbH
   5 *
   6 * Author: Frieder Schrempf <frieder.schrempf@kontron.de>
   7 */
   8
   9#include <linux/device.h>
  10#include <linux/kernel.h>
  11#include <linux/mtd/spinand.h>
  12
  13#define SPINAND_MFR_TOSHIBA             0x98
  14#define TOSH_STATUS_ECC_HAS_BITFLIPS_T  (3 << 4)
  15
  16static SPINAND_OP_VARIANTS(read_cache_variants,
  17                SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
  18                SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
  19                SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
  20                SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
  21
  22static SPINAND_OP_VARIANTS(write_cache_variants,
  23                SPINAND_PROG_LOAD(true, 0, NULL, 0));
  24
  25static SPINAND_OP_VARIANTS(update_cache_variants,
  26                SPINAND_PROG_LOAD(false, 0, NULL, 0));
  27
  28static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section,
  29                                     struct mtd_oob_region *region)
  30{
  31        if (section > 0)
  32                return -ERANGE;
  33
  34        region->offset = mtd->oobsize / 2;
  35        region->length = mtd->oobsize / 2;
  36
  37        return 0;
  38}
  39
  40static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section,
  41                                      struct mtd_oob_region *region)
  42{
  43        if (section > 0)
  44                return -ERANGE;
  45
  46        /* 2 bytes reserved for BBM */
  47        region->offset = 2;
  48        region->length = (mtd->oobsize / 2) - 2;
  49
  50        return 0;
  51}
  52
  53static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = {
  54        .ecc = tc58cxgxsx_ooblayout_ecc,
  55        .free = tc58cxgxsx_ooblayout_free,
  56};
  57
  58static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand,
  59                                      u8 status)
  60{
  61        struct nand_device *nand = spinand_to_nand(spinand);
  62        u8 mbf = 0;
  63        struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
  64
  65        switch (status & STATUS_ECC_MASK) {
  66        case STATUS_ECC_NO_BITFLIPS:
  67                return 0;
  68
  69        case STATUS_ECC_UNCOR_ERROR:
  70                return -EBADMSG;
  71
  72        case STATUS_ECC_HAS_BITFLIPS:
  73        case TOSH_STATUS_ECC_HAS_BITFLIPS_T:
  74                /*
  75                 * Let's try to retrieve the real maximum number of bitflips
  76                 * in order to avoid forcing the wear-leveling layer to move
  77                 * data around if it's not necessary.
  78                 */
  79                if (spi_mem_exec_op(spinand->spimem, &op))
  80                        return nand->eccreq.strength;
  81
  82                mbf >>= 4;
  83
  84                if (WARN_ON(mbf > nand->eccreq.strength || !mbf))
  85                        return nand->eccreq.strength;
  86
  87                return mbf;
  88
  89        default:
  90                break;
  91        }
  92
  93        return -EINVAL;
  94}
  95
  96static const struct spinand_info toshiba_spinand_table[] = {
  97        /* 3.3V 1Gb */
  98        SPINAND_INFO("TC58CVG0S3", 0xC2,
  99                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 100                     NAND_ECCREQ(8, 512),
 101                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 102                                              &write_cache_variants,
 103                                              &update_cache_variants),
 104                     0,
 105                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 106                                     tc58cxgxsx_ecc_get_status)),
 107        /* 3.3V 2Gb */
 108        SPINAND_INFO("TC58CVG1S3", 0xCB,
 109                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
 110                     NAND_ECCREQ(8, 512),
 111                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 112                                              &write_cache_variants,
 113                                              &update_cache_variants),
 114                     0,
 115                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 116                                     tc58cxgxsx_ecc_get_status)),
 117        /* 3.3V 4Gb */
 118        SPINAND_INFO("TC58CVG2S0", 0xCD,
 119                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
 120                     NAND_ECCREQ(8, 512),
 121                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 122                                              &write_cache_variants,
 123                                              &update_cache_variants),
 124                     0,
 125                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 126                                     tc58cxgxsx_ecc_get_status)),
 127        /* 1.8V 1Gb */
 128        SPINAND_INFO("TC58CYG0S3", 0xB2,
 129                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 130                     NAND_ECCREQ(8, 512),
 131                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 132                                              &write_cache_variants,
 133                                              &update_cache_variants),
 134                     0,
 135                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 136                                     tc58cxgxsx_ecc_get_status)),
 137        /* 1.8V 2Gb */
 138        SPINAND_INFO("TC58CYG1S3", 0xBB,
 139                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
 140                     NAND_ECCREQ(8, 512),
 141                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 142                                              &write_cache_variants,
 143                                              &update_cache_variants),
 144                     0,
 145                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 146                                     tc58cxgxsx_ecc_get_status)),
 147        /* 1.8V 4Gb */
 148        SPINAND_INFO("TC58CYG2S0", 0xBD,
 149                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
 150                     NAND_ECCREQ(8, 512),
 151                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 152                                              &write_cache_variants,
 153                                              &update_cache_variants),
 154                     0,
 155                     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
 156                                     tc58cxgxsx_ecc_get_status)),
 157};
 158
 159static int toshiba_spinand_detect(struct spinand_device *spinand)
 160{
 161        u8 *id = spinand->id.data;
 162        int ret;
 163
 164        /*
 165         * Toshiba SPI NAND read ID needs a dummy byte,
 166         * so the first byte in id is garbage.
 167         */
 168        if (id[1] != SPINAND_MFR_TOSHIBA)
 169                return 0;
 170
 171        ret = spinand_match_and_init(spinand, toshiba_spinand_table,
 172                                     ARRAY_SIZE(toshiba_spinand_table),
 173                                     id[2]);
 174        if (ret)
 175                return ret;
 176
 177        return 1;
 178}
 179
 180static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
 181        .detect = toshiba_spinand_detect,
 182};
 183
 184const struct spinand_manufacturer toshiba_spinand_manufacturer = {
 185        .id = SPINAND_MFR_TOSHIBA,
 186        .name = "Toshiba",
 187        .ops = &toshiba_spinand_manuf_ops,
 188};
 189