162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017 Free Electrons 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Boris Brezillon <boris.brezillon@free-electrons.com> 762306a36Sopenharmony_ci * Peter Pan <peterpandong@micron.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "nand-bbt: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/mtd/nand.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * nanddev_bbt_init() - Initialize the BBT (Bad Block Table) 1762306a36Sopenharmony_ci * @nand: NAND device 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Initialize the in-memory BBT. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ciint nanddev_bbt_init(struct nand_device *nand) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); 2662306a36Sopenharmony_ci unsigned int nblocks = nanddev_neraseblocks(nand); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci nand->bbt.cache = bitmap_zalloc(nblocks * bits_per_block, GFP_KERNEL); 2962306a36Sopenharmony_ci if (!nand->bbt.cache) 3062306a36Sopenharmony_ci return -ENOMEM; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return 0; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_bbt_init); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) 3862306a36Sopenharmony_ci * @nand: NAND device 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Undoes what has been done in nanddev_bbt_init() 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_civoid nanddev_bbt_cleanup(struct nand_device *nand) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci bitmap_free(nand->bbt.cache); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * nanddev_bbt_update() - Update a BBT 5062306a36Sopenharmony_ci * @nand: nand device 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Update the BBT. Currently a NOP function since on-flash bbt is not yet 5362306a36Sopenharmony_ci * supported. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint nanddev_bbt_update(struct nand_device *nand) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_bbt_update); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * nanddev_bbt_get_block_status() - Return the status of an eraseblock 6562306a36Sopenharmony_ci * @nand: nand device 6662306a36Sopenharmony_ci * @entry: the BBT entry 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry 6962306a36Sopenharmony_ci * is bigger than the BBT size. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ciint nanddev_bbt_get_block_status(const struct nand_device *nand, 7262306a36Sopenharmony_ci unsigned int entry) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); 7562306a36Sopenharmony_ci unsigned long *pos = nand->bbt.cache + 7662306a36Sopenharmony_ci ((entry * bits_per_block) / BITS_PER_LONG); 7762306a36Sopenharmony_ci unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; 7862306a36Sopenharmony_ci unsigned long status; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (entry >= nanddev_neraseblocks(nand)) 8162306a36Sopenharmony_ci return -ERANGE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci status = pos[0] >> offs; 8462306a36Sopenharmony_ci if (bits_per_block + offs > BITS_PER_LONG) 8562306a36Sopenharmony_ci status |= pos[1] << (BITS_PER_LONG - offs); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return status & GENMASK(bits_per_block - 1, 0); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the 9362306a36Sopenharmony_ci * in-memory BBT 9462306a36Sopenharmony_ci * @nand: nand device 9562306a36Sopenharmony_ci * @entry: the BBT entry to update 9662306a36Sopenharmony_ci * @status: the new status 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * Update an entry of the in-memory BBT. If you want to push the updated BBT 9962306a36Sopenharmony_ci * the NAND you should call nanddev_bbt_update(). 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT 10262306a36Sopenharmony_ci * size. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciint nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, 10562306a36Sopenharmony_ci enum nand_bbt_block_status status) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); 10862306a36Sopenharmony_ci unsigned long *pos = nand->bbt.cache + 10962306a36Sopenharmony_ci ((entry * bits_per_block) / BITS_PER_LONG); 11062306a36Sopenharmony_ci unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; 11162306a36Sopenharmony_ci unsigned long val = status & GENMASK(bits_per_block - 1, 0); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (entry >= nanddev_neraseblocks(nand)) 11462306a36Sopenharmony_ci return -ERANGE; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); 11762306a36Sopenharmony_ci pos[0] |= val << offs; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (bits_per_block + offs > BITS_PER_LONG) { 12062306a36Sopenharmony_ci unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci pos[1] &= ~GENMASK(rbits - 1, 0); 12362306a36Sopenharmony_ci pos[1] |= val >> (bits_per_block - rbits); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); 129