mtd: cfi_cmdset_0002: Add support for writing OTP memory
authorChristian Riesch <christian.riesch@omicron.at>
Thu, 6 Mar 2014 12:18:29 +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 writing 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 612eb3d..6c575e6 100644 (file)
@@ -67,6 +67,8 @@ static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
                                         size_t *, u_char *);
 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_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
                                  size_t *retlen, const u_char *buf);
@@ -530,6 +532,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg;
        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->flags   = MTD_CAP_NORFLASH;
        mtd->name    = map->name;
        mtd->writesize = 1;
@@ -1257,6 +1260,40 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
        return ret;
 }
 
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
+                                    unsigned long adr, map_word datum,
+                                    int mode);
+
+static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
+                       size_t len, u_char *buf)
+{
+       int ret;
+       while (len) {
+               unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
+               int gap = adr - bus_ofs;
+               int n = min_t(int, len, map_bankwidth(map) - gap);
+               map_word datum;
+
+               if (n != map_bankwidth(map)) {
+                       /* partial write of a word, load old contents */
+                       otp_enter(map, chip, bus_ofs, map_bankwidth(map));
+                       datum = map_read(map, bus_ofs);
+                       otp_exit(map, chip, bus_ofs, map_bankwidth(map));
+               }
+
+               datum = map_word_load_partial(map, datum, buf, gap, n);
+               ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
+               if (ret)
+                       return ret;
+
+               adr += n;
+               buf += n;
+               len -= n;
+       }
+
+       return 0;
+}
+
 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)
@@ -1400,7 +1437,17 @@ static int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
                                   buf, do_read_secsi_onechip, 1);
 }
 
-static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
+static int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+                                         size_t len, size_t *retlen,
+                                         u_char *buf)
+{
+       return cfi_amdstd_otp_walk(mtd, from, len, retlen, buf,
+                                  do_otp_write, 1);
+}
+
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
+                                    unsigned long adr, map_word datum,
+                                    int mode)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long timeo = jiffies + HZ;
@@ -1421,7 +1468,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        adr += chip->start;
 
        mutex_lock(&chip->mutex);
-       ret = get_chip(map, chip, adr, FL_WRITING);
+       ret = get_chip(map, chip, adr, mode);
        if (ret) {
                mutex_unlock(&chip->mutex);
                return ret;
@@ -1430,6 +1477,9 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
               __func__, adr, datum.x[0] );
 
+       if (mode == FL_OTP_WRITE)
+               otp_enter(map, chip, adr, map_bankwidth(map));
+
        /*
         * Check for a NOP for the case when the datum to write is already
         * present - it saves time and works around buggy chips that corrupt
@@ -1446,12 +1496,13 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
        ENABLE_VPP(map);
        xip_disable(map, chip, adr);
+
  retry:
        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(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        map_write(map, datum, adr);
-       chip->state = FL_WRITING;
+       chip->state = mode;
 
        INVALIDATE_CACHE_UDELAY(map, chip,
                                adr, map_bankwidth(map),
@@ -1460,7 +1511,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        /* See comment above for timeout value. */
        timeo = jiffies + uWriteTimeout;
        for (;;) {
-               if (chip->state != FL_WRITING) {
+               if (chip->state != mode) {
                        /* Someone's suspended the write. Sleep */
                        DECLARE_WAITQUEUE(wait, current);
 
@@ -1500,6 +1551,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        }
        xip_enable(map, chip, adr);
  op_done:
+       if (mode == FL_OTP_WRITE)
+               otp_exit(map, chip, adr, map_bankwidth(map));
        chip->state = FL_READY;
        DISABLE_VPP(map);
        put_chip(map, chip, adr);
@@ -1555,7 +1608,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
 
                ret = do_write_oneword(map, &cfi->chips[chipnum],
-                                      bus_ofs, tmp_buf);
+                                      bus_ofs, tmp_buf, FL_WRITING);
                if (ret)
                        return ret;
 
@@ -1579,7 +1632,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                datum = map_word_load(map, buf);
 
                ret = do_write_oneword(map, &cfi->chips[chipnum],
-                                      ofs, datum);
+                                      ofs, datum, FL_WRITING);
                if (ret)
                        return ret;
 
@@ -1622,7 +1675,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
 
                ret = do_write_oneword(map, &cfi->chips[chipnum],
-                               ofs, tmp_buf);
+                                      ofs, tmp_buf, FL_WRITING);
                if (ret)
                        return ret;