mtd: spi-nor: everspin: refactor initialization and enable Octal STR mode

Refactor the Everspin MRAM initialization sequence to support transition
into Octal STR (8s-0-8s) mode.

The following changes are implemented:
- Add everspin_mram_software_reset() to ensure the device is in a known
  state (Single-SPI) before configuration.
- Update register addresses and opcodes to align with the datasheet and
  legacy driver requirements (e.g., using 0x81 for WRAR).
- Refactor everspin_mram_write_reg() to use SNOR_PROTO_1_1_1 for initial
  configuration and ensure Write Enable (WREN) is issued.
- Implement Octal STR mode activation by configuring CFR1V (Dummy Cycles)
  and CFR0V (Mode).
- Add verification steps by reading the Status Register and ID in
  SNOR_PROTO_8_8_8 mode with appropriate dummy cycles.

This ensures the MRAM is correctly detected and switched to high-speed
octal operation during the probe phase.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
Heinrich Toews
2026-02-17 12:22:47 +01:00
parent 2b10a5afd4
commit 133e49bfc4
+233 -7
View File
@@ -5,9 +5,229 @@
*/
#include <linux/mtd/spi-nor.h>
#include <linux/delay.h>
#include <linux/spi/spi-mem.h>
#include "core.h"
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile configuration register */
#define SPINOR_REG_MT_CFR0V 0x00 /* Address for Mode Configuration */
#define SPINOR_REG_MT_CFR1V 0x01 /* Address for Dummy Cycle Configuration */
#define SPINOR_MT_OCT_STR 0xB7 /* Enable Octal STR mode with DS */
/**
* everspin_mram_software_reset - Software Reset in Single-SPI mode
*/
static int everspin_mram_software_reset(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
/* Software Reset Enable (0x66) */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(0x66, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* Software Reset (0x99) */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(0x99, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
udelay(100);
return 0;
}
/**
* everspin_mram_write_reg - Writes to configuration registers (4-byte addr)
*/
static int everspin_mram_write_reg(struct spi_nor *nor, u32 addr, u8 val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(3, addr, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
nor->bouncebuf[0] = val;
spi_nor_write_enable(nor);
return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_1_1_1);
}
/**
* everspin_mram_unlock - Clears Block Protection bits
*/
static int everspin_mram_unlock(struct spi_nor *nor)
{
struct spi_mem_op op;
spi_nor_write_enable(nor);
nor->bouncebuf[0] = 0x00;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
}
static void everspin_mram_default_init(struct spi_nor *nor)
{
struct spi_mem_op op;
struct spi_mem_op op_ri; /* for read id */
int ret;
/* Do a Software Reset to get sure we are in a clean state.
* Not necessary if a hardware reset is already done in NOR layer.
*/
everspin_mram_software_reset(nor);
dev_info(nor->dev, "Starting Everspin MRAM initialization ...\n");
/* Initial SR1 Check */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_1_1_1);
if (!ret) {
u8 sr1 = nor->bouncebuf[0];
dev_info(nor->dev, "Initial SR1: 0x%02x (BP-Bits: 0x%x)\n", sr1, (sr1 & 0x3c) >> 2);
if (sr1 & GENMASK(5, 2))
everspin_mram_unlock(nor);
}
/* Configuration: 8 dummy bytes */
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR1V, 8);
/* Verify WEL */
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_1_1_1);
if (!ret)
dev_info(nor->dev, "SR1 after config: 0x%02x (WEL should be 0x00)\n", nor->bouncebuf[0]);
/* Setup Octal-STR with DS */
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR0V, SPINOR_MT_OCT_STR);
/* Status Register Read (05h) in Octal STR Mode (8s-0-8s) with 8 Dummies */
struct spi_mem_op op_rsr = SPI_MEM_OP(
/* Command: Opcode 05h, sent on 8 lanes */
SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 8),
/* Address: None */
SPI_MEM_OP_NO_ADDR,
/* Dummy: 8 cycles, sent on 8 lanes (as per your table) */
SPI_MEM_OP_DUMMY(8, 8),
/* Data: 1 byte (Status), received on 8 lanes */
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 8)
);
/* Execute using the framework helper with Octal-STR protocol */
ret = spi_nor_read_any_reg(nor, &op_rsr, SNOR_PROTO_8_8_8);
if (!ret) {
/* Access the result from the DMA-safe bounce buffer */
u8 status = nor->bouncebuf[0];
dev_info(nor->dev, "MRAM Status Register (8s-0-8s): 0x%02x\n", status);
}
dev_info(nor->dev, "Going to access Read ID (0x9f) in 8s-0-8s Mode ...\n");
/* Read ID (0x9f) access in Octal-STR mode (8s-0-8s) with 8 Dummy Bytes */
op_ri = (struct spi_mem_op)SPI_MEM_OP(
/* Opcode 0x9F, 1 Byte long, sent on 8 lanes */
SPI_MEM_OP_CMD(SPINOR_OP_RDID, 8),
/* No address phase */
SPI_MEM_OP_NO_ADDR,
/* 8 Dummy Cycles, sent on 8 lanes */
SPI_MEM_OP_DUMMY(8, 8),
/* Read 3 bytes of ID data, received on 8 lanes */
SPI_MEM_OP_DATA_IN(3, nor->bouncebuf, 8)
);
/* Protokoll 8-8-8 für 8s-0-8s (Octal STR) */
ret = spi_nor_read_any_reg(nor, &op_ri, SNOR_PROTO_8_8_8);
if (!ret) {
/* Access the 3 bytes from bouncebuf */
u8 manufacturer_id = nor->bouncebuf[0];
u8 memory_type = nor->bouncebuf[1];
u8 capacity = nor->bouncebuf[2];
/* Log the result in hex format */
dev_info(nor->dev, "MRAM INFO: Octal Read ID: %02x %02x %02x.\n",
manufacturer_id, memory_type, capacity);
} else {
dev_info(nor->dev, "Read ID Access in 8s-0-8s mode FAILED!\n");
}
}
/**
* everspin_mram_ready_noop - MRAM is always ready, no polling needed
*/
static int everspin_mram_ready_noop(struct spi_nor *nor)
{
return 1;
}
/**
* everspin_mram_late_init - Final 8-8-8 STR configuration for EM008LXO
*/
static int everspin_mram_late_init(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
dev_info(nor->dev, "Finalizing 8s-8s-8s STR: Write/Read fully functional.\n");
/* 1. Bypass core hwcaps restrictions */
params->hwcaps.mask |= SNOR_HWCAPS_READ | SNOR_HWCAPS_PP;
/* 2. Global Octal STR Protocol Settings */
nor->read_proto = SNOR_PROTO_8_8_8;
nor->write_proto = SNOR_PROTO_8_8_8;
nor->reg_proto = SNOR_PROTO_8_8_8;
nor->read_opcode = 0xCB;
nor->read_dummy = 8;
nor->program_opcode = 0x82;
nor->addr_nbytes = 3;
params->addr_nbytes = 3;
/* 3. Disable WIP Polling (Fixes the 40s Timeout) */
params->ready = everspin_mram_ready_noop;
/* 4. Align Page Size for Controller Stability */
/* Cadence OSPI handles 256-byte pages more reliably in Octal mode */
params->page_size = 256;
/* 5. Map Opcodes to standard slots for MTD core */
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
0, 8, 0xCB, SNOR_PROTO_8_8_8);
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
0x82, SNOR_PROTO_8_8_8);
return 0;
}
static const struct spi_nor_fixups everspin_mram_fixups = {
.default_init = everspin_mram_default_init,
.late_init = everspin_mram_late_init,
};
static const struct flash_info everspin_nor_parts[] = {
/* Everspin */
{ "mr25h128", CAT25_INFO(16 * 1024, 1, 256, 2) },
@@ -19,22 +239,28 @@ static const struct flash_info everspin_nor_parts[] = {
static const struct flash_info everspin_mram_parts[] = {
/* Everspin */
{ "em256lx", INFO(0x6bbb19, 0, 32 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em128lx", INFO(0x6bbb18, 0, 16 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em064lx", INFO(0x6bbb17, 0, 8 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em032lx", INFO(0x6bbb16, 0, 4 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em016lx", INFO(0x6bbb15, 0, 2 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em008lx", INFO(0x6bbb14, 0, 1 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
};