mtd: cfi_cmdset_0002: Add support for locking OTP memory
authorChristian Riesch <christian.riesch@omicron.at>
Thu, 6 Mar 2014 12:18:30 +0000 (13:18 +0100)
committerBrian Norris <computersforpeace@gmail.com>
Sat, 12 Jul 2014 02:44:25 +0000 (19:44 -0700)
This patch adds support for the locking of the one time
programmable (OTP) memory of Micron M29EW devices.

Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
drivers/mtd/chips/cfi_cmdset_0002.c

index 6c575e65010b2ca6a7b1520324fb117d87aeb17e..bf313be6ee265ce90486bd92bbe90db32030a287 100644 (file)
@@ -69,6 +69,7 @@ static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
                                         size_t *, u_char *);
 static int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t,
                                          size_t *, u_char *);
+static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t);
 
 static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
                                  size_t *retlen, const u_char *buf);
@@ -533,6 +534,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info;
        mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info;
        mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg;
+       mtd->_lock_user_prot_reg = cfi_amdstd_lock_user_prot_reg;
        mtd->flags   = MTD_CAP_NORFLASH;
        mtd->name    = map->name;
        mtd->writesize = 1;
@@ -1153,7 +1155,7 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_
 }
 
 typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
-                       loff_t adr, size_t len, u_char *buf);
+                       loff_t adr, size_t len, u_char *buf, size_t grouplen);
 
 static inline void otp_enter(struct map_info *map, struct flchip *chip,
                             loff_t adr, size_t len)
@@ -1187,7 +1189,10 @@ static inline void otp_exit(struct map_info *map, struct flchip *chip,
        INVALIDATE_CACHED_RANGE(map, chip->start + adr, len);
 }
 
-static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+static inline int do_read_secsi_onechip(struct map_info *map,
+                                       struct flchip *chip, loff_t adr,
+                                       size_t len, u_char *buf,
+                                       size_t grouplen)
 {
        DECLARE_WAITQUEUE(wait, current);
        unsigned long timeo = jiffies + HZ;
@@ -1246,7 +1251,8 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
                else
                        thislen = len;
 
-               ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
+               ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs,
+                                           thislen, buf, 0);
                if (ret)
                        break;
 
@@ -1265,7 +1271,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
                                     int mode);
 
 static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
-                       size_t len, u_char *buf)
+                       size_t len, u_char *buf, size_t grouplen)
 {
        int ret;
        while (len) {
@@ -1294,6 +1300,70 @@ static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
        return 0;
 }
 
+static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
+                      size_t len, u_char *buf, size_t grouplen)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       uint8_t lockreg;
+       unsigned long timeo;
+       int ret;
+
+       /* make sure area matches group boundaries */
+       if ((adr != 0) || (len != grouplen))
+               return -EINVAL;
+
+       mutex_lock(&chip->mutex);
+       ret = get_chip(map, chip, chip->start, FL_LOCKING);
+       if (ret) {
+               mutex_unlock(&chip->mutex);
+               return ret;
+       }
+       chip->state = FL_LOCKING;
+
+       /* Enter lock register command */
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x40, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+
+       /* read lock register */
+       lockreg = cfi_read_query(map, 0);
+
+       /* set bit 0 to protect extended memory block */
+       lockreg &= ~0x01;
+
+       /* set bit 0 to protect extended memory block */
+       /* write lock register */
+       map_write(map, CMD(0xA0), chip->start);
+       map_write(map, CMD(lockreg), chip->start);
+
+       /* wait for chip to become ready */
+       timeo = jiffies + msecs_to_jiffies(2);
+       for (;;) {
+               if (chip_ready(map, adr))
+                       break;
+
+               if (time_after(jiffies, timeo)) {
+                       pr_err("Waiting for chip to be ready timed out.\n");
+                       ret = -EIO;
+                       break;
+               }
+               UDELAY(map, chip, 0, 1);
+       }
+
+       /* exit protection commands */
+       map_write(map, CMD(0x90), chip->start);
+       map_write(map, CMD(0x00), chip->start);
+
+       chip->state = FL_READY;
+       put_chip(map, chip, chip->start);
+       mutex_unlock(&chip->mutex);
+
+       return ret;
+}
+
 static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
                               size_t *retlen, u_char *buf,
                               otp_op_t action, int user_regs)
@@ -1392,7 +1462,8 @@ static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
                } else if ((from < otpsize) && (len > 0)) {
                        size_t size;
                        size = (len < otpsize - from) ? len : otpsize - from;
-                       ret = action(map, chip, otpoffset + from, size, buf);
+                       ret = action(map, chip, otpoffset + from, size, buf,
+                                    otpsize);
                        if (ret < 0)
                                return ret;
 
@@ -1445,6 +1516,14 @@ static int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
                                   do_otp_write, 1);
 }
 
+static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+                                        size_t len)
+{
+       size_t retlen;
+       return cfi_amdstd_otp_walk(mtd, from, len, &retlen, NULL,
+                                  do_otp_lock, 1);
+}
+
 static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
                                     unsigned long adr, map_word datum,
                                     int mode)
This page took 0.027262 seconds and 5 git commands to generate.