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