]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mtd/onenand/onenand_base.c
Merge branch 'upstream'
[linux-2.6-omap-h63xx.git] / drivers / mtd / onenand / onenand_base.c
index cc38fa0d45c6910747d77734226aa568f72b761f..a53a73fc2a5af0d0a07a8f880793e2b8c37c4489 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>
@@ -938,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[MAX_ONENAND_PAGESIZE], *pbuf;
+       unsigned char *pbuf;
        size_t total_len, len;
        int i, written = 0;
        int ret = 0;
@@ -973,7 +975,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
        /* Loop until all keve's data has been written */
        len = 0;
        while (count) {
-               pbuf = buffer;
+               pbuf = this->page_buf;
                /*
                 * If the given tuple is >= pagesize then
                 * write it out from the iov
@@ -993,7 +995,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
                        int cnt = 0, thislen;
                        while (cnt < mtd->oobblock) {
                                thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len);
-                               memcpy(buffer + cnt, vecs->iov_base + len, thislen);
+                               memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen);
                                cnt += thislen;
                                len += thislen;
 
@@ -1294,6 +1296,12 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 
        /* Block lock scheme */
        for (block = start; block < end; block++) {
+               /* Set block address */
+               value = onenand_block_address(this, block);
+               this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+               /* Select DataRAM for DDP */
+               value = onenand_bufferram_address(this, block);
+               this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
                /* Set start block address */
                this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
                /* Write unlock command */
@@ -1307,10 +1315,6 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
                    & ONENAND_CTRL_ONGO)
                        continue;
 
-               /* Set block address for read block status */
-               value = onenand_block_address(this, block);
-               this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
-
                /* Check lock status */
                status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
                if (!(status & ONENAND_WP_US))
@@ -1344,7 +1348,6 @@ static void onenand_print_device_info(int device)
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
         {ONENAND_MFR_SAMSUNG, "Samsung"},
-        {ONENAND_MFR_UNKNOWN, "Unknown"}
 };
 
 /**
@@ -1355,17 +1358,22 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = {
  */
 static int onenand_check_maf(int manuf)
 {
+       int size = ARRAY_SIZE(onenand_manuf_ids);
+       char *name;
         int i;
 
-        for (i = 0; onenand_manuf_ids[i].id; i++) {
+       for (i = 0; i < size; i++)
                 if (manuf == onenand_manuf_ids[i].id)
                         break;
-        }
 
-        printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n",
-                onenand_manuf_ids[i].name, manuf);
+       if (i < size)
+               name = onenand_manuf_ids[i].name;
+       else
+               name = "Unknown";
+
+       printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
 
-        return (i != ONENAND_MFR_UNKNOWN);
+       return (i == size);
 }
 
 /**
@@ -1511,6 +1519,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
                this->read_bufferram = onenand_sync_read_bufferram;
        }
 
+       /* Allocate buffers, if necessary */
+       if (!this->page_buf) {
+               size_t len;
+               len = mtd->oobblock + mtd->oobsize;
+               this->page_buf = kmalloc(len, GFP_KERNEL);
+               if (!this->page_buf) {
+                       printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
+                       return -ENOMEM;
+               }
+               this->options |= ONENAND_PAGEBUF_ALLOC;
+       }
+
        this->state = FL_READY;
        init_waitqueue_head(&this->wq);
        spin_lock_init(&this->chip_lock);
@@ -1572,12 +1592,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
  */
 void onenand_release(struct mtd_info *mtd)
 {
+       struct onenand_chip *this = mtd->priv;
+
 #ifdef CONFIG_MTD_PARTITIONS
        /* Deregister partitions */
        del_mtd_partitions (mtd);
 #endif
        /* Deregister the device */
        del_mtd_device (mtd);
+
+       /* Free bad block table memory, if allocated */
+       if (this->bbm)
+               kfree(this->bbm);
+       /* Buffer allocated by onenand_scan */
+       if (this->options & ONENAND_PAGEBUF_ALLOC)
+               kfree(this->page_buf);
 }
 
 EXPORT_SYMBOL_GPL(onenand_scan);