]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/mtd/onenand/onenand_base.c
[MTD] [OneNAND] Use pre-alloced oob buffer instead of local buffer
[net-next-2.6.git] / drivers / mtd / onenand / onenand_base.c
index b2c40f67db83544b5672a3d3991271d4e45f8f9b..b281b116aaeb55dab9c8c4e210cba43231392a17 100644 (file)
@@ -169,6 +169,18 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
        return ((bsa << ONENAND_BSA_SHIFT) | bsc);
 }
 
+/**
+ * onenand_get_density - [DEFAULT] Get OneNAND density
+ * @param dev_id       OneNAND device ID
+ *
+ * Get OneNAND density from device ID
+ */
+static inline int onenand_get_density(int dev_id)
+{
+       int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       return (density & ONENAND_DEVICE_DENSITY_MASK);
+}
+
 /**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd          MTD device structure
@@ -182,8 +194,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
 static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
 {
        struct onenand_chip *this = mtd->priv;
-       int value, readcmd = 0, block_cmd = 0;
-       int block, page;
+       int value, block, page;
 
        /* Address translation */
        switch (cmd) {
@@ -198,7 +209,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
        case ONENAND_CMD_ERASE:
        case ONENAND_CMD_BUFFERRAM:
        case ONENAND_CMD_OTP_ACCESS:
-               block_cmd = 1;
                block = (int) (addr >> this->erase_shift);
                page = -1;
                break;
@@ -240,11 +250,9 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
 
-               if (block_cmd) {
-                       /* Select DataRAM for DDP */
-                       value = onenand_bufferram_address(this, block);
-                       this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
-               }
+               /* Select DataRAM for DDP */
+               value = onenand_bufferram_address(this, block);
+               this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
        }
 
        if (page != -1) {
@@ -256,7 +264,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                case ONENAND_CMD_READ:
                case ONENAND_CMD_READOOB:
                        dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
-                       readcmd = 1;
                        break;
 
                default:
@@ -273,12 +280,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                /* Write 'BSA, BSC' of DataRAM */
                value = onenand_buffer_address(dataram, sectors, count);
                this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
-
-               if (readcmd) {
-                       /* Select DataRAM for DDP */
-                       value = onenand_bufferram_address(this, block);
-                       this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
-               }
        }
 
        /* Interrupt clear */
@@ -327,7 +328,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
                printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl);
                if (ctrl & ONENAND_CTRL_LOCK)
                        printk(KERN_ERR "onenand_wait: it's locked error.\n");
-               return ctrl;
+               return -EIO;
        }
 
        if (interrupt & ONENAND_INT_READ) {
@@ -336,7 +337,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
                        if (ecc & ONENAND_ECC_2BIT_ALL) {
                                printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
                                mtd->ecc_stats.failed++;
-                               return ecc;
+                               return -EBADMSG;
                        } else if (ecc & ONENAND_ECC_1BIT_ALL) {
                                printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
                                mtd->ecc_stats.corrected++;
@@ -359,7 +360,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
  */
 static irqreturn_t onenand_interrupt(int irq, void *data)
 {
-       struct onenand_chip *this = (struct onenand_chip *) data;
+       struct onenand_chip *this = data;
 
        /* To handle shared interrupt */
        if (!this->complete.done)
@@ -855,6 +856,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        ret = this->wait(mtd, FL_READING);
                        onenand_update_bufferram(mtd, from, !ret);
+                       if (ret == -EBADMSG)
+                               ret = 0;
                }
        }
 
@@ -913,6 +916,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                /* Now wait for load */
                ret = this->wait(mtd, FL_READING);
                onenand_update_bufferram(mtd, from, !ret);
+               if (ret == -EBADMSG)
+                       ret = 0;
        }
 
        /*
@@ -923,12 +928,12 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        ops->retlen = read;
        ops->oobretlen = oobread;
 
-       if (mtd->ecc_stats.failed - stats.failed)
-               return -EBADMSG;
-
        if (ret)
                return ret;
 
+       if (mtd->ecc_stats.failed - stats.failed)
+               return -EBADMSG;
+
        return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
 }
 
@@ -944,6 +949,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
                        struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
+       struct mtd_ecc_stats stats;
        int read = 0, thislen, column, oobsize;
        size_t len = ops->ooblen;
        mtd_oob_mode_t mode = ops->mode;
@@ -977,6 +983,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
                return -EINVAL;
        }
 
+       stats = mtd->ecc_stats;
+
        while (read < len) {
                cond_resched();
 
@@ -988,18 +996,16 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
                onenand_update_bufferram(mtd, from, 0);
 
                ret = this->wait(mtd, FL_READING);
-               /* First copy data and check return value for ECC handling */
+               if (ret && ret != -EBADMSG) {
+                       printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
+                       break;
+               }
 
                if (mode == MTD_OOB_AUTO)
                        onenand_transfer_auto_oob(mtd, buf, column, thislen);
                else
                        this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 
