mirror of
https://github.com/torvalds/linux.git
synced 2024-10-26 23:12:36 +00:00
mtd: nand: refactor chip->block_markbad interface
The chip->block_markbad pointer should really only be responsible for writing a bad block marker for new bad blocks. It should not take care of BBT-related functionality, nor should it handle bookkeeping of bad block stats. This patch refactors the 3 users of the block_markbad interface (plus the default nand_base implementation) so that the common code is kept in nand_block_markbad_lowlevel(). It removes some inconsistencies between the various implementations and should allow for more centralized improvements in the future. Because gpmi-nand no longer needs the nand_update_bbt() function, let's stop exporting it as well. Signed-off-by: Brian Norris <computersforpeace@gmail.com> Acked-by: Huang Shijie <b32955@freescale.com> (for gpmi-nand parts) Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
39dbb02998
commit
5a0edb251a
|
@ -1093,7 +1093,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
struct nand_chip *nand = mtd->priv;
|
struct nand_chip *nand = mtd->priv;
|
||||||
struct docg4_priv *doc = nand->priv;
|
struct docg4_priv *doc = nand->priv;
|
||||||
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
|
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
|
||||||
int block = (int)(ofs >> nand->bbt_erase_shift);
|
|
||||||
int page = (int)(ofs >> nand->page_shift);
|
int page = (int)(ofs >> nand->page_shift);
|
||||||
uint32_t g4_addr = mtd_to_docg4_address(page, 0);
|
uint32_t g4_addr = mtd_to_docg4_address(page, 0);
|
||||||
|
|
||||||
|
@ -1108,9 +1107,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* update bbt in memory */
|
|
||||||
nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2);
|
|
||||||
|
|
||||||
/* write bit-wise negation of pattern to oob buffer */
|
/* write bit-wise negation of pattern to oob buffer */
|
||||||
memset(nand->oob_poi, 0xff, mtd->oobsize);
|
memset(nand->oob_poi, 0xff, mtd->oobsize);
|
||||||
for (i = 0; i < bbtd->len; i++)
|
for (i = 0; i < bbtd->len; i++)
|
||||||
|
@ -1120,8 +1116,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
write_page_prologue(mtd, g4_addr);
|
write_page_prologue(mtd, g4_addr);
|
||||||
docg4_write_page(mtd, nand, buf, 1);
|
docg4_write_page(mtd, nand, buf, 1);
|
||||||
ret = pageprog(mtd);
|
ret = pageprog(mtd);
|
||||||
if (!ret)
|
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
|
||||||
|
|
|
@ -1148,43 +1148,31 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
struct gpmi_nand_data *this = chip->priv;
|
struct gpmi_nand_data *this = chip->priv;
|
||||||
int block, ret = 0;
|
int ret = 0;
|
||||||
uint8_t *block_mark;
|
uint8_t *block_mark;
|
||||||
int column, page, status, chipnr;
|
int column, page, status, chipnr;
|
||||||
|
|
||||||
/* Get block number */
|
chipnr = (int)(ofs >> chip->chip_shift);
|
||||||
block = (int)(ofs >> chip->bbt_erase_shift);
|
chip->select_chip(mtd, chipnr);
|
||||||
if (chip->bbt)
|
|
||||||
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
|
||||||
|
|
||||||
/* Do we have a flash based bad block table ? */
|
column = this->swap_block_mark ? mtd->writesize : 0;
|
||||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
|
||||||
ret = nand_update_bbt(mtd, ofs);
|
|
||||||
else {
|
|
||||||
chipnr = (int)(ofs >> chip->chip_shift);
|
|
||||||
chip->select_chip(mtd, chipnr);
|
|
||||||
|
|
||||||
column = this->swap_block_mark ? mtd->writesize : 0;
|
/* Write the block mark. */
|
||||||
|
block_mark = this->data_buffer_dma;
|
||||||
|
block_mark[0] = 0; /* bad block marker */
|
||||||
|
|
||||||
/* Write the block mark. */
|
/* Shift to get page */
|
||||||
block_mark = this->data_buffer_dma;
|
page = (int)(ofs >> chip->page_shift);
|
||||||
block_mark[0] = 0; /* bad block marker */
|
|
||||||
|
|
||||||
/* Shift to get page */
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
|
||||||
page = (int)(ofs >> chip->page_shift);
|
chip->write_buf(mtd, block_mark, 1);
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
|
status = chip->waitfunc(mtd, chip);
|
||||||
chip->write_buf(mtd, block_mark, 1);
|
if (status & NAND_STATUS_FAIL)
|
||||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
ret = -EIO;
|
||||||
|
|
||||||
status = chip->waitfunc(mtd, chip);
|
chip->select_chip(mtd, -1);
|
||||||
if (status & NAND_STATUS_FAIL)
|
|
||||||
ret = -EIO;
|
|
||||||
|
|
||||||
chip->select_chip(mtd, -1);
|
|
||||||
}
|
|
||||||
if (!ret)
|
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,13 +324,58 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_default_block_markbad - [DEFAULT] mark a block bad
|
* nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
* @ofs: offset from device start
|
* @ofs: offset from device start
|
||||||
*
|
*
|
||||||
* This is the default implementation, which can be overridden by a hardware
|
* This is the default implementation, which can be overridden by a hardware
|
||||||
* specific driver. We try operations in the following order, according to our
|
* specific driver. It provides the details for writing a bad block marker to a
|
||||||
* bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
|
* block.
|
||||||
|
*/
|
||||||
|
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
uint8_t buf[2] = { 0, 0 };
|
||||||
|
int ret = 0, res, i = 0;
|
||||||
|
|
||||||
|
ops.datbuf = NULL;
|
||||||
|
ops.oobbuf = buf;
|
||||||
|
ops.ooboffs = chip->badblockpos;
|
||||||
|
if (chip->options & NAND_BUSWIDTH_16) {
|
||||||
|
ops.ooboffs &= ~0x01;
|
||||||
|
ops.len = ops.ooblen = 2;
|
||||||
|
} else {
|
||||||
|
ops.len = ops.ooblen = 1;
|
||||||
|
}
|
||||||
|
ops.mode = MTD_OPS_PLACE_OOB;
|
||||||
|
|
||||||
|
/* Write to first/last page(s) if necessary */
|
||||||
|
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||||
|
ofs += mtd->erasesize - mtd->writesize;
|
||||||
|
do {
|
||||||
|
res = nand_do_write_oob(mtd, ofs, &ops);
|
||||||
|
if (!ret)
|
||||||
|
ret = res;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
ofs += mtd->writesize;
|
||||||
|
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_block_markbad_lowlevel - mark a block bad
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @ofs: offset from device start
|
||||||
|
*
|
||||||
|
* This function performs the generic NAND bad block marking steps (i.e., bad
|
||||||
|
* block table(s) and/or marker(s)). We only allow the hardware driver to
|
||||||
|
* specify how to write bad block markers to OOB (chip->block_markbad).
|
||||||
|
*
|
||||||
|
* We try operations in the following order, according to our bbt_options
|
||||||
|
* (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
|
||||||
* (1) erase the affected block, to allow OOB marker to be written cleanly
|
* (1) erase the affected block, to allow OOB marker to be written cleanly
|
||||||
* (2) update in-memory BBT
|
* (2) update in-memory BBT
|
||||||
* (3) write bad block marker to OOB area of affected block
|
* (3) write bad block marker to OOB area of affected block
|
||||||
|
@ -338,11 +383,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||||
* Note that we retain the first error encountered in (3) or (4), finish the
|
* Note that we retain the first error encountered in (3) or (4), finish the
|
||||||
* procedures, and dump the error in the end.
|
* procedures, and dump the error in the end.
|
||||||
*/
|
*/
|
||||||
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
uint8_t buf[2] = { 0, 0 };
|
int block, res, ret = 0;
|
||||||
int block, res, ret = 0, i = 0;
|
|
||||||
int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
|
int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
|
||||||
|
|
||||||
if (write_oob) {
|
if (write_oob) {
|
||||||
|
@ -364,34 +408,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
|
||||||
/* Write bad block marker to OOB */
|
/* Write bad block marker to OOB */
|
||||||
if (write_oob) {
|
if (write_oob) {
|
||||||
struct mtd_oob_ops ops;
|
|
||||||
loff_t wr_ofs = ofs;
|
|
||||||
|
|
||||||
nand_get_device(mtd, FL_WRITING);
|
nand_get_device(mtd, FL_WRITING);
|
||||||
|
ret = chip->block_markbad(mtd, ofs);
|
||||||
ops.datbuf = NULL;
|
|
||||||
ops.oobbuf = buf;
|
|
||||||
ops.ooboffs = chip->badblockpos;
|
|
||||||
if (chip->options & NAND_BUSWIDTH_16) {
|
|
||||||
ops.ooboffs &= ~0x01;
|
|
||||||
ops.len = ops.ooblen = 2;
|
|
||||||
} else {
|
|
||||||
ops.len = ops.ooblen = 1;
|
|
||||||
}
|
|
||||||
ops.mode = MTD_OPS_PLACE_OOB;
|
|
||||||
|
|
||||||
/* Write to first/last page(s) if necessary */
|
|
||||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
|
||||||
wr_ofs += mtd->erasesize - mtd->writesize;
|
|
||||||
do {
|
|
||||||
res = nand_do_write_oob(mtd, wr_ofs, &ops);
|
|
||||||
if (!ret)
|
|
||||||
ret = res;
|
|
||||||
|
|
||||||
i++;
|
|
||||||
wr_ofs += mtd->writesize;
|
|
||||||
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
|
||||||
|
|
||||||
nand_release_device(mtd);
|
nand_release_device(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2683,7 +2701,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
||||||
*/
|
*/
|
||||||
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nand_block_isbad(mtd, ofs);
|
ret = nand_block_isbad(mtd, ofs);
|
||||||
|
@ -2694,7 +2711,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chip->block_markbad(mtd, ofs);
|
return nand_block_markbad_lowlevel(mtd, ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1392,4 +1392,3 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||||
|
|
||||||
EXPORT_SYMBOL(nand_scan_bbt);
|
EXPORT_SYMBOL(nand_scan_bbt);
|
||||||
EXPORT_SYMBOL(nand_default_bbt);
|
EXPORT_SYMBOL(nand_default_bbt);
|
||||||
EXPORT_SYMBOL_GPL(nand_update_bbt);
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct mtd_oob_ops ops;
|
struct mtd_oob_ops ops;
|
||||||
struct sm_oob oob;
|
struct sm_oob oob;
|
||||||
int ret, error = 0;
|
int ret;
|
||||||
|
|
||||||
memset(&oob, -1, SM_OOB_SIZE);
|
memset(&oob, -1, SM_OOB_SIZE);
|
||||||
oob.block_status = 0x0F;
|
oob.block_status = 0x0F;
|
||||||
|
@ -61,11 +61,10 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
"sm_common: can't mark sector at %i as bad\n",
|
"sm_common: can't mark sector at %i as bad\n",
|
||||||
(int)ofs);
|
(int)ofs);
|
||||||
error = -EIO;
|
return -EIO;
|
||||||
} else
|
}
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
return error;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user