]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mtd/onenand/onenand_base.c
Merge branch 'master'
[linux-2.6-omap-h63xx.git] / drivers / mtd / onenand / onenand_base.c
index bcce22ae3cb1536e217e8a06fd349db06ae40b10..f67d5d6eb9a68aa1f290b4e580429c13017cd5c1 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/partitions.h>
@@ -84,25 +86,23 @@ static void onenand_writew(unsigned short value, void __iomem *addr)
 
 /**
  * onenand_block_address - [DEFAULT] Get block address
- * @param device       the device id
+ * @param this         onenand chip data structure
  * @param block                the block
  * @return             translated block address if DDP, otherwise same
  *
  * Setup Start Address 1 Register (F100h)
  */
-static int onenand_block_address(int device, int block)
+static int onenand_block_address(struct onenand_chip *this, int block)
 {
-       if (device & ONENAND_DEVICE_IS_DDP) {
+       if (this->device_id & ONENAND_DEVICE_IS_DDP) {
                /* Device Flash Core select, NAND Flash Block Address */
-               int dfs = 0, density, mask;
+               int dfs = 0;
 
-               density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-               mask = (1 << (density + 6));
-
-               if (block & mask)
+               if (block & this->density_mask)
                        dfs = 1;
 
-               return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1));
+               return (dfs << ONENAND_DDP_SHIFT) |
+                       (block & (this->density_mask - 1));
        }
 
        return block;
@@ -110,22 +110,19 @@ static int onenand_block_address(int device, int block)
 
 /**
  * onenand_bufferram_address - [DEFAULT] Get bufferram address
- * @param device       the device id
+ * @param this         onenand chip data structure
  * @param block                the block
  * @return             set DBS value if DDP, otherwise 0
  *
  * Setup Start Address 2 Register (F101h) for DDP
  */
-static int onenand_bufferram_address(int device, int block)
+static int onenand_bufferram_address(struct onenand_chip *this, int block)
 {
-       if (device & ONENAND_DEVICE_IS_DDP) {
+       if (this->device_id & ONENAND_DEVICE_IS_DDP) {
                /* Device BufferRAM Select */
-               int dbs = 0, density, mask;
-
-               density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-               mask = (1 << (density + 6));
+               int dbs = 0;
 
-               if (block & mask)
+               if (block & this->density_mask)
                        dbs = 1;
 
                return (dbs << ONENAND_DDP_SHIFT);
@@ -223,7 +220,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
        /* NOTE: The setting order of the registers is very important! */
        if (cmd == ONENAND_CMD_BUFFERRAM) {
                /* Select DataRAM for DDP */
-               value = onenand_bufferram_address(this->device_id, block);
+               value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
                /* Switch to the next data buffer */
@@ -234,7 +231,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 
        if (block != -1) {
                /* Write 'DFS, FBA' of Flash */
-               value = onenand_block_address(this->device_id, block);
+               value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
        }
 
@@ -260,10 +257,10 @@ 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->device_id, block);
+                       value = onenand_bufferram_address(this, block);
                        this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
                }
        }
@@ -311,19 +308,21 @@ static int onenand_wait(struct mtd_info *mtd, int state)
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
        if (ctrl & ONENAND_CTRL_ERROR) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl);
-               return -EIO;
+               /* It maybe occur at initial bad block */
+               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
+               /* Clear other interrupt bits for preventing ECC error */
+               interrupt &= ONENAND_INT_MASTER;
        }
 
        if (ctrl & ONENAND_CTRL_LOCK) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl);
-               return -EIO;
+               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
+               return -EACCES;
        }
 
        if (interrupt & ONENAND_INT_READ) {
                ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
                if (ecc & ONENAND_ECC_2BIT_ALL) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc);
+                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
                        return -EBADMSG;
                }
        }
@@ -378,6 +377,35 @@ static int onenand_read_bufferram(struct mtd_info *mtd, int area,
        return 0;
 }
 
+/**
+ * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
+ * @param mtd          MTD data structure
+ * @param area         BufferRAM area
+ * @param buffer       the databuffer to put/get data
+ * @param offset       offset to read from or write to
+ * @param count                number of bytes to read/write
+ *
+ * Read the BufferRAM area with Sync. Burst Mode
+ */
+static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
+               unsigned char *buffer, int offset, size_t count)
+{
+       struct onenand_chip *this = mtd->priv;
+       void __iomem *bufferram;
+
+       bufferram = this->base + area;
+
+       bufferram += onenand_bufferram_offset(mtd, area);
+
+       this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
+
+       memcpy(buffer, bufferram + offset, count);
+
+       this->mmcontrol(mtd, 0);
+
+       return 0;
+}
+
 /**
  * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
  * @param mtd          MTD data structure
@@ -407,7 +435,7 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
  * @param mtd          MTD data structure
  * @param addr         address to check
- * @return             1 if there are valid data, otherwise 0 
+ * @return             1 if there are valid data, otherwise 0
  *
  * Check bufferram if there is data we required
  */
