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 <ht@twx-software.de>
This commit is contained in:
Heinrich Toews
2026-02-19 17:50:22 +01:00
parent b6aba6e146
commit 94b0440bc4
+63 -2
View File
@@ -3467,13 +3467,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