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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user