From af7447505732ea729af6a99e76fc558b6e3fcbcd Mon Sep 17 00:00:00 2001 From: Christian Riesch Date: Thu, 6 Mar 2014 13:18:29 +0100 Subject: mtd: cfi_cmdset_0002: Add support for writing OTP memory This patch adds support for writing the one time programmable (OTP) memory of Micron M29EW devices. Signed-off-by: Christian Riesch Signed-off-by: Brian Norris --- drivers/mtd/chips/cfi_cmdset_0002.c | 67 +++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 612eb3d..6c575e6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -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; -- cgit v1.1