[MTD ONENAND] Check OneNAND lock scheme & all block unlock command support
authorKyungmin Park <kyungmin.park@samsung.com>
Tue, 26 Sep 2006 09:45:28 +0000 (09:45 +0000)
committerDavid Woodhouse <dwmw2@infradead.org>
Tue, 26 Sep 2006 15:46:28 +0000 (16:46 +0100)
OneNAND lock scheme depends on density and process of chip.
Some OneNAND chips support all block unlock

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
drivers/mtd/onenand/onenand_base.c
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h

index 09aefe2..8ed68b2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  linux/drivers/mtd/onenand/onenand_base.c
  *
- *  Copyright (C) 2005 Samsung Electronics
+ *  Copyright (C) 2005-2006 Samsung Electronics
  *  Kyungmin Park <kyungmin.park@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
        case ONENAND_CMD_UNLOCK:
        case ONENAND_CMD_LOCK:
        case ONENAND_CMD_LOCK_TIGHT:
+       case ONENAND_CMD_UNLOCK_ALL:
                block = -1;
                page = -1;
                break;
@@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        end = len >> this->erase_shift;
 
        /* Continuous lock scheme */
-       if (this->options & ONENAND_CONT_LOCK) {
+       if (this->options & ONENAND_HAS_CONT_LOCK) {
                /* Set start block address */
                this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
                /* Set end block address */
-               this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+               this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
                /* Write unlock command */
                this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
 
@@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        }
 
        /* Block lock scheme */
-       for (block = start; block < end; block++) {
+       for (block = start; block < start + end; block++) {
                /* Set block address */
                value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
@@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        return 0;
 }
 
+/**
+ * onenand_check_lock_status - [OneNAND Interface] Check lock status
+ * @param this         onenand chip data structure
+ *
+ * Check lock status
+ */
+static void onenand_check_lock_status(struct onenand_chip *this)
+{
+       unsigned int value, block, status;
+       unsigned int end;
+
+       end = this->chipsize >> this->erase_shift;
+       for (block = 0; 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);
+
+               /* Check lock status */
+               status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+               if (!(status & ONENAND_WP_US))
+                       printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
+       }
+}
+
+/**
+ * onenand_unlock_all - [OneNAND Interface] unlock all blocks
+ * @param mtd          MTD device structure
+ *
+ * Unlock all blocks
+ */
+static int onenand_unlock_all(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+
+       if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+               /* Write unlock command */
+               this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+               /* There's no return value */
+               this->wait(mtd, FL_UNLOCKING);
+
+               /* Sanity check */
+               while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+                   & ONENAND_CTRL_ONGO)
+                       continue;
+
+               /* Workaround for all block unlock in DDP */
+               if (this->device_id & ONENAND_DEVICE_IS_DDP) {
+                       loff_t ofs;
+                       size_t len;
+
+                       /* 1st block on another chip */
+                       ofs = this->chipsize >> 1;
+                       len = 1 << this->erase_shift;
+
+                       onenand_unlock(mtd, ofs, len);
+               }
+
+               onenand_check_lock_status(this);
+
+               return 0;
+       }
+
+       mtd->unlock(mtd, 0x0, this->chipsize);
+
+       return 0;
+}
+
 #ifdef CONFIG_MTD_ONENAND_OTP
 
 /* Interal OTP operation */
@@ -1563,13 +1637,44 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 }
 #endif /* CONFIG_MTD_ONENAND_OTP */
 
+/**
+ * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * @param mtd          MTD data structure
+ *
+ * Check and set OneNAND lock scheme
+ */
+static void onenand_lock_scheme(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int density, process;
+
+       /* Lock scheme depends on density and process */
+       density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+
+       /* Lock scheme */
+       if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+               /* A-Die has all block unlock */
+               if (process) {
+                       printk(KERN_DEBUG "Chip support all block unlock\n");
+                       this->options |= ONENAND_HAS_UNLOCK_ALL;
+               }
+       } else {
+               /* Some OneNAND has continues lock scheme */
+               if (!process) {
+                       printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
+                       this->options |= ONENAND_HAS_CONT_LOCK;
+               }
+       }
+}
+
 /**
  * onenand_print_device_info - Print device ID
  * @param device        device ID
  *
  * Print device ID
  */
