mtd: spi-nor: only apply reset hacks to broken hardware
authorBrian Norris <computersforpeace@gmail.com>
Fri, 27 Jul 2018 18:33:13 +0000 (11:33 -0700)
committerBoris Brezillon <boris.brezillon@bootlin.com>
Wed, 1 Aug 2018 07:27:38 +0000 (09:27 +0200)
Commit 59b356ffd0b0 ("mtd: m25p80: restore the status of SPI flash when
exiting") is the latest from a long history of attempts to add reboot
handling to handle stateful addressing modes on SPI flash. Some prior
mostly-related discussions:

http://lists.infradead.org/pipermail/linux-mtd/2013-March/046343.html
[PATCH 1/3] mtd: m25p80: utilize dedicated 4-byte addressing commands

http://lists.infradead.org/pipermail/barebox/2014-September/020682.html
[RFC] MTD m25p80 3-byte addressing and boot problem

http://lists.infradead.org/pipermail/linux-mtd/2015-February/057683.html
[PATCH 2/2] m25p80: if supported put chip to deep power down if not used

Previously, attempts to add reboot-time software reset handling were
rejected, but the latest attempt was not.

Quick summary of the problem:
Some systems (e.g., boot ROM or bootloader) assume that they can read
initial boot code from their SPI flash using 3-byte addressing. If the
flash is left in 4-byte mode after reset, these systems won't boot. The
above patch provided a shutdown/remove hook to attempt to reset the
addressing mode before we reboot. Notably, this patch misses out on
huge classes of unexpected reboots (e.g., crashes, watchdog resets).

Unfortunately, it is essentially impossible to solve this problem 100%:
if your system doesn't know how to reset the SPI flash to power-on
defaults at initialization time, no amount of software can really rescue
you -- there will always be a chance of some unexpected reset that
leaves your flash in an addressing mode that your boot sequence didn't
expect.

While it is not directly harmful to perform hacks like the
aforementioned commit on all 4-byte addressing flash, a
properly-designed system should not need the hack -- and in fact,
providing this hack may mask the fact that a given system is indeed
broken. So this patch attempts to apply this unsound hack more narrowly,
providing a strong suggestion to developers and system designers that
this is truly a hack. With luck, system designers can catch their errors
early on in their development cycle, rather than applying this hack long
term. But apparently enough systems are out in the wild that we still
have to provide this hack.

Document a new device tree property to denote systems that do not have a
proper hardware (or software) reset mechanism, and apply the hack (with
a loud warning) only in this case.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
drivers/mtd/spi-nor/spi-nor.c
include/linux/mtd/spi-nor.h

index 956bb046e599d576e3f881b2901e0d369a3c9802..f03be904d3c228d8e46cc117ea0d5893f68e6e9b 100644 (file)
@@ -69,6 +69,15 @@ Optional properties:
                    all chips and support for it can not be detected at runtime.
                    Refer to your chips' datasheet to check if this is supported
                    by your chip.
+- broken-flash-reset : Some flash devices utilize stateful addressing modes
+                  (e.g., for 32-bit addressing) which need to be managed
+                  carefully by a system. Because these sorts of flash don't
+                  have a standardized software reset command, and because some
+                  systems don't toggle the flash RESET# pin upon system reset
+                  (if the pin even exists at all), there are systems which
+                  cannot reboot properly if the flash is left in the "wrong"
+                  state. This boolean flag can be used on such systems, to
+                  denote the absence of a reliable reset mechanism.
 
 Example:
 
index d9c368c441948cd57e85c86c9579acf101aacf5c..f028277fb1cedb86ac4837e78341705c82f60b1b 100644 (file)
@@ -2757,8 +2757,18 @@ static int spi_nor_init(struct spi_nor *nor)
 
        if ((nor->addr_width == 4) &&
            (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-           !(nor->info->flags & SPI_NOR_4B_OPCODES))
+           !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
+               /*
+                * If the RESET# pin isn't hooked up properly, or the system
+                * otherwise doesn't perform a reset command in the boot
+                * sequence, it's impossible to 100% protect against unexpected
+                * 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");
                set_4byte(nor, nor->info, 1);
+       }
 
        return 0;
 }
@@ -2781,7 +2791,8 @@ void spi_nor_restore(struct spi_nor *nor)
        /* restore the addressing mode */
        if ((nor->addr_width == 4) &&
            (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-           !(nor->info->flags & SPI_NOR_4B_OPCODES))
+           !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
+           (nor->flags & SNOR_F_BROKEN_RESET))
                set_4byte(nor, nor->info, 0);
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
@@ -2911,6 +2922,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
                params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
        }
 
+       if (of_property_read_bool(np, "broken-flash-reset"))
+               nor->flags |= SNOR_F_BROKEN_RESET;
+
        /* Some devices cannot do fast-read, no matter what DT tells us */
        if (info->flags & SPI_NOR_NO_FR)
                params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
index e60da0d34cc14794529f2f79dcabf3ca250839d6..c922e97f205a01b82a628d1d5a5d4f2c7e6c31a7 100644 (file)
@@ -235,6 +235,7 @@ enum spi_nor_option_flags {
        SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
        SNOR_F_READY_XSR_RDY    = BIT(4),
        SNOR_F_USE_CLSR         = BIT(5),
+       SNOR_F_BROKEN_RESET     = BIT(6),
 };
 
 /**