Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
NAND: Add support for subpage reads for NAND_ECC_HW_OOB_FIRST
  • Loading branch information
larsclausen authored and wpwrak committed Mar 31, 2013
1 parent 4c3d5c1 commit 5d4ea6e
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 6 deletions.
82 changes: 77 additions & 5 deletions drivers/mtd/nand/nand_base.c
Expand Up @@ -1135,7 +1135,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @bufpoi: buffer to store read data
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
Expand Down Expand Up @@ -1320,6 +1320,75 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
return max_bitflips;
}

/**
* nand_read_subpage_hwecc_oob_first - [REPLACABLE] hw ecc based sub-page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @data_offs: offset of requested data within the page
* @readlen: data length
* @bufpoi: buffer to store read data
* @page: page number to read
*
* Hardware ECC for large page chips, require OOB to be read first.
* For this ECC mode, the write_page method is re-used from ECC_HW.
* These methods read/write ECC from the OOB area, unlike the
* ECC_HW_SYNDROME support with multiple ECC steps, follows the
* "infix ECC" scheme and reads/writes ECC from the data area, by
* overwriting the NAND manufacturer bad block markings.
*/
static int nand_read_subpage_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint8_t *p;
int data_col_addr;
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint8_t *ecc_code = chip->buffers->ecccode;
uint8_t *ecc_calc = chip->buffers->ecccalc;
int i;

/* Column address wihin the page aligned to ECC size */
start_step = data_offs / chip->ecc.size;
end_step = (data_offs + readlen - 1) / chip->ecc.size;
num_steps = end_step - start_step + 1;

data_col_addr = start_step * chip->ecc.size;

/* Read the OOB area first */
if (mtd->writesize > 512) {
chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
} else {
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_READ0, data_col_addr, page);
}

for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];

p = bufpoi + data_col_addr;

for (i = eccbytes * start_step; num_steps; num_steps--, i += eccbytes, p += eccsize) {
int stat;

chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);

stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}

return 0;
}

/**
* nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
* @mtd: mtd info structure
Expand Down Expand Up @@ -1485,7 +1554,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi);
col, bytes, bufpoi, page);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
oob_required, page);
Expand Down Expand Up @@ -3457,8 +3526,11 @@ int nand_scan_tail(struct mtd_info *mtd)
"hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
if (!chip->ecc.read_page) {
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
if (!chip->ecc.read_subpage)
chip->ecc.read_subpage = nand_read_subpage_hwecc_oob_first;
}

case NAND_ECC_HW:
/* Use standard hwecc read page function? */
Expand Down Expand Up @@ -3633,8 +3705,8 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Invalidate the pagebuffer reference */
chip->pagebuf = -1;

/* Large page NAND with SOFT_ECC should support subpage reads */
if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
/* Large page NAND with read_subpage should support subpage reads */
if (chip->ecc.read_subpage && chip->page_shift > 9)
chip->options |= NAND_SUBPAGE_READ;

/* Fill in remaining MTD driver data */
Expand Down
2 changes: 1 addition & 1 deletion include/linux/mtd/nand.h
Expand Up @@ -391,7 +391,7 @@ struct nand_ecc_ctrl {
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf);
uint32_t offs, uint32_t len, uint8_t *buf, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required);
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
Expand Down

0 comments on commit 5d4ea6e

Please sign in to comment.