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