Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2017 Free Electrons
0004  *
0005  * Authors:
0006  *  Boris Brezillon <boris.brezillon@free-electrons.com>
0007  *  Peter Pan <peterpandong@micron.com>
0008  */
0009 
0010 #define pr_fmt(fmt) "nand-bbt: " fmt
0011 
0012 #include <linux/mtd/nand.h>
0013 #include <linux/slab.h>
0014 
0015 /**
0016  * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
0017  * @nand: NAND device
0018  *
0019  * Initialize the in-memory BBT.
0020  *
0021  * Return: 0 in case of success, a negative error code otherwise.
0022  */
0023 int nanddev_bbt_init(struct nand_device *nand)
0024 {
0025     unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
0026     unsigned int nblocks = nanddev_neraseblocks(nand);
0027     unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
0028                        BITS_PER_LONG);
0029 
0030     nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
0031                   GFP_KERNEL);
0032     if (!nand->bbt.cache)
0033         return -ENOMEM;
0034 
0035     return 0;
0036 }
0037 EXPORT_SYMBOL_GPL(nanddev_bbt_init);
0038 
0039 /**
0040  * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
0041  * @nand: NAND device
0042  *
0043  * Undoes what has been done in nanddev_bbt_init()
0044  */
0045 void nanddev_bbt_cleanup(struct nand_device *nand)
0046 {
0047     kfree(nand->bbt.cache);
0048 }
0049 EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
0050 
0051 /**
0052  * nanddev_bbt_update() - Update a BBT
0053  * @nand: nand device
0054  *
0055  * Update the BBT. Currently a NOP function since on-flash bbt is not yet
0056  * supported.
0057  *
0058  * Return: 0 in case of success, a negative error code otherwise.
0059  */
0060 int nanddev_bbt_update(struct nand_device *nand)
0061 {
0062     return 0;
0063 }
0064 EXPORT_SYMBOL_GPL(nanddev_bbt_update);
0065 
0066 /**
0067  * nanddev_bbt_get_block_status() - Return the status of an eraseblock
0068  * @nand: nand device
0069  * @entry: the BBT entry
0070  *
0071  * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
0072  *     is bigger than the BBT size.
0073  */
0074 int nanddev_bbt_get_block_status(const struct nand_device *nand,
0075                  unsigned int entry)
0076 {
0077     unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
0078     unsigned long *pos = nand->bbt.cache +
0079                  ((entry * bits_per_block) / BITS_PER_LONG);
0080     unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
0081     unsigned long status;
0082 
0083     if (entry >= nanddev_neraseblocks(nand))
0084         return -ERANGE;
0085 
0086     status = pos[0] >> offs;
0087     if (bits_per_block + offs > BITS_PER_LONG)
0088         status |= pos[1] << (BITS_PER_LONG - offs);
0089 
0090     return status & GENMASK(bits_per_block - 1, 0);
0091 }
0092 EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
0093 
0094 /**
0095  * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
0096  *                  in-memory BBT
0097  * @nand: nand device
0098  * @entry: the BBT entry to update
0099  * @status: the new status
0100  *
0101  * Update an entry of the in-memory BBT. If you want to push the updated BBT
0102  * the NAND you should call nanddev_bbt_update().
0103  *
0104  * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
0105  *     size.
0106  */
0107 int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
0108                  enum nand_bbt_block_status status)
0109 {
0110     unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
0111     unsigned long *pos = nand->bbt.cache +
0112                  ((entry * bits_per_block) / BITS_PER_LONG);
0113     unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
0114     unsigned long val = status & GENMASK(bits_per_block - 1, 0);
0115 
0116     if (entry >= nanddev_neraseblocks(nand))
0117         return -ERANGE;
0118 
0119     pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
0120     pos[0] |= val << offs;
0121 
0122     if (bits_per_block + offs > BITS_PER_LONG) {
0123         unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
0124 
0125         pos[1] &= ~GENMASK(rbits - 1, 0);
0126         pos[1] |= val >> (bits_per_block - rbits);
0127     }
0128 
0129     return 0;
0130 }
0131 EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);