mtd: Add support for marking blocks as good
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
a0fa6e1d2b
commit
172af2a30c
|
@ -32,6 +32,7 @@
|
||||||
#define NAND_ADD 1
|
#define NAND_ADD 1
|
||||||
#define NAND_DEL 2
|
#define NAND_DEL 2
|
||||||
#define NAND_MARKBAD 3
|
#define NAND_MARKBAD 3
|
||||||
|
#define NAND_MARKGOOD 4
|
||||||
|
|
||||||
static int do_nand(int argc, char *argv[])
|
static int do_nand(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -39,7 +40,7 @@ static int do_nand(int argc, char *argv[])
|
||||||
int command = 0;
|
int command = 0;
|
||||||
loff_t badblock = 0;
|
loff_t badblock = 0;
|
||||||
|
|
||||||
while((opt = getopt(argc, argv, "adb:")) > 0) {
|
while((opt = getopt(argc, argv, "adb:g:")) > 0) {
|
||||||
if (command) {
|
if (command) {
|
||||||
printf("only one command may be given\n");
|
printf("only one command may be given\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -55,12 +56,24 @@ static int do_nand(int argc, char *argv[])
|
||||||
case 'b':
|
case 'b':
|
||||||
command = NAND_MARKBAD;
|
command = NAND_MARKBAD;
|
||||||
badblock = strtoull_suffix(optarg, NULL, 0);
|
badblock = strtoull_suffix(optarg, NULL, 0);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
command = NAND_MARKGOOD;
|
||||||
|
badblock = strtoull_suffix(optarg, NULL, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return COMMAND_ERROR_USAGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
return COMMAND_ERROR_USAGE;
|
return COMMAND_ERROR_USAGE;
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
printf("No action given\n");
|
||||||
|
return COMMAND_ERROR_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
if (command == NAND_ADD) {
|
if (command == NAND_ADD) {
|
||||||
while (optind < argc) {
|
while (optind < argc) {
|
||||||
if (dev_add_bb_dev(basename(argv[optind]), NULL))
|
if (dev_add_bb_dev(basename(argv[optind]), NULL))
|
||||||
|
@ -77,11 +90,21 @@ static int do_nand(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command == NAND_MARKBAD) {
|
if (command == NAND_MARKBAD || command == NAND_MARKGOOD) {
|
||||||
int ret = 0, fd;
|
int ret = 0, fd;
|
||||||
|
const char *str;
|
||||||
|
int ctl;
|
||||||
|
|
||||||
printf("marking block at 0x%08llx on %s as bad\n",
|
if (command == NAND_MARKBAD) {
|
||||||
badblock, argv[optind]);
|
str = "bad";
|
||||||
|
ctl = MEMSETBADBLOCK;
|
||||||
|
} else {
|
||||||
|
str = "good";
|
||||||
|
ctl = MEMSETGOODBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("marking block at 0x%08llx on %s as %s\n",
|
||||||
|
badblock, argv[optind], str);
|
||||||
|
|
||||||
fd = open(argv[optind], O_RDWR);
|
fd = open(argv[optind], O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
|
@ -89,7 +112,7 @@ static int do_nand(int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, MEMSETBADBLOCK, &badblock);
|
ret = ioctl(fd, ctl, &badblock);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret == -EINVAL)
|
if (ret == -EINVAL)
|
||||||
printf("Maybe offset %lld is out of range.\n",
|
printf("Maybe offset %lld is out of range.\n",
|
||||||
|
@ -110,6 +133,7 @@ BAREBOX_CMD_HELP_TEXT("Options:")
|
||||||
BAREBOX_CMD_HELP_OPT ("-a", "register a bad block aware device ontop of a normal NAND device")
|
BAREBOX_CMD_HELP_OPT ("-a", "register a bad block aware device ontop of a normal NAND device")
|
||||||
BAREBOX_CMD_HELP_OPT ("-d", "deregister a bad block aware device")
|
BAREBOX_CMD_HELP_OPT ("-d", "deregister a bad block aware device")
|
||||||
BAREBOX_CMD_HELP_OPT ("-b OFFS", "mark block at OFFSet as bad")
|
BAREBOX_CMD_HELP_OPT ("-b OFFS", "mark block at OFFSet as bad")
|
||||||
|
BAREBOX_CMD_HELP_OPT ("-g OFFS", "mark block at OFFSet as good")
|
||||||
BAREBOX_CMD_HELP_END
|
BAREBOX_CMD_HELP_END
|
||||||
|
|
||||||
BAREBOX_CMD_START(nand)
|
BAREBOX_CMD_START(nand)
|
||||||
|
|
|
@ -231,6 +231,10 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf)
|
||||||
dev_dbg(cdev->dev, "MEMSETBADBLOCK: 0x%08llx\n", *offset);
|
dev_dbg(cdev->dev, "MEMSETBADBLOCK: 0x%08llx\n", *offset);
|
||||||
ret = mtd_block_markbad(mtd, *offset);
|
ret = mtd_block_markbad(mtd, *offset);
|
||||||
break;
|
break;
|
||||||
|
case MEMSETGOODBLOCK:
|
||||||
|
dev_dbg(cdev->dev, "MEMSETGOODBLOCK: 0x%08llx\n", *offset);
|
||||||
|
ret = mtd_block_markgood(mtd, *offset);
|
||||||
|
break;
|
||||||
case MEMERASE:
|
case MEMERASE:
|
||||||
ret = mtd_op_erase(cdev, ei->length, ei->start + cdev->offset);
|
ret = mtd_op_erase(cdev, ei->length, ei->start + cdev->offset);
|
||||||
break;
|
break;
|
||||||
|
@ -320,6 +324,18 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (mtd->block_markgood)
|
||||||
|
ret = mtd->block_markgood(mtd, ofs);
|
||||||
|
else
|
||||||
|
ret = -ENOSYS;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||||
u_char *buf)
|
u_char *buf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -506,6 +506,28 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int concat_block_markgood(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
struct mtd_concat *concat = CONCAT(mtd);
|
||||||
|
int i, err = -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < concat->num_subdev; i++) {
|
||||||
|
struct mtd_info *subdev = concat->subdev[i];
|
||||||
|
|
||||||
|
if (ofs >= subdev->size) {
|
||||||
|
ofs -= subdev->size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mtd_block_markgood(subdev, ofs);
|
||||||
|
if (!err)
|
||||||
|
mtd->ecc_stats.badblocks--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function constructs a virtual MTD device by concatenating
|
* This function constructs a virtual MTD device by concatenating
|
||||||
* num_devs MTD devices. A pointer to the new device object is
|
* num_devs MTD devices. A pointer to the new device object is
|
||||||
|
@ -565,6 +587,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||||
concat->mtd.block_isbad = concat_block_isbad;
|
concat->mtd.block_isbad = concat_block_isbad;
|
||||||
if (subdev[0]->block_markbad)
|
if (subdev[0]->block_markbad)
|
||||||
concat->mtd.block_markbad = concat_block_markbad;
|
concat->mtd.block_markbad = concat_block_markbad;
|
||||||
|
if (subdev[0]->block_markgood)
|
||||||
|
concat->mtd.block_markgood = concat_block_markgood;
|
||||||
|
|
||||||
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
|
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
|
||||||
|
|
||||||
|
|
|
@ -456,6 +456,38 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
||||||
return chip->block_bad(mtd, ofs, getchip);
|
return chip->block_bad(mtd, ofs, getchip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_default_block_markgood - [DEFAULT] mark a block good
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @ofs: offset from device start
|
||||||
|
*
|
||||||
|
* This is the default implementation, which can be overridden by
|
||||||
|
* a hardware specific driver.
|
||||||
|
*/
|
||||||
|
static __maybe_unused int nand_default_block_markgood(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int block, res, ret = 0;
|
||||||
|
|
||||||
|
/* Get block number */
|
||||||
|
block = (int)(ofs >> chip->bbt_erase_shift);
|
||||||
|
/* Mark block good in memory-based BBT */
|
||||||
|
if (chip->bbt)
|
||||||
|
chip->bbt[block >> 2] &= ~(0x01 << ((block & 0x03) << 1));
|
||||||
|
|
||||||
|
/* Update flash-based bad block table */
|
||||||
|
if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||||
|
res = nand_update_bbt(mtd, ofs);
|
||||||
|
if (!ret)
|
||||||
|
ret = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
mtd->ecc_stats.badblocks++;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for the ready pin, after a command. The timeout is caught later. */
|
/* Wait for the ready pin, after a command. The timeout is caught later. */
|
||||||
void nand_wait_ready(struct mtd_info *mtd)
|
void nand_wait_ready(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
|
@ -2774,6 +2806,30 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return chip->block_markbad(mtd, ofs);
|
return chip->block_markbad(mtd, ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_block_markgood - [MTD Interface] Mark block at the given offset as bad
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @ofs: offset relative to mtd start
|
||||||
|
*/
|
||||||
|
static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_MTD_WRITE))
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
ret = nand_block_isbad(mtd, ofs);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* If it was good already, return success and do nothing */
|
||||||
|
if (!ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return chip->block_markgood(mtd, ofs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
|
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -2844,6 +2900,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||||
#ifdef CONFIG_MTD_WRITE
|
#ifdef CONFIG_MTD_WRITE
|
||||||
if (!chip->block_markbad)
|
if (!chip->block_markbad)
|
||||||
chip->block_markbad = nand_default_block_markbad;
|
chip->block_markbad = nand_default_block_markbad;
|
||||||
|
if (!chip->block_markgood)
|
||||||
|
chip->block_markgood = nand_default_block_markgood;
|
||||||
if (!chip->write_buf)
|
if (!chip->write_buf)
|
||||||
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
|
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
|
||||||
#endif
|
#endif
|
||||||
|
@ -3707,6 +3765,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||||
mtd->unlock = NULL;
|
mtd->unlock = NULL;
|
||||||
mtd->block_isbad = nand_block_isbad;
|
mtd->block_isbad = nand_block_isbad;
|
||||||
mtd->block_markbad = nand_block_markbad;
|
mtd->block_markbad = nand_block_markbad;
|
||||||
|
mtd->block_markgood = nand_block_markgood;
|
||||||
mtd->writebufsize = mtd->writesize;
|
mtd->writebufsize = mtd->writesize;
|
||||||
|
|
||||||
/* propagate ecc info to mtd_info */
|
/* propagate ecc info to mtd_info */
|
||||||
|
|
|
@ -114,6 +114,21 @@ static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mtd_part_block_markgood(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!(mtd->flags & MTD_WRITEABLE))
|
||||||
|
return -EROFS;
|
||||||
|
if (ofs >= mtd->size)
|
||||||
|
return -EINVAL;
|
||||||
|
ofs += mtd->master_offset;
|
||||||
|
res = mtd->master->block_markgood(mtd->master, ofs);
|
||||||
|
if (!res)
|
||||||
|
mtd->ecc_stats.badblocks--;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
|
struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
|
||||||
uint64_t size, unsigned long flags, const char *name)
|
uint64_t size, unsigned long flags, const char *name)
|
||||||
{
|
{
|
||||||
|
@ -168,6 +183,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
|
||||||
part->lock = mtd_part_lock;
|
part->lock = mtd_part_lock;
|
||||||
part->unlock = mtd_part_unlock;
|
part->unlock = mtd_part_unlock;
|
||||||
part->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
|
part->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
|
||||||
|
part->block_markgood = mtd->block_markgood ? mtd_part_block_markgood : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtd->write_oob)
|
if (mtd->write_oob)
|
||||||
|
|
|
@ -192,6 +192,7 @@ static int partition_ioctl(struct cdev *cdev, int request, void *buf)
|
||||||
|
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case MEMSETBADBLOCK:
|
case MEMSETBADBLOCK:
|
||||||
|
case MEMSETGOODBLOCK:
|
||||||
case MEMGETBADBLOCK:
|
case MEMGETBADBLOCK:
|
||||||
offset = *_buf;
|
offset = *_buf;
|
||||||
offset += cdev->offset;
|
offset += cdev->offset;
|
||||||
|
|
|
@ -118,6 +118,7 @@ struct otp_info {
|
||||||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||||
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||||
#define MTDFILEMODE _IO('M', 19)
|
#define MTDFILEMODE _IO('M', 19)
|
||||||
|
#define MEMSETGOODBLOCK _IOW('M', 20, loff_t)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||||
|
|
|
@ -189,6 +189,7 @@ struct mtd_info {
|
||||||
/* Bad block management functions */
|
/* Bad block management functions */
|
||||||
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
|
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
|
||||||
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
|
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
|
||||||
|
int (*block_markgood) (struct mtd_info *mtd, loff_t ofs);
|
||||||
|
|
||||||
/* ECC status information */
|
/* ECC status information */
|
||||||
struct mtd_ecc_stats ecc_stats;
|
struct mtd_ecc_stats ecc_stats;
|
||||||
|
@ -308,6 +309,7 @@ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||||
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||||
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
|
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
|
||||||
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
|
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
|
||||||
|
int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs);
|
||||||
|
|
||||||
int mtd_all_ff(const void *buf, unsigned int len);
|
int mtd_all_ff(const void *buf, unsigned int len);
|
||||||
|
|
||||||
|
|
|
@ -394,6 +394,7 @@ struct nand_buffers {
|
||||||
* @select_chip: [REPLACEABLE] select chip nr
|
* @select_chip: [REPLACEABLE] select chip nr
|
||||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
* @block_bad: [REPLACEABLE] check, if the block is bad
|
||||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
* @block_markbad: [REPLACEABLE] mark the block bad
|
||||||
|
* @block_markgood: [REPLACEABLE] mark the block good
|
||||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||||
* ALE/CLE/nCE. Also used to write command and address
|
* ALE/CLE/nCE. Also used to write command and address
|
||||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
||||||
|
@ -479,6 +480,7 @@ struct nand_chip {
|
||||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||||
|
int (*block_markgood)(struct mtd_info *mtd, loff_t ofs);
|
||||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||||
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
||||||
u8 *id_data);
|
u8 *id_data);
|
||||||
|
|
Loading…
Reference in New Issue