mtd: nand: fix bug writing 1 byte less than page size
[pandora-kernel.git] / drivers / mtd / nand / nand_base.c
index 7f2691f..89b8583 100644 (file)
@@ -406,6 +406,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        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);
 
                /*
@@ -414,13 +416,13 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
                 * 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;
@@ -1382,12 +1384,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
 {
        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;
@@ -1437,7 +1439,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        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;
@@ -1469,7 +1471,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                        }
 
                        /* 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)
@@ -1478,14 +1480,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                        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);
                        }
 
@@ -1573,6 +1583,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
                     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 */
@@ -1583,13 +1594,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        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);
 
@@ -1759,7 +1771,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 
        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;
@@ -1787,7 +1799,10 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        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);
@@ -1862,9 +1877,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
        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:
@@ -2110,12 +2125,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
 
        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;
@@ -2164,7 +2179,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        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;
@@ -2214,7 +2229,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                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;
@@ -2233,7 +2248,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                }
 
                ret = chip->write_page(mtd, chip, wbuf, page, cached,
-                                      (ops->mode == MTD_OOB_RAW));
+                                      (ops->mode == MTD_OPS_RAW));
                if (ret)
                        break;
 
@@ -2275,6 +2290,7 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
                            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 */
@@ -2289,13 +2305,14 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
        /* 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;
 }
 
@@ -2313,6 +2330,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
                          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 */
@@ -2323,13 +2341,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        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);
 
@@ -2353,7 +2372,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
        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;
@@ -2404,7 +2423,11 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
                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;
@@ -2438,9 +2461,9 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
        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:
@@ -2872,17 +2895,27 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        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;
 }
@@ -3046,9 +3079,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                        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
@@ -3380,6 +3412,12 @@ int nand_scan_tail(struct mtd_info *mtd)
                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.