linux/drivers/mtd/nand/bbt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2017 Free Electrons
   4 *
   5 * Authors:
   6 *      Boris Brezillon <boris.brezillon@free-electrons.com>
   7 *      Peter Pan <peterpandong@micron.com>
   8 */
   9
  10#define pr_fmt(fmt)     "nand-bbt: " fmt
  11
  12#include <linux/mtd/nand.h>
  13#include <linux/slab.h>
  14
  15/**
  16 * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
  17 * @nand: NAND device
  18 *
  19 * Initialize the in-memory BBT.
  20 *
  21 * Return: 0 in case of success, a negative error code otherwise.
  22 */
  23int nanddev_bbt_init(struct nand_device *nand)
  24{
  25        unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
  26        unsigned int nblocks = nanddev_neraseblocks(nand);
  27        unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
  28                                           BITS_PER_LONG);
  29
  30        nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
  31                                  GFP_KERNEL);
  32        if (!nand->bbt.cache)
  33                return -ENOMEM;
  34
  35        return 0;
  36}
  37EXPORT_SYMBOL_GPL(nanddev_bbt_init);
  38
  39/**
  40 * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
  41 * @nand: NAND device
  42 *
  43 * Undoes what has been done in nanddev_bbt_init()
  44 */
  45void nanddev_bbt_cleanup(struct nand_device *nand)
  46{
  47        kfree(nand->bbt.cache);
  48}
  49EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
  50
  51/**
  52 * nanddev_bbt_update() - Update a BBT
  53 * @nand: nand device
  54 *
  55 * Update the BBT. Currently a NOP function since on-flash bbt is not yet
  56 * supported.
  57 *
  58 * Return: 0 in case of success, a negative error code otherwise.
  59 */
  60int nanddev_bbt_update(struct nand_device *nand)
  61{
  62        return 0;
  63}
  64EXPORT_SYMBOL_GPL(nanddev_bbt_update);
  65
  66/**
  67 * nanddev_bbt_get_block_status() - Return the status of an eraseblock
  68 * @nand: nand device
  69 * @entry: the BBT entry
  70 *
  71 * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
  72 *         is bigger than the BBT size.
  73 */
  74int nanddev_bbt_get_block_status(const struct nand_device *nand,
  75                                 unsigned int entry)
  76{
  77        unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
  78        unsigned long *pos = nand->bbt.cache +
  79                             ((entry * bits_per_block) / BITS_PER_LONG);
  80        unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
  81        unsigned long status;
  82
  83        if (entry >= nanddev_neraseblocks(nand))
  84                return -ERANGE;
  85
  86        status = pos[0] >> offs;
  87        if (bits_per_block + offs > BITS_PER_LONG)
  88                status |= pos[1] << (BITS_PER_LONG - offs);
  89
  90        return status & GENMASK(bits_per_block - 1, 0);
  91}
  92EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
  93
  94/**
  95 * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
  96 *                                  in-memory BBT
  97 * @nand: nand device
  98 * @entry: the BBT entry to update
  99 * @status: the new status
 100 *
 101 * Update an entry of the in-memory BBT. If you want to push the updated BBT
 102 * the NAND you should call nanddev_bbt_update().
 103 *
 104 * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
 105 *         size.
 106 */
 107int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
 108                                 enum nand_bbt_block_status status)
 109{
 110        unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
 111        unsigned long *pos = nand->bbt.cache +
 112                             ((entry * bits_per_block) / BITS_PER_LONG);
 113        unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
 114        unsigned long val = status & GENMASK(bits_per_block - 1, 0);
 115
 116        if (entry >= nanddev_neraseblocks(nand))
 117                return -ERANGE;
 118
 119        pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
 120        pos[0] |= val << offs;
 121
 122        if (bits_per_block + offs > BITS_PER_LONG) {
 123                unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
 124
 125                pos[1] &= ~GENMASK(rbits - 1, 0);
 126                pos[1] |= val >> (bits_per_block - rbits);
 127        }
 128
 129        return 0;
 130}
 131EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
 132