@@ -416,7 +444,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
        struct onenand_chip *this = mtd->priv;
        int block, page;
        int i;
-       
+
        block = (int) (addr >> this->erase_shift);
        page = (int) (addr >> this->page_shift);
        page &= this->page_mask;
@@ -446,7 +474,7 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
        struct onenand_chip *this = mtd->priv;
        int block, page;
        int i;
-       
+
        block = (int) (addr >> this->erase_shift);
        page = (int) (addr >> this->page_shift);
        page &= this->page_mask;
@@ -474,7 +502,7 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
  *
  * Get the device and lock it for exclusive access
  */
-static void onenand_get_device(struct mtd_info *mtd, int new_state)
+static int onenand_get_device(struct mtd_info *mtd, int new_state)
 {
        struct onenand_chip *this = mtd->priv;
        DECLARE_WAITQUEUE(wait, current);
@@ -489,12 +517,18 @@ static void onenand_get_device(struct mtd_info *mtd, int new_state)
                        spin_unlock(&this->chip_lock);
                        break;
                }
+               if (new_state == FL_PM_SUSPENDED) {
+                       spin_unlock(&this->chip_lock);
+                       return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
+               }
                set_current_state(TASK_UNINTERRUPTIBLE);
                add_wait_queue(&this->wq, &wait);
                spin_unlock(&this->chip_lock);
                schedule();
                remove_wait_queue(&this->wq, &wait);
        }
+
+       return 0;
 }
 
 /**
@@ -688,13 +722,10 @@ out:
  * onenand_verify_page - [GENERIC] verify the chip contents after a write
  * @param mtd          MTD device structure
  * @param buf          the databuffer to verify
- * @param block                block address
- * @param page         page address
  *
  * Check DataRAM area directly
  */
-static int onenand_verify_page(struct mtd_info *mtd, u_char *buf,
-       loff_t addr, int block, int page)
+static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 {
        struct onenand_chip *this = mtd->priv;
        void __iomem *dataram0, *dataram1;
@@ -714,7 +745,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf,
 
        if (memcmp(dataram0, dataram1, mtd->oobblock))
                return -EBADMSG;
-       
+
        return 0;
 }
 #else
@@ -785,7 +816,7 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
                written += thislen;
 
                /* Only check verify write turn on */
-               ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page);
+               ret = onenand_verify_page(mtd, (u_char *) buf, to);
                if (ret) {
                        DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret);
                        goto out;
@@ -803,7 +834,7 @@ out:
        onenand_release_device(mtd);
 
        *retlen = written;
-       
+
        return ret;
 }
 
@@ -888,7 +919,7 @@ out:
        onenand_release_device(mtd);
 
        *retlen = written;
-       
+
        return 0;
 }
 
@@ -909,7 +940,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
        u_char *eccbuf, struct nand_oobinfo *oobsel)
 {
        struct onenand_chip *this = mtd->priv;
-       unsigned char buffer[mtd->oobblock], *pbuf;
+       unsigned char buffer[MAX_ONENAND_PAGESIZE], *pbuf;
        size_t total_len, len;
        int i, written = 0;
        int ret = 0;
@@ -940,12 +971,12 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
        onenand_get_device(mtd, FL_WRITING);
 
        /* TODO handling oob */
-       
+
        /* Loop until all keve's data has been written */
        len = 0;
        while (count) {
                pbuf = buffer;
-               /* 
+               /*
                 * If the given tuple is >= pagesize then
                 * write it out from the iov
                 */
@@ -994,7 +1025,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
 
 
                /* Only check verify write turn on */
-               ret = onenand_verify_page(mtd, (u_char *) pbuf, to, block, page);
+               ret = onenand_verify_page(mtd, (u_char *) pbuf, to);
                if (ret) {
                        DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret);
                        goto out;
@@ -1030,6 +1061,25 @@ static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs,
        return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
 }
 
+/**
+ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset from device start
+ * @param getchip      0, if the chip is already selected
+ * @param allowbbt     1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+
+       /* Return info from the table */
+       return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
 /**
  * onenand_erase - [MTD Interface] erase block(s)
  * @param mtd          MTD device structure
@@ -1080,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
        while (len) {
 
-               /* TODO Check badblock */
+               /* Check if we have a bad block, we do not erase bad blocks */
+               if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+                       printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
+                       instr->state = MTD_ERASE_FAILED;
+                       goto erase_exit;
+               }
 
                this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
 
@@ -1132,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd)
        onenand_release_device(mtd);
 }
 
+
 /**
  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
  * @param mtd          MTD device structure
  * @param ofs          offset relative to mtd start
+ *
+ * Check whether the block is bad
  */
 static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
