save changes

Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
Heinrich Toews
2026-02-17 17:58:35 +01:00
parent fcd11f2818
commit 55219aec07
3 changed files with 117 additions and 128 deletions
@@ -195,7 +195,7 @@
compatible = "jedec,spi-nor";
reg = <0x0>;
spi-tx-bus-width = <1>;
spi-tx-bus-width = <8>;
spi-rx-bus-width = <8>;
spi-max-frequency = <25000000>;
+115 -126
View File
@@ -4,193 +4,182 @@
* Copyright (C) 2014, Freescale Semiconductor, Inc.
*/
#include <linux/delay.h>
#include <linux/mtd/spi-nor.h>
#include <linux/delay.h>
#include "core.h"
/* Opcodes and Register Definitions */
#define SPINOR_OP_EVERSPIN_WRAR 0x71 /* Write Any Register */
#define SPINOR_OP_EVERSPIN_RDAR 0x65 /* Read Any Register */
#define SPINOR_REG_EVERSPIN_VCR0 0x000000 /* Volatile Config Register 0 */
#define EVERSPIN_OCTAL_STR_B7 0xB7 /* Octal STR with Data Strobe */
/* Opcodes and Registers based on patch and successful tests */
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */
#define SPINOR_REG_MT_CFR0V 0x00 /* Mode configuration */
#define SPINOR_REG_MT_CFR1V 0x01 /* Dummy cycle configuration */
#define SPINOR_MT_OCT_STR 0x97 /* Octal STR without DS */
#define SPINOR_OP_EVERSPIN_WRAR 0x71
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_EVERSPIN_WRAR, 1),
SPI_MEM_OP_ADDR(3, addr, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
*nor->bouncebuf = val;
spi_nor_write_enable(nor);
return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
}
#if 0
/**
* everspin_mram_write_reg - Helper to write to volatile/non-volatile registers
* everspin_mram_write_reg - Uses Opcode 0x81 and 3-byte address
*/
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_EVERSPIN_WRAR, 1),
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;
/* Send WREN before EVERY register write */
/* RICHTIG: Wert in den Puffer schreiben */
*nor->bouncebuf = val;
spi_nor_write_enable(nor);
return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
}
/**
* everspin_mram_unlock - Clears Block Protection bits in Status Register 1
*/
static int everspin_mram_unlock(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
/* Send Write Enable (WREN) */
spi_nor_write_enable(nor);
/* Write 0x00 to Status Register 1 (Opcode 01h) */
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));
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* Verify: Read back SR1 */
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, nor->reg_proto);
if (ret)
return ret;
/* Check value at pointer address */
if (nor->bouncebuf[0] & GENMASK(5, 2)) {
dev_err(nor->dev, "Unlock failed! SR1 still 0x%02x\n", nor->bouncebuf[0]);
return -EACCES;
}
dev_info(nor->dev, "Unlock successful. SR1 is now 0x%02x\n", nor->bouncebuf[0]);
return 0;
}
#endif
static void everspin_mram_default_init(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
dev_info(nor->dev, "Starting Everspin MRAM initialization (STR Octal)...\n");
dev_info(nor->dev, "Initializing Everspin MRAM EM008LX (Octal STR)...\n");
/* 1. Read SR1 and unlock if necessary */
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
/* Auf R2 ausführen, um die permanenten (Non-Volatile) Werte zu lesen */
u8 nv_registers[] = {0x00, 0x01, 0x02, 0x03, 0x04};
dev_info(nor->dev, "Dumping Non-Volatile (NV) Registers from R2...\n");
for (int i = 0; i < 5; i++) {
/* Opcode 0x65 ist RDAR (Read Any Register) für NV-Bereiche */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(0x65, 1),
SPI_MEM_OP_ADDR(3, i, 1),
SPI_MEM_OP_DUMMY(8, 1),
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
/* Wichtig: Da R2 im Octal-Modus ist, nutzen wir SNOR_PROTO_8_8_8 */
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_8_8_8);
if (!ret)
dev_info(nor->dev, "R2 NV-Register 0x%02x: 0x%02x\n", i, *nor->bouncebuf);
else
dev_err(nor->dev, "Failed to read NV-Register 0x%02x\n", i);
}
/* Auf R2 ausführen, um Werte zu vergleichen */
u8 registers[] = {0x00, 0x01, 0x02, 0x03, 0x04}; // VCR0 bis VCR4
for (int i = 0; i < 5; i++) {
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(0x85, 1), // RDAR
SPI_MEM_OP_ADDR(3, i, 1),
SPI_MEM_OP_DUMMY(8, 1),
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
spi_nor_read_any_reg(nor, &op, SNOR_PROTO_8_8_8);
dev_info(nor->dev, "R2 VCR%d (at 0x%02x): 0x%02x\n", i, i, *nor->bouncebuf);
}
/* RICHTIG: Cast auf (struct spi_mem_op) hinzugefügt */
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, nor->reg_proto);
if (!ret) {
u8 sr1 = nor->bouncebuf[0];
if (sr1 & GENMASK(5, 2)) {
dev_info(nor->dev, "SR1: 0x%02x | Protection detected, unlocking...\n", sr1);
everspin_mram_unlock(nor);
} else {
dev_info(nor->dev, "SR1: 0x%02x | No protection active.\n", sr1);
}
u8 sr1 = *nor->bouncebuf;
dev_info(nor->dev, "Initial SR1: 0x%02x (BP-Bits: 0x%x)\n",
sr1, (sr1 & 0x3c) >> 2);
}
/* 2. Switch to Octal Mode using the correct VCR0 address and value */
dev_info(nor->dev, "Setting VCR0 to Octal STR (0xB7) at address 0x000000...\n");
/* 1. Set the Address Width to 3 Bytes (mandatory for 1MB MRAM) */
nor->params->addr_nbytes = 3;
nor->addr_nbytes = 3;
nor->addr_nbytes = 3;
#if 0
/* IMPORTANT: Use 0xB7 for Octal with Data Strobe */
ret = everspin_mram_write_reg(nor, SPINOR_REG_EVERSPIN_VCR0, EVERSPIN_OCTAL_STR_B7);
/* Register-Schreibvorgänge (WREN ist in everspin_mram_write_reg enthalten) */
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR1V, 0x08);
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR0V, SPINOR_MT_OCT_STR);
/* WICHTIG: Da der Chip nun im Octal-Modus ist, müssen wir das
Protokoll für den folgenden RDSR-Check umstellen! */
if (ret) {
dev_err(nor->dev, "Error writing VCR0 register (%d)\n", ret);
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_DUMMY(8, 1), /* Octal-SR braucht oft Dummies */
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
/* Wir lesen jetzt mit dem Protokoll, das der Chip nun erwartet */
/* Nutze SNOR_PROTO_8_8_8 für Octal STR */
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_8_8_8);
if (!ret) {
u8 sr1_after = *nor->bouncebuf;
dev_info(nor->dev, "SR1 after Init (Octal Read): 0x%02x\n", sr1_after);
} else {
dev_info(nor->dev, "VCR0 successfully set to Octal STR.\n");
/* Give the chip a few microseconds to settle the new mode */
udelay(100);
/* Read SR1 again to see if WEL is cleared after CFR1V write */
spi_nor_read_any_reg(nor, &op, nor->reg_proto);
dev_info(nor->dev, "SR1 after VCR0 write: 0x%02x\n", nor->bouncebuf[0]);
dev_err(nor->dev, "Failed to read SR1 in Octal mode (ret %d)\n", ret);
}
#endif
/* 3. Ensure NO 4-byte mode is activated */
nor->flags &= ~SNOR_F_4B_OPCODES;
nor->params->set_4byte_addr_mode = NULL;
/* 3. Synchronize kernel parameters */
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
nor->params->rdsr_dummy = 8;
nor->params->rdsr_addr_nbytes = 0;
nor->params->addr_nbytes = 4;
nor->params->set_4byte_addr_mode = NULL;
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->params->quad_enable = NULL;
nor->params->addr_nbytes = 3;
// Manche Cadence-Implementationen brauchen ein explizites Byte-Alignment
nor->program_opcode = SPINOR_OP_PP;
/* 1. Protokolle fest auf Octal setzen, damit der Core nicht wechselt */
nor->reg_proto = SNOR_PROTO_8_8_8;
nor->read_proto = SNOR_PROTO_8_8_8;
nor->write_proto = SNOR_PROTO_8_8_8;
dev_info(nor->dev, "Standard parameters for OSPI controller set.\n");
/* 2. SFDP Parsing und 4-Byte-Modus komplett unterdrücken */
nor->flags &= ~SNOR_F_4B_OPCODES;
nor->flags |= SPI_NOR_SKIP_SFDP; // Falls in deiner Kernel-Version verfügbar
nor->params->set_4byte_addr_mode = NULL;
/* 3. WICHTIG: Die Dummy-Zyklen für das MTD-System global setzen */
nor->params->rdsr_dummy = 8;
dev_info(nor->dev, "MRAM initialized. Protocols forced to Octal.\n");
}
static int everspin_mram_late_init(struct spi_nor *nor)
{
/* Read: Octal STR (1-8-8) */
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_1_8_8;
/* Versuche SPINOR_OP_READ_1_8_8 (meist 0xFD) */
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_1_8_8],
0, 8, 0xFD, SNOR_PROTO_1_8_8);
/* Write: Octal STR (1-8-8) */
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_1_8_8;
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_1_8_8],
0x12, SNOR_PROTO_1_8_8);
return 0;
}
#if 0
static int everspin_mram_late_init(struct spi_nor *nor)
{
dev_info(nor->dev, "Configuring Octal opcodes for Read/Write...\n");
/* Read Settings: 1-8-8 STR */
/* Read/Write Settings: Octal STR (1-8-8) */
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_1_8_8;
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_1_8_8],
0, 8, SPINOR_OP_READ_1_8_8, SNOR_PROTO_1_8_8);
/* Write Settings: 1-8-8 STR */
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_1_8_8;
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_1_8_8],
SPINOR_OP_PP_1_8_8, SNOR_PROTO_1_8_8);
/* In late_init: Versuchen Sie 0x82 statt 0x12, falls vorhanden */
nor->params->page_programs[SNOR_CMD_PP_1_8_8].opcode = 0x12;
dev_info(nor->dev, "Everspin MRAM successfully initialized in Octal mode.\n");
return 0;
}
#endif
static int everspin_mram_late_init(struct spi_nor *nor)
{
dev_info(nor->dev, "Configuring Hybrid Mode: Octal Read / Single Write...\n");
/* 1. Fast Read Settings: Octal STR (1-8-8) */
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_1_8_8;
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_1_8_8],
0, 8, SPINOR_OP_READ_1_8_8, SNOR_PROTO_1_8_8);
/* 2. Stable Write Settings: Force Single-SPI (Standard Page Program) */
/* We disable Octal-Write and enable standard PP */
nor->params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_8_8;
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP;
/* Standard Page Program uses Opcode 02h and Protocol 1-1-1 */
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP,
SNOR_PROTO_1_1_1);
dev_info(nor->dev, "Hybrid Mode initialized: Read 8-8-8, Write 1-1-1.\n");
return 0;
}
static const struct spi_nor_fixups everspin_mram_fixups = {
.default_init = everspin_mram_default_init,
.late_init = everspin_mram_late_init,