linux/drivers/mtd/onenand/onenand_bbt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  linux/drivers/mtd/onenand/onenand_bbt.c
   4 *
   5 *  Bad Block Table support for the OneNAND driver
   6 *
   7 *  Copyright(c) 2005 Samsung Electronics
   8 *  Kyungmin Park <kyungmin.park@samsung.com>
   9 *
  10 *  Derived from nand_bbt.c
  11 *
  12 *  TODO:
  13 *    Split BBT core and chip specific BBT.
  14 */
  15
  16#include <linux/slab.h>
  17#include <linux/mtd/mtd.h>
  18#include <linux/mtd/onenand.h>
  19#include <linux/export.h>
  20
  21/**
  22 * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  23 * @param buf           the buffer to search
  24 * @param len           the length of buffer to search
  25 * @param paglen        the pagelength
  26 * @param td            search pattern descriptor
  27 *
  28 * Check for a pattern at the given place. Used to search bad block
  29 * tables and good / bad block identifiers. Same as check_pattern, but
  30 * no optional empty check and the pattern is expected to start
  31 * at offset 0.
  32 *
  33 */
  34static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
  35{
  36        int i;
  37        uint8_t *p = buf;
  38
  39        /* Compare the pattern */
  40        for (i = 0; i < td->len; i++) {
  41                if (p[i] != td->pattern[i])
  42                        return -1;
  43        }
  44        return 0;
  45}
  46
  47/**
  48 * create_bbt - [GENERIC] Create a bad block table by scanning the device
  49 * @param mtd           MTD device structure
  50 * @param buf           temporary buffer
  51 * @param bd            descriptor for the good/bad block search pattern
  52 * @param chip          create the table for a specific chip, -1 read all chips.
  53 *              Applies only if NAND_BBT_PERCHIP option is set
  54 *
  55 * Create a bad block table by scanning the device
  56 * for the given good/bad block identify pattern
  57 */
  58static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
  59{
  60        struct onenand_chip *this = mtd->priv;
  61        struct bbm_info *bbm = this->bbm;
  62        int i, j, numblocks, len, scanlen;
  63        int startblock;
  64        loff_t from;
  65        size_t readlen, ooblen;
  66        struct mtd_oob_ops ops;
  67        int rgn;
  68
  69        printk(KERN_INFO "Scanning device for bad blocks\n");
  70
  71        len = 2;
  72
  73        /* We need only read few bytes from the OOB area */
  74        scanlen = ooblen = 0;
  75        readlen = bd->len;
  76
  77        /* chip == -1 case only */
  78        /* Note that numblocks is 2 * (real numblocks) here;
  79         * see i += 2 below as it makses shifting and masking less painful
  80         */
  81        numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
  82        startblock = 0;
  83        from = 0;
  84
  85        ops.mode = MTD_OPS_PLACE_OOB;
  86        ops.ooblen = readlen;
  87        ops.oobbuf = buf;
  88        ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
  89
  90        for (i = startblock; i < numblocks; ) {
  91                int ret;
  92
  93                for (j = 0; j < len; j++) {
  94                        /* No need to read pages fully,
  95                         * just read required OOB bytes */
  96                        ret = onenand_bbt_read_oob(mtd,
  97                                from + j * this->writesize + bd->offs, &ops);
  98
  99                        /* If it is a initial bad block, just ignore it */
 100                        if (ret == ONENAND_BBT_READ_FATAL_ERROR)
 101                                return -EIO;
 102
 103                        if (ret || check_short_pattern(&buf[j * scanlen],
 104                                               scanlen, this->writesize, bd)) {
 105                                bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
 106                                printk(KERN_INFO "OneNAND eraseblock %d is an "
 107                                        "initial bad block\n", i >> 1);
 108                                mtd->ecc_stats.badblocks++;
 109                                break;
 110                        }
 111                }
 112                i += 2;
 113
 114                if (FLEXONENAND(this)) {
 115                        rgn = flexonenand_region(mtd, from);
 116                        from += mtd->eraseregions[rgn].erasesize;
 117                } else
 118                        from += (1 << bbm->bbt_erase_shift);
 119        }
 120
 121        return 0;
 122}
 123
 124
 125/**
 126 * onenand_memory_bbt - [GENERIC] create a memory based bad block table
 127 * @param mtd           MTD device structure
 128 * @param bd            descriptor for the good/bad block search pattern
 129 *
 130 * The function creates a memory based bbt by scanning the device
 131 * for manufacturer / software marked good / bad blocks
 132 */
 133static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
 134{
 135        struct onenand_chip *this = mtd->priv;
 136
 137        return create_bbt(mtd, this->page_buf, bd, -1);
 138}
 139
 140/**
 141 * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
 142 * @param mtd           MTD device structure
 143 * @param offs          offset in the device
 144 * @param allowbbt      allow access to bad block table region
 145 */
 146static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
 147{
 148        struct onenand_chip *this = mtd->priv;
 149        struct bbm_info *bbm = this->bbm;
 150        int block;
 151        uint8_t res;
 152
 153        /* Get block number * 2 */
 154        block = (int) (onenand_block(this, offs) << 1);
 155        res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 156
 157        pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
 158                (unsigned int) offs, block >> 1, res);
 159
 160        switch ((int) res) {
 161        case 0x00:      return 0;
 162        case 0x01:      return 1;
 163        case 0x02:      return allowbbt ? 0 : 1;
 164        }
 165
 166        return 1;
 167}
 168
 169/**
 170 * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
 171 * @param mtd           MTD device structure
 172 * @param bd            descriptor for the good/bad block search pattern
 173 *
 174 * The function checks, if a bad block table(s) is/are already
 175 * available. If not it scans the device for manufacturer
 176 * marked good / bad blocks and writes the bad block table(s) to
 177 * the selected place.
 178 *
 179 * The bad block table memory is allocated here. It is freed
 180 * by the onenand_release function.
 181 *
 182 */
 183static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 184{
 185        struct onenand_chip *this = mtd->priv;
 186        struct bbm_info *bbm = this->bbm;
 187        int len, ret = 0;
 188
 189        len = this->chipsize >> (this->erase_shift + 2);
 190        /* Allocate memory (2bit per block) and clear the memory bad block table */
 191        bbm->bbt = kzalloc(len, GFP_KERNEL);
 192        if (!bbm->bbt)
 193                return -ENOMEM;
 194
 195        /* Set the bad block position */
 196        bbm->badblockpos = ONENAND_BADBLOCK_POS;
 197
 198        /* Set erase shift */
 199        bbm->bbt_erase_shift = this->erase_shift;
 200
 201        if (!bbm->isbad_bbt)
 202                bbm->isbad_bbt = onenand_isbad_bbt;
 203
 204        /* Scan the device to build a memory based bad block table */
 205        if ((ret = onenand_memory_bbt(mtd, bd))) {
 206                printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
 207                kfree(bbm->bbt);
 208                bbm->bbt = NULL;
 209        }
 210
 211        return ret;
 212}
 213
 214/*
 215 * Define some generic bad / good block scan pattern which are used
 216 * while scanning a device for factory marked good / bad blocks.
 217 */
 218static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
 219
 220static struct nand_bbt_descr largepage_memorybased = {
 221        .options = 0,
 222        .offs = 0,
 223        .len = 2,
 224        .pattern = scan_ff_pattern,
 225};
 226
 227/**
 228 * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
 229 * @param mtd           MTD device structure
 230 *
 231 * This function selects the default bad block table
 232 * support for the device and calls the onenand_scan_bbt function
 233 */
 234int onenand_default_bbt(struct mtd_info *mtd)
 235{
 236        struct onenand_chip *this = mtd->priv;
 237        struct bbm_info *bbm;
 238
 239        this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL);
 240        if (!this->bbm)
 241                return -ENOMEM;
 242
 243        bbm = this->bbm;
 244
 245        /* 1KB page has same configuration as 2KB page */
 246        if (!bbm->badblock_pattern)
 247                bbm->badblock_pattern = &largepage_memorybased;
 248
 249        return onenand_scan_bbt(mtd, bbm->badblock_pattern);
 250}
 251