-       /*
-        * TODO 
-        * 1. Bad block table (BBT)
-        *   -> using NAND BBT to support JFFS2
-        * 2. Bad block management (BBM)
-        *   -> bad block replace scheme
-        *
-        * Currently we do nothing
-        */
-       return 0;
+       /* Check for invalid offset */
+       if (ofs > mtd->size)
+               return -EINVAL;
+
+       return onenand_block_checkbad(mtd, ofs, 1, 0);
+}
+
+/**
+ * onenand_default_block_markbad - [DEFAULT] mark a block bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       u_char buf[2] = {0, 0};
+       size_t retlen;
+       int block;
+
+       /* Get block number */
+       block = ((int) ofs) >> bbm->bbt_erase_shift;
+        if (bbm->bbt)
+                bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+        /* We write two bytes, so we dont have to mess with 16 bit access */
+        ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
+        return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
 }
 
 /**
  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
  * @param mtd          MTD device structure
  * @param ofs          offset relative to mtd start
+ *
+ * Mark the block as bad
  */
 static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
-       /* see above */
-       return 0;
+       struct onenand_chip *this = mtd->priv;
+       int ret;
+
+       ret = onenand_block_isbad(mtd, ofs);
+       if (ret) {
+               /* If it was bad already, return success and do nothing */
+               if (ret > 0)
+                       return 0;
+               return ret;
+       }
+
+       return this->block_markbad(mtd, ofs);
 }
 
 /**
@@ -1219,7 +1310,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
                        continue;
 
                /* Set block address for read block status */
-               value = onenand_block_address(this->device_id, block);
+               value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
 
                /* Check lock status */
@@ -1227,7 +1318,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
                if (!(status & ONENAND_WP_US))
                        printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
        }
-       
+
        return 0;
 }
 
@@ -1273,8 +1364,8 @@ static int onenand_check_maf(int manuf)
                         break;
         }
 
-        printk(KERN_DEBUG "OneNAND Manufacturer: %s\n",
-                onenand_manuf_ids[i].name);
+        printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n",
+                onenand_manuf_ids[i].name, manuf);
 
         return (i != ONENAND_MFR_UNKNOWN);
 }
@@ -1321,6 +1412,8 @@ static int onenand_probe(struct mtd_info *mtd)
 
        density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
        this->chipsize = (16 << density) << 20;
+       /* Set density mask. it is used for DDP */
+       this->density_mask = (1 << (density + 6));
 
        /* OneNAND page size & block size */
        /* The data buffer size is equal to page size */
@@ -1348,10 +1441,34 @@ static int onenand_probe(struct mtd_info *mtd)
                printk(KERN_INFO "Lock scheme is Continues Lock\n");
                this->options |= ONENAND_CONT_LOCK;
        }
-       
+
        return 0;
 }
 
+/**
+ * onenand_suspend - [MTD Interface] Suspend the OneNAND flash
+ * @param mtd          MTD device structure
+ */
+static int onenand_suspend(struct mtd_info *mtd)
+{
+       return onenand_get_device(mtd, FL_PM_SUSPENDED);
+}
+
+/**
+ * onenand_resume - [MTD Interface] Resume the OneNAND flash
+ * @param mtd          MTD device structure
+ */
+static void onenand_resume(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+
+       if (this->state == FL_PM_SUSPENDED)
+               onenand_release_device(mtd);
+       else
+               printk(KERN_ERR "resume() called for the chip which is not"
+                               "in suspended state\n");
+}
+
 
 /**
  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
@@ -1382,9 +1499,20 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        if (!this->write_bufferram)
                this->write_bufferram = onenand_write_bufferram;
 
+       if (!this->block_markbad)
+               this->block_markbad = onenand_default_block_markbad;
+       if (!this->scan_bbt)
+               this->scan_bbt = onenand_default_bbt;
+
        if (onenand_probe(mtd))
                return -ENXIO;
 
+       /* Set Sync. Burst Read after probing */
+       if (this->mmcontrol) {
+               printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
+               this->read_bufferram = onenand_sync_read_bufferram;
+       }
+
        this->state = FL_READY;
        init_waitqueue_head(&this->wq);
        spin_lock_init(&this->chip_lock);
@@ -1407,7 +1535,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        }
 
        memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
-       
+
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
@@ -1428,8 +1556,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->sync = onenand_sync;
        mtd->lock = NULL;
        mtd->unlock = onenand_unlock;
-       mtd->suspend = NULL;
-       mtd->resume = NULL;
+       mtd->suspend = onenand_suspend;
+       mtd->resume = onenand_resume;
        mtd->block_isbad = onenand_block_isbad;
        mtd->block_markbad = onenand_block_markbad;
        mtd->owner = THIS_MODULE;
@@ -1437,7 +1565,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        /* Unlock whole block */
        mtd->unlock(mtd, 0x0, this->chipsize);
 
-       return 0;
+       return this->scan_bbt(mtd);
 }
 
 /**