|
@@ -1110,7 +1110,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function
|
|
|
+ * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
|
|
|
* @mtd: mtd info structure
|
|
|
* @chip: nand chip info structure
|
|
|
* @data_offs: offset of requested data within the page
|
|
@@ -1978,6 +1978,67 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
|
|
|
+ * @mtd: mtd info structure
|
|
|
+ * @chip: nand chip info structure
|
|
|
+ * @column: column address of subpage within the page
|
|
|
+ * @data_len: data length
|
|
|
+ * @oob_required: must write chip->oob_poi to OOB
|
|
|
+ */
|
|
|
+static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
|
|
+ struct nand_chip *chip, uint32_t offset,
|
|
|
+ uint32_t data_len, const uint8_t *data_buf,
|
|
|
+ int oob_required)
|
|
|
+{
|
|
|
+ uint8_t *oob_buf = chip->oob_poi;
|
|
|
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
|
+ int ecc_size = chip->ecc.size;
|
|
|
+ int ecc_bytes = chip->ecc.bytes;
|
|
|
+ int ecc_steps = chip->ecc.steps;
|
|
|
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
+ uint32_t start_step = offset / ecc_size;
|
|
|
+ uint32_t end_step = (offset + data_len - 1) / ecc_size;
|
|
|
+ int oob_bytes = mtd->oobsize / ecc_steps;
|
|
|
+ int step, i;
|
|
|
+
|
|
|
+ for (step = 0; step < ecc_steps; step++) {
|
|
|
+ /* configure controller for WRITE access */
|
|
|
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
|
+
|
|
|
+ /* write data (untouched subpages already masked by 0xFF) */
|
|
|
+ chip->write_buf(mtd, data_buf, ecc_size);
|
|
|
+
|
|
|
+ /* mask ECC of un-touched subpages by padding 0xFF */
|
|
|
+ if ((step < start_step) || (step > end_step))
|
|
|
+ memset(ecc_calc, 0xff, ecc_bytes);
|
|
|
+ else
|
|
|
+ chip->ecc.calculate(mtd, data_buf, ecc_calc);
|
|
|
+
|
|
|
+ /* mask OOB of un-touched subpages by padding 0xFF */
|
|
|
+ /* if oob_required, preserve OOB metadata of written subpage */
|
|
|
+ if (!oob_required || (step < start_step) || (step > end_step))
|
|
|
+ memset(oob_buf, 0xff, oob_bytes);
|
|
|
+
|
|
|
+ data_buf += ecc_size;
|
|
|
+ ecc_calc += ecc_bytes;
|
|
|
+ oob_buf += oob_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* copy calculated ECC for whole page to chip->buffer->oob */
|
|
|
+ /* this include masked-value(0xFF) for unwritten subpages */
|
|
|
+ ecc_calc = chip->buffers->ecccalc;
|
|
|
+ for (i = 0; i < chip->ecc.total; i++)
|
|
|
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
+
|
|
|
+ /* write OOB buffer to NAND device */
|
|
|
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
|
|
|
* @mtd: mtd info structure
|
|
@@ -2030,6 +2091,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
|
|
* nand_write_page - [REPLACEABLE] write one page
|
|
|
* @mtd: MTD device structure
|
|
|
* @chip: NAND chip descriptor
|
|
|
+ * @offset: address offset within the page
|
|
|
+ * @data_len: length of actual data to be written
|
|
|
* @buf: the data to write
|
|
|
* @oob_required: must write chip->oob_poi to OOB
|
|
|
* @page: page number to write
|
|
@@ -2037,15 +2100,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
|
|
* @raw: use _raw version of write_page
|
|
|
*/
|
|
|
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- const uint8_t *buf, int oob_required, int page,
|
|
|
- int cached, int raw)
|
|
|
+ uint32_t offset, int data_len, const uint8_t *buf,
|
|
|
+ int oob_required, int page, int cached, int raw)
|
|
|
{
|
|
|
- int status;
|
|
|
+ int status, subpage;
|
|
|
+
|
|
|
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
|
|
|
+ chip->ecc.write_subpage)
|
|
|
+ subpage = offset || (data_len < mtd->writesize);
|
|
|
+ else
|
|
|
+ subpage = 0;
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
|
|
|
|
|
if (unlikely(raw))
|
|
|
- status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
|
|
|
+ status = chip->ecc.write_page_raw(mtd, chip, buf,
|
|
|
+ oob_required);
|
|
|
+ else if (subpage)
|
|
|
+ status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
|
|
|
+ buf, oob_required);
|
|
|
else
|
|
|
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
|
|
|
|
|
@@ -2159,7 +2232,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
|
|
|
|
uint8_t *oob = ops->oobbuf;
|
|
|
uint8_t *buf = ops->datbuf;
|
|
|
- int ret, subpage;
|
|
|
+ int ret;
|
|
|
int oob_required = oob ? 1 : 0;
|
|
|
|
|
|
ops->retlen = 0;
|
|
@@ -2174,10 +2247,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
|
}
|
|
|
|
|
|
column = to & (mtd->writesize - 1);
|
|
|
- subpage = column || (writelen & (mtd->writesize - 1));
|
|
|
-
|
|
|
- if (subpage && oob)
|
|
|
- return -EINVAL;
|
|
|
|
|
|
chipnr = (int)(to >> chip->chip_shift);
|
|
|
chip->select_chip(mtd, chipnr);
|
|
@@ -2226,9 +2295,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
|
/* We still need to erase leftover OOB data */
|
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
|
}
|
|
|
-
|
|
|
- ret = chip->write_page(mtd, chip, wbuf, oob_required, page,
|
|
|
- cached, (ops->mode == MTD_OPS_RAW));
|
|
|
+ ret = chip->write_page(mtd, chip, column, bytes, wbuf,
|
|
|
+ oob_required, page, cached,
|
|
|
+ (ops->mode == MTD_OPS_RAW));
|
|
|
if (ret)
|
|
|
break;
|
|
|
|
|
@@ -3414,6 +3483,10 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
|
if (!chip->ecc.write_oob)
|
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
|
|
+ if (!chip->ecc.read_subpage)
|
|
|
+ chip->ecc.read_subpage = nand_read_subpage;
|
|
|
+ if (!chip->ecc.write_subpage)
|
|
|
+ chip->ecc.write_subpage = nand_write_subpage_hwecc;
|
|
|
|
|
|
case NAND_ECC_HW_SYNDROME:
|
|
|
if ((!chip->ecc.calculate || !chip->ecc.correct ||
|