if (chip->bbt_options & NAND_BBT_USE_FLASH)
ret = nand_update_bbt(mtd, ofs);
else {
+ struct mtd_oob_ops ops;
+
nand_get_device(chip, mtd, FL_WRITING);
/*
* procedure. We write two bytes per location, so we dont have
* to mess with 16 bit access.
*/
+ ops.len = ops.ooblen = 2;
+ ops.datbuf = NULL;
+ ops.oobbuf = buf;
+ ops.ooboffs = chip->badblockpos & ~0x01;
+ ops.mode = MTD_OPS_PLACE_OOB;
do {
- chip->ops.len = chip->ops.ooblen = 2;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = buf;
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
-
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ ret = nand_do_write_oob(mtd, ofs, &ops);
i++;
ofs += mtd->writesize;
{
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
- uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
+ uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf;
}
/* Now read the page into the buffer */
- if (unlikely(ops->mode == MTD_OOB_RAW))
+ if (unlikely(ops->mode == MTD_OPS_RAW))
ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
page);
- if (ret < 0)
+ if (ret < 0) {
+ if (!aligned)
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
break;
+ }
/* Transfer not aligned data */
if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob &&
- !(mtd->ecc_stats.failed - stats.failed))
+ !(mtd->ecc_stats.failed - stats.failed) &&
+ (ops->mode != MTD_OPS_RAW))
chip->pagebuf = realpage;
+ else
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
size_t *retlen, uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
nand_get_device(chip, mtd, FL_READING);
- chip->ops.len = len;
- chip->ops.datbuf = buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_read_ops(mtd, from, &chip->ops);
+ ret = nand_do_read_ops(mtd, from, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
nand_release_device(mtd);
stats = mtd->ecc_stats;
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
page = realpage & chip->pagemask;
while (1) {
- sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
+ if (ops->mode == MTD_OPS_RAW)
+ sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd);
+ else
+ sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
len = min(len, readlen);
buf = nand_transfer_oob(chip, buf, ops, len);
nand_get_device(chip, mtd, FL_READING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen;
- uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
+ uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *oob = ops->oobbuf;
uint8_t *wbuf = buf;
/* Partial page write? */
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
+ if (unlikely(column || writelen < mtd->writesize)) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
chip->pagebuf = -1;
}
ret = chip->write_page(mtd, chip, wbuf, page, cached,
- (ops->mode == MTD_OOB_RAW));
+ (ops->mode == MTD_OPS_RAW));
if (ret)
break;
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
/* Grab the device */
panic_nand_get_device(chip, mtd, FL_WRITING);
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_write_ops(mtd, to, &chip->ops);
+ ret = nand_do_write_ops(mtd, to, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
return ret;
}
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
/* Do not allow reads past end of device */
nand_get_device(chip, mtd, FL_WRITING);
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ops.mode = 0;
- ret = nand_do_write_ops(mtd, to, &chip->ops);
+ ret = nand_do_write_ops(mtd, to, &ops);
- *retlen = chip->ops.retlen;
+ *retlen = ops.retlen;
nand_release_device(mtd);
pr_debug("%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)ops->ooblen);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
chip->pagebuf = -1;
nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
if (status)
return status;
nand_get_device(chip, mtd, FL_WRITING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
sanitize_string(p->model, sizeof(p->model));
if (!mtd->name)
mtd->name = p->model;
+
mtd->writesize = le32_to_cpu(p->byte_per_page);
- mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+
+ /*
+ * pages_per_block and blocks_per_lun may not be a power-of-2 size
+ * (don't ask me who thought of this...). MTD assumes that these
+ * dimensions will be power-of-2, so just truncate the remaining area.
+ */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
- chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+
+ /* See erasesize comment */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize;
*busw = 0;
if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16;
- chip->options &= ~NAND_CHIPOPTIONS_MSK;
- chip->options |= (NAND_NO_READRDY |
- NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+ chip->options |= NAND_NO_READRDY | NAND_NO_AUTOINCR;
return 1;
}
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
}
- /* Get chip options, preserve non chip based options */
- chip->options &= ~NAND_CHIPOPTIONS_MSK;
- chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+ /* Get chip options */
+ chip->options |= type->options;
/*
* Check if chip is not a Samsung device. Do not clear the
BUG();
}
+ /* For many systems, the standard OOB write also works for raw */
+ if (!chip->ecc.read_oob_raw)
+ chip->ecc.read_oob_raw = chip->ecc.read_oob;
+ if (!chip->ecc.write_oob_raw)
+ chip->ecc.write_oob_raw = chip->ecc.write_oob;
+
/*
* The number of bytes available for a client to place data into
* the out of band area.