2018-02-05 23:02:05 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2017 Free Electrons
*
* Authors :
* Boris Brezillon < boris . brezillon @ free - electrons . com >
* Peter Pan < peterpandong @ micron . com >
*/
# define pr_fmt(fmt) "nand-bbt: " fmt
# include <linux/mtd/nand.h>
# include <linux/slab.h>
/**
* nanddev_bbt_init ( ) - Initialize the BBT ( Bad Block Table )
* @ nand : NAND device
*
* Initialize the in - memory BBT .
*
* Return : 0 in case of success , a negative error code otherwise .
*/
int nanddev_bbt_init ( struct nand_device * nand )
{
unsigned int bits_per_block = fls ( NAND_BBT_BLOCK_NUM_STATUS ) ;
unsigned int nblocks = nanddev_neraseblocks ( nand ) ;
2022-07-03 18:09:45 +02:00
nand - > bbt . cache = bitmap_zalloc ( nblocks * bits_per_block , GFP_KERNEL ) ;
2018-02-05 23:02:05 +01:00
if ( ! nand - > bbt . cache )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( nanddev_bbt_init ) ;
/**
* nanddev_bbt_cleanup ( ) - Cleanup the BBT ( Bad Block Table )
* @ nand : NAND device
*
* Undoes what has been done in nanddev_bbt_init ( )
*/
void nanddev_bbt_cleanup ( struct nand_device * nand )
{
2022-07-03 18:09:45 +02:00
bitmap_free ( nand - > bbt . cache ) ;
2018-02-05 23:02:05 +01:00
}
EXPORT_SYMBOL_GPL ( nanddev_bbt_cleanup ) ;
/**
* nanddev_bbt_update ( ) - Update a BBT
* @ nand : nand device
*
* Update the BBT . Currently a NOP function since on - flash bbt is not yet
* supported .
*
* Return : 0 in case of success , a negative error code otherwise .
*/
int nanddev_bbt_update ( struct nand_device * nand )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( nanddev_bbt_update ) ;
/**
* nanddev_bbt_get_block_status ( ) - Return the status of an eraseblock
* @ nand : nand device
* @ entry : the BBT entry
*
* Return : a positive number nand_bbt_block_status status or - % ERANGE if @ entry
* is bigger than the BBT size .
*/
int nanddev_bbt_get_block_status ( const struct nand_device * nand ,
unsigned int entry )
{
unsigned int bits_per_block = fls ( NAND_BBT_BLOCK_NUM_STATUS ) ;
unsigned long * pos = nand - > bbt . cache +
( ( entry * bits_per_block ) / BITS_PER_LONG ) ;
unsigned int offs = ( entry * bits_per_block ) % BITS_PER_LONG ;
unsigned long status ;
if ( entry > = nanddev_neraseblocks ( nand ) )
return - ERANGE ;
status = pos [ 0 ] > > offs ;
if ( bits_per_block + offs > BITS_PER_LONG )
status | = pos [ 1 ] < < ( BITS_PER_LONG - offs ) ;
return status & GENMASK ( bits_per_block - 1 , 0 ) ;
}
EXPORT_SYMBOL_GPL ( nanddev_bbt_get_block_status ) ;
/**
* nanddev_bbt_set_block_status ( ) - Update the status of an eraseblock in the
* in - memory BBT
* @ nand : nand device
* @ entry : the BBT entry to update
* @ status : the new status
*
* Update an entry of the in - memory BBT . If you want to push the updated BBT
* the NAND you should call nanddev_bbt_update ( ) .
*
* Return : 0 in case of success or - % ERANGE if @ entry is bigger than the BBT
* size .
*/
int nanddev_bbt_set_block_status ( struct nand_device * nand , unsigned int entry ,
enum nand_bbt_block_status status )
{
unsigned int bits_per_block = fls ( NAND_BBT_BLOCK_NUM_STATUS ) ;
unsigned long * pos = nand - > bbt . cache +
( ( entry * bits_per_block ) / BITS_PER_LONG ) ;
unsigned int offs = ( entry * bits_per_block ) % BITS_PER_LONG ;
unsigned long val = status & GENMASK ( bits_per_block - 1 , 0 ) ;
if ( entry > = nanddev_neraseblocks ( nand ) )
return - ERANGE ;
pos [ 0 ] & = ~ GENMASK ( offs + bits_per_block - 1 , offs ) ;
pos [ 0 ] | = val < < offs ;
if ( bits_per_block + offs > BITS_PER_LONG ) {
unsigned int rbits = bits_per_block + offs - BITS_PER_LONG ;
pos [ 1 ] & = ~ GENMASK ( rbits - 1 , 0 ) ;
2021-04-06 10:47:08 +09:00
pos [ 1 ] | = val > > ( bits_per_block - rbits ) ;
2018-02-05 23:02:05 +01:00
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( nanddev_bbt_set_block_status ) ;