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_DEL 2
|
||||
#define NAND_MARKBAD 3
|
||||
#define NAND_MARKGOOD 4
|
||||
|
||||
static int do_nand(int argc, char *argv[])
|
||||
{
|
||||
|
@ -39,7 +40,7 @@ static int do_nand(int argc, char *argv[])
|
|||
int command = 0;
|
||||
loff_t badblock = 0;
|
||||
|
||||
while((opt = getopt(argc, argv, "adb:")) > 0) {
|
||||
while((opt = getopt(argc, argv, "adb:g:")) > 0) {
|
||||
if (command) {
|
||||
printf("only one command may be given\n");
|
||||
return 1;
|
||||
|
@ -55,12 +56,24 @@ static int do_nand(int argc, char *argv[])
|
|||
case 'b':
|
||||
command = NAND_MARKBAD;
|
||||
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)
|
||||
return COMMAND_ERROR_USAGE;
|
||||
|
||||
if (!command) {
|
||||
printf("No action given\n");
|
||||
return COMMAND_ERROR_USAGE;
|
||||
}
|
||||
|
||||
if (command == NAND_ADD) {
|
||||
while (optind < argc) {
|
||||
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;
|
||||
const char *str;
|
||||
int ctl;
|
||||
|
||||
printf("marking block at 0x%08llx on %s as bad\n",
|
||||
badblock, argv[optind]);
|
||||
if (command == NAND_MARKBAD) {
|
||||
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);
|
||||
if (fd < 0) {
|
||||
|
@ -89,7 +112,7 @@ static int do_nand(int argc, char *argv[])
|
|||
return 1;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, MEMSETBADBLOCK, &badblock);
|
||||
ret = ioctl(fd, ctl, &badblock);
|
||||
if (ret) {
|
||||
if (ret == -EINVAL)
|
||||
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 ("-d", "deregister a bad block aware device")
|
||||
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_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);
|
||||
ret = mtd_block_markbad(mtd, *offset);
|
||||
break;
|
||||
case MEMSETGOODBLOCK:
|
||||
dev_dbg(cdev->dev, "MEMSETGOODBLOCK: 0x%08llx\n", *offset);
|
||||
ret = mtd_block_markgood(mtd, *offset);
|
||||
break;
|
||||
case MEMERASE:
|
||||
ret = mtd_op_erase(cdev, ei->length, ei->start + cdev->offset);
|
||||
break;
|
||||
|
@ -320,6 +324,18 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
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,
|
||||
u_char *buf)
|
||||
{
|
||||
|
|
|
@ -506,6 +506,28 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
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
|
||||
* 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;
|
||||
if (subdev[0]->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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @mtd: MTD device structure
|
||||
|
@ -2844,6 +2900,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
|||
#ifdef CONFIG_MTD_WRITE
|
||||
if (!chip->block_markbad)
|
||||
chip->block_markbad = nand_default_block_markbad;
|
||||
if (!chip->block_markgood)
|
||||
chip->block_markgood = nand_default_block_markgood;
|
||||
if (!chip->write_buf)
|
||||
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
|
||||
#endif
|
||||
|
@ -3707,6 +3765,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
mtd->unlock = NULL;
|
||||
mtd->block_isbad = nand_block_isbad;
|
||||
mtd->block_markbad = nand_block_markbad;
|
||||
mtd->block_markgood = nand_block_markgood;
|
||||
mtd->writebufsize = mtd->writesize;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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,
|
||||
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->unlock = mtd_part_unlock;
|
||||
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)
|
||||
|
|
|
@ -192,6 +192,7 @@ static int partition_ioctl(struct cdev *cdev, int request, void *buf)
|
|||
|
||||
switch (request) {
|
||||
case MEMSETBADBLOCK:
|
||||
case MEMSETGOODBLOCK:
|
||||
case MEMGETBADBLOCK:
|
||||
offset = *_buf;
|
||||
offset += cdev->offset;
|
||||
|
|
|
@ -118,6 +118,7 @@ struct otp_info {
|
|||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||
#define MTDFILEMODE _IO('M', 19)
|
||||
#define MEMSETGOODBLOCK _IOW('M', 20, loff_t)
|
||||
|
||||
/*
|
||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||
|
|
|
@ -189,6 +189,7 @@ struct mtd_info {
|
|||
/* Bad block management functions */
|
||||
int (*block_isbad) (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 */
|
||||
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_block_isbad(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);
|
||||
|
||||
|
|
|
@ -394,6 +394,7 @@ struct nand_buffers {
|
|||
* @select_chip: [REPLACEABLE] select chip nr
|
||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
||||
* @block_markgood: [REPLACEABLE] mark the block good
|
||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
||||
|
@ -479,6 +480,7 @@ struct nand_chip {
|
|||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
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);
|
||||
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
||||
u8 *id_data);
|
||||
|
|
Loading…
Reference in New Issue