OneNAND: One-Time Programmable (OTP) support
authorKyungmin Park <kyungmin.park@samsung.com>
Fri, 12 May 2006 14:03:07 +0000 (17:03 +0300)
committerJarkko Lavinen <lavinen@pentafluge.infradead.org>
Fri, 12 May 2006 14:35:50 +0000 (15:35 +0100)
One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
Also, 1st Block of NAND Flash Array can be used as OTP.

The OTP block can be read, programmed and locked using the same
operations as any other NAND Flash Array memory block.
OTP block cannot be erased.

OTP block is fully-guaranteed to be a valid block.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
drivers/mtd/mtdchar.c
drivers/mtd/onenand/Kconfig
drivers/mtd/onenand/onenand_base.c
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h

index 6b83aee8abb8ee137c51b4d07075a62a5610d87a..7a7df851c99339062cce3570cab6833e03465690 100644 (file)
@@ -552,7 +552,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
-#ifdef CONFIG_MTD_OTP
+#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
        case OTPSELECT:
        {
                int mode;
index 126ff6bf63d5c8b8cfc733b13f5656f758a40cd7..5930a03736d7557ab9bdfc9137bb604a863560d1 100644 (file)
@@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC
        help
          Support for OneNAND flash via platform device driver.
 
+config MTD_ONENAND_OTP
+       bool "OneNAND OTP Support"
+       depends on MTD_ONENAND
+       help
+         One Block of the NAND Flash Array memory is reserved as
+         a One-Time Programmable Block memory area.
+         Also, 1st Block of NAND Flash Array can be used as OTP.
+
+         The OTP block can be read, programmed and locked using the same
+         operations as any other NAND Flash Array memory block.
+         OTP block cannot be erased.
+
+         OTP block is fully-guaranteed to be a valid block.
+
 config MTD_ONENAND_SYNC_READ
        bool "OneNAND Sync. Burst Read Support"
        depends on ARCH_OMAP
index 7c7dc0ae5a190cc53d9c0c6247f1c503202f8994..163c81135447973e5f003fda9b2c84cc4e287940 100644 (file)
@@ -191,7 +191,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;
+       int value, readcmd = 0, block_cmd = 0;
        int block, page;
        /* Now we use page size operation */
        int sectors = 4, count = 4;
@@ -207,6 +207,8 @@ 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;
@@ -235,7 +237,7 @@ 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 (cmd == ONENAND_CMD_ERASE) {
+               if (cmd == block_cmd) {
                        /* Select DataRAM for DDP */
                        value = onenand_bufferram_address(this, block);
                        this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
@@ -1412,6 +1414,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        return 0;
 }
 
+#ifdef CONFIG_MTD_ONENAND_OTP
+
+/* Interal OTP operation */
+typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
+               size_t *retlen, u_char *buf);
+
+/**
+ * do_otp_read - [DEFAULT] Read OTP block area
+ * @param mtd          MTD device structure
+ * @param from         The offset to read
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of readbytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Read OTP block area.
+ */
+static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ret;
+
+       /* Enter OTP access mode */
+       this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+       this->wait(mtd, FL_OTPING);
+
+       ret = mtd->read(mtd, from, len, retlen, buf);
+
+       /* Exit OTP access mode */
+       this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+       this->wait(mtd, FL_RESETING);
+
+       return ret;
+}
+
+/**
+ * do_otp_write - [DEFAULT] Write OTP block area
+ * @param mtd          MTD device structure
+ * @param from         The offset to write
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of write bytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Write OTP block area.
+ */
+static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned char *pbuf = buf;
+       int ret;
+
+       /* Force buffer page aligned */
+       if (len < mtd->oobblock) {
+               memcpy(this->page_buf, buf, len);
+               memset(this->page_buf + len, 0xff, mtd->oobblock - len);
+               pbuf = this->page_buf;
+               len = mtd->oobblock;
+       }
+
+       /* Enter OTP access mode */
+       this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+       this->wait(mtd, FL_OTPING);
+
+       ret = mtd->write(mtd, from, len, retlen, pbuf);
+
+       /* Exit OTP access mode */
+       this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+       this->wait(mtd, FL_RESETING);
+
+       return ret;
+}
+
+/**
+ * do_otp_lock - [DEFAULT] Lock OTP block area
+ * @param mtd          MTD device structure
+ * @param from         The offset to lock
+ * @param len          number of bytes to lock
+ * @param retlen       pointer to variable to store the number of lock bytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Lock OTP block area.
+ */
+static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ret;
+
+       /* Enter OTP access mode */
+       this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+       this->wait(mtd, FL_OTPING);
+
+       ret = mtd->write_oob(mtd, from, len, retlen, buf);
+
+       /* Exit OTP access mode */
+       this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+       this->wait(mtd, FL_RESETING);
+
+       return ret;
+}
+
+/**
+ * onenand_otp_walk - [DEFAULT] Handle OTP operation
+ * @param mtd          MTD device structure
+ * @param from         The offset to read/write
+ * @param len          number of bytes to read/write
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put/get data
+ * @param action       do given action
+ * @param mode         specify user and factory
+ *
+ * Handle OTP operation.
+ */
+static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
+                       size_t *retlen, u_char *buf,
+                       otp_op_t action, int mode)
+{
+       struct onenand_chip *this = mtd->priv;
+       int otp_pages;
+       int density;
+       int ret = 0;
+
+       *retlen = 0;
+
+       density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       if (density < ONENAND_DEVICE_DENSITY_512Mb)
+               otp_pages = 20;
+       else
+               otp_pages = 10;
+
+       if (mode == MTD_OTP_FACTORY) {
+               from += mtd->oobblock * otp_pages;
+               otp_pages = 64 - otp_pages;
+       }
+
+       /* Check User/Factory boundary */
+       if (((mtd->oobblock * otp_pages) - (from + len)) < 0)
+               return 0;
+
+       while (len > 0 && otp_pages > 0) {
+               if (!action) {  /* OTP Info functions */
+                       struct otp_info *otpinfo;
+
+                       len -= sizeof(struct otp_info);
+                       if (len <= 0)
+                               return -ENOSPC;
+
+                       otpinfo = (struct otp_info *) buf;
+                       otpinfo->start = from;
+                       otpinfo->length = mtd->oobblock;
+                       otpinfo->locked = 0;
+
+                       from += mtd->oobblock;
+                       buf += sizeof(struct otp_info);
+                       *retlen += sizeof(struct otp_info);
+               } else {
+                       size_t tmp_retlen;
+                       int size = len;
+
+                       ret = action(mtd, from, len, &tmp_retlen, buf);
+
+                       buf += size;
+                       len -= size;
+                       *retlen += size;
+
+                       if (ret < 0)
+                               return ret;
+               }
+               otp_pages--;
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
+ * @param mtd          MTD device structure
+ * @param buf          the databuffer to put/get data
+ * @param len          number of bytes to read
+ *
+ * Read factory OTP info.
+ */
+static int onenand_get_fact_prot_info(struct mtd_info *mtd,
+                       struct otp_info *buf, size_t len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
+
+       return ret ? : retlen;
+}
+
+/**
+ * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
+ * @param mtd          MTD device structure
+ * @param from         The offset to read
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Read factory OTP area.
+ */
+static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+                       size_t len, size_t *retlen, u_char *buf)
+{
+       return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);
+}
+
+/**
+ * onenand_get_user_prot_info - [MTD Interface] Read user OTP info
+ * @param mtd          MTD device structure
+ * @param buf          the databuffer to put/get data
+ * @param len          number of bytes to read
+ *
+ * Read user OTP info.
+ */
+static int onenand_get_user_prot_info(struct mtd_info *mtd,
+                       struct otp_info *buf, size_t len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
+
+       return ret ? : retlen;
+}
+
+/**
+ * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
+ * @param mtd          MTD device structure
+ * @param from         The offset to read
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Read user OTP area.
+ */
+static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+                       size_t len, size_t *retlen, u_char *buf)
+{
+       return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);
+}
+
+/**
+ * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
+ * @param mtd          MTD device structure
+ * @param from         The offset to write
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of write bytes
+ * @param buf          the databuffer to put/get data
+ *
+ * Write user OTP area.
+ */
+static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+                       size_t len, size_t *retlen, u_char *buf)
+{
+       return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);
+}
+
+/**
+ * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
+ * @param mtd          MTD device structure
+ * @param from         The offset to lock
+ * @param len          number of bytes to unlock
+ *
+ * Write lock mark on spare area in page 0 in OTP block
+ */
+static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+                       size_t len)
+{
+       unsigned char oob_buf[64];
+       size_t retlen;
+       int ret;
+
+       memset(oob_buf, 0xff, mtd->oobsize);
+       /*
+        * Note: OTP lock operation
+        *       OTP block : 0xXXFC
+        *       1st block : 0xXXF3 (If chip support)
+        *       Both      : 0xXXF0 (If chip support)
+        */
+       oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
+
+       /*
+        * Write lock mark to 8th word of sector0 of page0 of the spare0.
+        * We write 16 bytes spare area instead of 2 bytes.
+        */
+       from = 0;
+       len = 16;
+
+       ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
+
+       return ret ? : retlen;
+}
+#endif /* CONFIG_MTD_ONENAND_OTP */
+
 /**
  * onenand_print_device_info - Print device ID
  * @param device        device ID
@@ -1563,7 +1863,6 @@ static void onenand_resume(struct mtd_info *mtd)
                                "in suspended state\n");
 }
 
-
 /**
  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
  * @param mtd          MTD device structure
@@ -1655,6 +1954,14 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->write_ecc = onenand_write_ecc;
        mtd->read_oob = onenand_read_oob;
        mtd->write_oob = onenand_write_oob;
+#ifdef CONFIG_MTD_ONENAND_OTP
+       mtd->get_fact_prot_info = onenand_get_fact_prot_info;
+       mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
+       mtd->get_user_prot_info = onenand_get_user_prot_info;
+       mtd->read_user_prot_reg = onenand_read_user_prot_reg;
+       mtd->write_user_prot_reg = onenand_write_user_prot_reg;
+       mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
+#endif
        mtd->readv = NULL;
        mtd->readv_ecc = NULL;
        mtd->writev = onenand_writev;
index 22322c8a7729da6984ef36994a3a9ae0d8cb57a4..3f5919f2e9da39e7329b8a3d368925ee663aef64 100644 (file)
@@ -35,6 +35,8 @@ typedef enum {
        FL_SYNCING,
        FL_UNLOCKING,
        FL_LOCKING,
+       FL_RESETING,
+       FL_OTPING,
        FL_PM_SUSPENDED,
 } onenand_state_t;
 
index d7832ef8ed635e9bb37a182b3ffe766afaf6be3d..4a72818d25453205f64d7e538913885ebd1c2657 100644 (file)
 #define ONENAND_CMD_LOCK_TIGHT         (0x2C)
 #define ONENAND_CMD_ERASE              (0x94)
 #define ONENAND_CMD_RESET              (0xF0)
+#define ONENAND_CMD_OTP_ACCESS         (0x65)
 #define ONENAND_CMD_READID             (0x90)
 
 /* NOTE: Those are not *REAL* commands */
 #define ONENAND_CTRL_ERASE             (1 << 11)
 #define ONENAND_CTRL_ERROR             (1 << 10)
 #define ONENAND_CTRL_RSTB              (1 << 7)
+#define ONENAND_CTRL_OTP_L             (1 << 6)
+#define ONENAND_CTRL_OTP_BL            (1 << 5)
 
 /*
  * Interrupt Status Register F241h (R)
 #define ONENAND_ECC_2BIT               (1 << 1)
 #define ONENAND_ECC_2BIT_ALL           (0xAAAA)
 
+/*
+ * One-Time Programmable (OTP)
+ */
+#define ONENAND_OTP_LOCK_OFFSET                (14)
+
 #endif /* __ONENAND_REG_H */