From 0918552295609b124ccc14b579223e413d3e0d36 Mon Sep 17 00:00:00 2001 From: Heinrich Toews Date: Thu, 19 Feb 2026 17:50:22 +0100 Subject: [PATCH] mtd: spi-nor: core: add blind octal reset fix for Everspin MRAM Everspin Octal-STR MRAMs (like EM008LXO) stay in Octal mode after a warm reboot if the hardware reset pin is not toggled (e.g., when connected to nPORZ-MCU). This causes the subsequent JEDEC ID read to fail, as the controller restarts in Single-SPI mode while the device still expects Octal-STR commands. This patch introduces spi_nor_everspin_reboot_fix(), which is called within spi_nor_hw_reset() as a fallback if no dedicated reset GPIO is defined in the Device Tree. The fix sends a "blind" Software Reset sequence (0x66 -> 0x99) using 8-8-8 protocol. If the device is in Octal-STR mode, it will recover to Single-SPI. If it is already in Single-SPI mode, the 8-lane command is ignored by the device, making this a safe recovery path for warm reboots. Signed-off-by: Heinrich Toews --- drivers/mtd/spi-nor/core.c | 65 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index e5871e5ac428..58a54e1075c9 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3442,13 +3442,74 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor) mtd->_put_device = spi_nor_put_device; } +/** + * spi_nor_everspin_reboot_fix - Forces Everspin MRAM out of Octal mode + * @nor: pointer to 'struct spi_nor' + * + * This function sends a "blind" Software Reset sequence in Octal-STR (8-8-8). + * It is required for warm reboots because the MRAM stays in Octal mode + * while the controller restarts in Single-SPI mode. + */ +/** + * spi_nor_everspin_reboot_fix - Blind reset for MRAM Octal-STR escape + * @nor: pointer to 'struct spi_nor' + */ +static void spi_nor_everspin_reboot_fix(struct spi_nor *nor) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(0x66, 8), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + int ret; + + if (!nor->spimem || !nor->dev) + return; + + /* + * Logging as dev_info so it appears in dmesg during boot. + * This helps verify if the fix is being executed. + */ + dev_info(nor->dev, "MRAM Reboot-Fix: Sending blind Octal-STR reset (0x66->0x99)...\n"); + + /* 1. Reset Enable (0x66) in Octal-STR */ + ret = spi_mem_exec_op(nor->spimem, &op); + + /* 2. Reset (0x99) in Octal-STR */ + op.cmd.opcode = 0x99; + ret |= spi_mem_exec_op(nor->spimem, &op); + + if (ret) { + /* + * Note: A failure here is expected if the controller is + * not yet ready for 8-lane commands or if the chip + * is already in Single-SPI mode. + */ + dev_dbg(nor->dev, "MRAM Reboot-Fix: Octal reset op returned %d (normal for Single-SPI)\n", ret); + } + + /* Essential delay: allow MRAM internal state machine to recover */ + usleep_range(1000, 2000); + + dev_info(nor->dev, "MRAM Reboot-Fix: Completed. Chip should be in Single-SPI mode now.\n"); +} + static int spi_nor_hw_reset(struct spi_nor *nor) { struct gpio_desc *reset; reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR_OR_NULL(reset)) - return PTR_ERR_OR_ZERO(reset); + if (IS_ERR(reset)) + return PTR_ERR(reset); + + if (!reset) { + /* + * FALLBACK: No dedicated reset-pin found in DT. + * Execute the blind Octal-STR reset to recover from warm reboots. + */ + spi_nor_everspin_reboot_fix(nor); + return 0; + } /* * Experimental delay values by looking at different flash device