linux/drivers/mtd/nand/raw/nand_samsung.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2017 Free Electrons
   4 * Copyright (C) 2017 NextThing Co
   5 *
   6 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
   7 */
   8
   9#include "internals.h"
  10
  11static void samsung_nand_decode_id(struct nand_chip *chip)
  12{
  13        struct nand_device *base = &chip->base;
  14        struct nand_ecc_props requirements = {};
  15        struct mtd_info *mtd = nand_to_mtd(chip);
  16        struct nand_memory_organization *memorg;
  17
  18        memorg = nanddev_get_memorg(&chip->base);
  19
  20        /* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
  21        if (chip->id.len == 6 && !nand_is_slc(chip) &&
  22            chip->id.data[5] != 0x00) {
  23                u8 extid = chip->id.data[3];
  24
  25                /* Get pagesize */
  26                memorg->pagesize = 2048 << (extid & 0x03);
  27                mtd->writesize = memorg->pagesize;
  28
  29                extid >>= 2;
  30
  31                /* Get oobsize */
  32                switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
  33                case 1:
  34                        memorg->oobsize = 128;
  35                        break;
  36                case 2:
  37                        memorg->oobsize = 218;
  38                        break;
  39                case 3:
  40                        memorg->oobsize = 400;
  41                        break;
  42                case 4:
  43                        memorg->oobsize = 436;
  44                        break;
  45                case 5:
  46                        memorg->oobsize = 512;
  47                        break;
  48                case 6:
  49                        memorg->oobsize = 640;
  50                        break;
  51                default:
  52                        /*
  53                         * We should never reach this case, but if that
  54                         * happens, this probably means Samsung decided to use
  55                         * a different extended ID format, and we should find
  56                         * a way to support it.
  57                         */
  58                        WARN(1, "Invalid OOB size value");
  59                        break;
  60                }
  61
  62                mtd->oobsize = memorg->oobsize;
  63
  64                /* Get blocksize */
  65                extid >>= 2;
  66                memorg->pages_per_eraseblock = (128 * 1024) <<
  67                                               (((extid >> 1) & 0x04) |
  68                                                (extid & 0x03)) /
  69                                               memorg->pagesize;
  70                mtd->erasesize = (128 * 1024) <<
  71                                 (((extid >> 1) & 0x04) | (extid & 0x03));
  72
  73                /* Extract ECC requirements from 5th id byte*/
  74                extid = (chip->id.data[4] >> 4) & 0x07;
  75                if (extid < 5) {
  76                        requirements.step_size = 512;
  77                        requirements.strength = 1 << extid;
  78                } else {
  79                        requirements.step_size = 1024;
  80                        switch (extid) {
  81                        case 5:
  82                                requirements.strength = 24;
  83                                break;
  84                        case 6:
  85                                requirements.strength = 40;
  86                                break;
  87                        case 7:
  88                                requirements.strength = 60;
  89                                break;
  90                        default:
  91                                WARN(1, "Could not decode ECC info");
  92                                requirements.step_size = 0;
  93                        }
  94                }
  95        } else {
  96                nand_decode_ext_id(chip);
  97
  98                if (nand_is_slc(chip)) {
  99                        switch (chip->id.data[1]) {
 100                        /* K9F4G08U0D-S[I|C]B0(T00) */
 101                        case 0xDC:
 102                                requirements.step_size = 512;
 103                                requirements.strength = 1;
 104                                break;
 105
 106                        /* K9F1G08U0E 21nm chips do not support subpage write */
 107                        case 0xF1:
 108                                if (chip->id.len > 4 &&
 109                                    (chip->id.data[4] & GENMASK(1, 0)) == 0x1)
 110                                        chip->options |= NAND_NO_SUBPAGE_WRITE;
 111                                break;
 112                        default:
 113                                break;
 114                        }
 115                }
 116        }
 117
 118        nanddev_set_ecc_requirements(base, &requirements);
 119}
 120
 121static int samsung_nand_init(struct nand_chip *chip)
 122{
 123        struct mtd_info *mtd = nand_to_mtd(chip);
 124
 125        if (mtd->writesize > 512)
 126                chip->options |= NAND_SAMSUNG_LP_OPTIONS;
 127
 128        if (!nand_is_slc(chip))
 129                chip->options |= NAND_BBM_LASTPAGE;
 130        else
 131                chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
 132
 133        return 0;
 134}
 135
 136const struct nand_manufacturer_ops samsung_nand_manuf_ops = {
 137        .detect = samsung_nand_decode_id,
 138        .init = samsung_nand_init,
 139};
 140