-static void onenand_print_device_info(int device)
+static void onenand_print_device_info(int device, int version)
 {
         int vcc, demuxed, ddp, density;
 
@@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device)
                 (16 << density),
                 vcc ? "2.65/3.3" : "1.8",
                 device);
+       printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
 }
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -1625,8 +1731,7 @@ static int onenand_check_maf(int manuf)
 static int onenand_probe(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
-       int bram_maf_id, bram_dev_id, maf_id, dev_id;
-       int version_id;
+       int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
        int density;
        int syscfg;
 
@@ -1657,14 +1762,16 @@ static int onenand_probe(struct mtd_info *mtd)
        /* Read manufacturer and device IDs from Register */
        maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
        dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+       ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID);
 
        /* Check OneNAND device */
        if (maf_id != bram_maf_id || dev_id != bram_dev_id)
                return -ENXIO;
 
        /* Flash device information */
-       onenand_print_device_info(dev_id);
+       onenand_print_device_info(dev_id, ver_id);
        this->device_id = dev_id;
+       this->version_id = ver_id;
 
        density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
        this->chipsize = (16 << density) << 20;
@@ -1687,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd)
 
        mtd->size = this->chipsize;
 
-       /* Version ID */
-       version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
-       printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id);
-
-       /* Lock scheme */
-       if (density <= ONENAND_DEVICE_DENSITY_512Mb &&
-           !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) {
-               printk(KERN_INFO "Lock scheme is Continues Lock\n");
-               this->options |= ONENAND_CONT_LOCK;
-       }
+       /* Check OneNAND lock scheme */
+       onenand_lock_scheme(mtd);
 
        return 0;
 }
@@ -1832,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->owner = THIS_MODULE;
 
        /* Unlock whole block */
-       mtd->unlock(mtd, 0x0, this->chipsize);
+       onenand_unlock_all(mtd);
 
        return this->scan_bbt(mtd);
 }
index 1f49721..6f045b5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  linux/include/linux/mtd/onenand.h
  *
- *  Copyright (C) 2005 Samsung Electronics
+ *  Copyright (C) 2005-2006 Samsung Electronics
  *  Kyungmin Park <kyungmin.park@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -96,6 +96,7 @@ struct onenand_chip {
        void __iomem            *base;
        unsigned int            chipsize;
        unsigned int            device_id;
+       unsigned int            version_id;
        unsigned int            density_mask;
        unsigned int            options;
 
@@ -149,7 +150,8 @@ struct onenand_chip {
 /*
  * Options bits
  */
-#define ONENAND_CONT_LOCK              (0x0001)
+#define ONENAND_HAS_CONT_LOCK          (0x0001)
+#define ONENAND_HAS_UNLOCK_ALL         (0x0002)
 #define ONENAND_PAGEBUF_ALLOC          (0x1000)
 
 /*
index 4a72818..9e409fe 100644 (file)
@@ -3,7 +3,7 @@
  *
  *  OneNAND Register header file
  *
- *  Copyright (C) 2005 Samsung Electronics
+ *  Copyright (C) 2005-2006 Samsung Electronics
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -72,6 +72,7 @@
 #define ONENAND_DEVICE_VCC_MASK                (0x3)
 
 #define ONENAND_DEVICE_DENSITY_512Mb   (0x002)
+#define ONENAND_DEVICE_DENSITY_1Gb     (0x003)
 
 /*
  * Version ID Register F002h (R)
 #define ONENAND_CMD_UNLOCK             (0x23)
 #define ONENAND_CMD_LOCK               (0x2A)
 #define ONENAND_CMD_LOCK_TIGHT         (0x2C)
+#define ONENAND_CMD_UNLOCK_ALL         (0x27)
 #define ONENAND_CMD_ERASE              (0x94)
 #define ONENAND_CMD_RESET              (0xF0)
 #define ONENAND_CMD_OTP_ACCESS         (0x65)