-               if (ret) {
-                       printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
-                       break;
-               }
-
                read += thislen;
 
                if (read == len)
@@ -1016,7 +1022,14 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
        }
 
        ops->oobretlen = read;
-       return ret;
+
+       if (ret)
+               return ret;
+
+       if (mtd->ecc_stats.failed - stats.failed)
+               return -EBADMSG;
+
+       return 0;
 }
 
 /**
@@ -1106,12 +1119,10 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
        interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
+       /* Initial bad block case: 0x2400 or 0x0400 */
        if (ctrl & ONENAND_CTRL_ERROR) {
                printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
-               /* Initial bad block case */
-               if (ctrl & ONENAND_CTRL_LOAD)
-                       return ONENAND_BBT_READ_ERROR;
-               return ONENAND_BBT_READ_FATAL_ERROR;
+               return ONENAND_BBT_READ_ERROR;
        }
 
        if (interrupt & ONENAND_INT_READ) {
@@ -1206,7 +1217,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
 static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
 {
        struct onenand_chip *this = mtd->priv;
-       char oobbuf[64];
+       u_char *oob_buf = this->oob_buf;
        int status, i;
 
        this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -1215,9 +1226,9 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
        if (status)
                return status;
 
-       this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+       this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
        for (i = 0; i < mtd->oobsize; i++)
-               if (buf[i] != 0xFF && buf[i] != oobbuf[i])
+               if (buf[i] != 0xFF && buf[i] != oob_buf[i])
                        return -EBADMSG;
 
        return 0;
@@ -1419,7 +1430,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                }
 
                /* Only check verify write turn on */
-               ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
+               ret = onenand_verify(mtd, buf, to, thislen);
                if (ret) {
                        printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
                        break;
@@ -1435,9 +1446,6 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                buf += thislen;
        }
 
-       /* Deselect and wake up anyone waiting on the device */
-       onenand_release_device(mtd);
-
        ops->retlen = written;
 
        return ret;
@@ -1711,13 +1719,14 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 erase_exit:
 
        ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-       /* Do call back function */
-       if (!ret)
-               mtd_erase_callback(instr);
 
        /* Deselect and wake up anyone waiting on the device */
        onenand_release_device(mtd);
 
+       /* Do call back function */
+       if (!ret)
+               mtd_erase_callback(instr);
+
        return ret;
 }
 
@@ -1904,7 +1913,12 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
  */
 static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
-       return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
+       int ret;
+
+       onenand_get_device(mtd, FL_LOCKING);
+       ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
+       onenand_release_device(mtd);
+       return ret;
 }
 
 /**
@@ -1917,7 +1931,12 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
  */
 static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
-       return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+       int ret;
+
+       onenand_get_device(mtd, FL_LOCKING);
+       ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+       onenand_release_device(mtd);
+       return ret;
 }
 
 /**
@@ -1979,7 +1998,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
                        loff_t ofs = this->chipsize >> 1;
                        size_t len = mtd->erasesize;
 
-                       onenand_unlock(mtd, ofs, len);
+                       onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
                }
 
                onenand_check_lock_status(this);
@@ -1987,7 +2006,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
                return 0;
        }
 
-       onenand_unlock(mtd, 0x0, this->chipsize);
+       onenand_do_lock_cmd(mtd, 0x0, this->chipsize, ONENAND_CMD_UNLOCK);
 
        return 0;
 }
@@ -2137,7 +2156,7 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 
        *retlen = 0;
 
-       density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       density = onenand_get_density(this->device_id);
        if (density < ONENAND_DEVICE_DENSITY_512Mb)
                otp_pages = 20;
        else
@@ -2288,7 +2307,8 @@ static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
 static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
                        size_t len)
 {
-       unsigned char oob_buf[64];
+       struct onenand_chip *this = mtd->priv;
+       u_char *oob_buf = this->oob_buf;
        size_t retlen;
        int ret;
 
@@ -2328,7 +2348,7 @@ static void onenand_check_features(struct mtd_info *mtd)
        unsigned int density, process;
 
        /* Lock scheme depends on density and process */
-       density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       density = onenand_get_density(this->device_id);
        process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
 
        /* Lock scheme */
@@ -2377,7 +2397,7 @@ static void onenand_print_device_info(int device, int version)
         vcc = device & ONENAND_DEVICE_VCC_MASK;
         demuxed = device & ONENAND_DEVICE_IS_DEMUX;
         ddp = device & ONENAND_DEVICE_IS_DDP;
-        density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
+        density = onenand_get_density(device);
         printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
                 demuxed ? "" : "Muxed ",
                 ddp ? "(DDP)" : "",
@@ -2469,7 +2489,7 @@ static int onenand_probe(struct mtd_info *mtd)
        this->device_id = dev_id;
        this->version_id = ver_id;
 
-       density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       density = onenand_get_density(dev_id);
        this->chipsize = (16 << density) << 20;
        /* Set density mask. it is used for DDP */
        if (ONENAND_IS_DDP(this))