123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- From ea92cbb50a78404e29de2cc3999a240615ffb1c8 Mon Sep 17 00:00:00 2001
- From: Chuanhong Guo <[email protected]>
- Date: Mon, 6 Apr 2020 17:58:48 +0800
- Subject: [PATCH] mtd: spi-nor: rework broken-flash-reset support
- Instead of resetting flash to 3B address on remove hook, this
- implementation only enters 4B mode when needed, which prevents
- more unexpected reboot stuck. This implementation makes it only
- break when a kernel panic happens during flash operation on 16M+
- areas.
- *OpenWrt only*: silent broken-flash-reset warning. We are not dealing
- with vendors and it's unpleasant for users to se that unnecessary
- and long WARN_ON print.
- Signed-off-by: Chuanhong Guo <[email protected]>
- ---
- drivers/mtd/spi-nor/spi-nor.c | 52 +++++++++++++++++++++++++++++++++--
- 1 file changed, 49 insertions(+), 3 deletions(-)
- --- a/drivers/mtd/spi-nor/spi-nor.c
- +++ b/drivers/mtd/spi-nor/spi-nor.c
- @@ -616,6 +616,22 @@ static void spi_nor_set_4byte_opcodes(st
- }
- }
-
- +static int spi_nor_check_set_addr_width(struct spi_nor *nor, loff_t addr)
- +{
- + u8 addr_width;
- +
- + if ((nor->flags & (SNOR_F_4B_OPCODES | SNOR_F_BROKEN_RESET)) !=
- + SNOR_F_BROKEN_RESET)
- + return 0;
- +
- + addr_width = addr & 0xff000000 ? 4 : 3;
- + if (nor->addr_width == addr_width)
- + return 0;
- +
- + nor->addr_width = addr_width;
- + return nor->params.set_4byte(nor, addr_width == 4);
- +}
- +
- static int macronix_set_4byte(struct spi_nor *nor, bool enable)
- {
- if (nor->spimem) {
- @@ -1259,6 +1275,10 @@ static int spi_nor_erase(struct mtd_info
- if (ret)
- return ret;
-
- + ret = spi_nor_check_set_addr_width(nor, instr->addr + instr->len);
- + if (ret < 0)
- + return ret;
- +
- /* whole-chip erase? */
- if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
- unsigned long timeout;
- @@ -1315,6 +1335,7 @@ static int spi_nor_erase(struct mtd_info
- write_disable(nor);
-
- erase_err:
- + spi_nor_check_set_addr_width(nor, 0);
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
-
- return ret;
- @@ -1621,7 +1642,9 @@ static int spi_nor_lock(struct mtd_info
- if (ret)
- return ret;
-
- + spi_nor_check_set_addr_width(nor, ofs + len);
- ret = nor->params.locking_ops->lock(nor, ofs, len);
- + spi_nor_check_set_addr_width(nor, 0);
-
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
- return ret;
- @@ -1636,7 +1659,9 @@ static int spi_nor_unlock(struct mtd_inf
- if (ret)
- return ret;
-
- + spi_nor_check_set_addr_width(nor, ofs + len);
- ret = nor->params.locking_ops->unlock(nor, ofs, len);
- + spi_nor_check_set_addr_width(nor, 0);
-
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
- return ret;
- @@ -1651,7 +1676,9 @@ static int spi_nor_is_locked(struct mtd_
- if (ret)
- return ret;
-
- + spi_nor_check_set_addr_width(nor, ofs + len);
- ret = nor->params.locking_ops->is_locked(nor, ofs, len);
- + spi_nor_check_set_addr_width(nor, 0);
-
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
- return ret;
- @@ -2557,6 +2584,10 @@ static int spi_nor_read(struct mtd_info
- if (ret)
- return ret;
-
- + ret = spi_nor_check_set_addr_width(nor, from + len);
- + if (ret < 0)
- + return ret;
- +
- while (len) {
- loff_t addr = from;
-
- @@ -2580,6 +2611,7 @@ static int spi_nor_read(struct mtd_info
- ret = 0;
-
- read_err:
- + spi_nor_check_set_addr_width(nor, 0);
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
- return ret;
- }
- @@ -2597,6 +2629,10 @@ static int sst_write(struct mtd_info *mt
- if (ret)
- return ret;
-
- + ret = spi_nor_check_set_addr_width(nor, to + len);
- + if (ret < 0)
- + return ret;
- +
- write_enable(nor);
-
- nor->sst_write_second = false;
- @@ -2659,6 +2695,7 @@ static int sst_write(struct mtd_info *mt
- }
- sst_write_err:
- *retlen += actual;
- + spi_nor_check_set_addr_width(nor, 0);
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
- return ret;
- }
- @@ -2681,6 +2718,10 @@ static int spi_nor_write(struct mtd_info
- if (ret)
- return ret;
-
- + ret = spi_nor_check_set_addr_width(nor, to + len);
- + if (ret < 0)
- + return ret;
- +
- for (i = 0; i < len; ) {
- ssize_t written;
- loff_t addr = to + i;
- @@ -2720,6 +2761,7 @@ static int spi_nor_write(struct mtd_info
- }
-
- write_err:
- + spi_nor_check_set_addr_width(nor, 0);
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
- return ret;
- }
- @@ -4725,9 +4767,13 @@ static int spi_nor_init(struct spi_nor *
- * reboots (e.g., crashes). Warn the user (or hopefully, system
- * designer) that this is bad.
- */
- - WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
- - "enabling reset hack; may not recover from unexpected reboots\n");
- - nor->params.set_4byte(nor, true);
- + if (nor->flags & SNOR_F_BROKEN_RESET) {
- + dev_warn(nor->dev,
- + "enabling reset hack; may not recover from unexpected reboots\n");
- + nor->addr_width = 3;
- + } else {
- + nor->params.set_4byte(nor, true);
- + }
- }
-
- return 0;
|