diff --git a/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi b/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi index b3e3a9e75c88..c4d4f05af7bb 100644 --- a/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi @@ -183,6 +183,9 @@ pinctrl-0 = <&ospi0_pins_default>; status = "okay"; + /* Setup the clock for 200 MHz (400 MHz Ref / 2) */ + assigned-clock-rates = <400000000>; + /* Everspin Tech. EM008LXO * Order-No EM008LXOAB320IS1R */ @@ -195,17 +198,29 @@ compatible = "jedec,spi-nor"; reg = <0x0>; - spi-tx-bus-width = <8>; - spi-rx-bus-width = <8>; - spi-max-frequency = <25000000>; - cdns,tshsl-ns = <60>; - cdns,tsd2d-ns = <60>; - cdns,tchsh-ns = <60>; - cdns,tslch-ns = <60>; - cdns,read-delay = <4>; + /* 50 MHz for Startup */ + spi-max-frequency = <50000000>; + + /* 200 MHz needs a very precise sampling point. + * Start with 4 or 5 since 125MHz already needed 3. + */ + cdns,read-delay = <5>; + + /* Enable PHY mode to assist controller internal clocking at + * high speeds + */ cdns,phy-mode; + /* Tighten timings for 200 MHz (T=5ns) */ + cdns,tshsl-ns = <200>; /* CS# high pulse width (increased for stability) */ + cdns,tsd2d-ns = <200>; /* Delay between back-to-back transfers */ + cdns,tchsh-ns = <3>; /* CS# hold time */ + cdns,tslch-ns = <3>; /* CS# setup time */ + + spi-rx-bus-width = <8>; + spi-tx-bus-width = <8>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; @@ -213,7 +228,7 @@ partition@0 { label = "ospi.mram.mem"; - reg = <0x0 0x800000>; + reg = <0x0 0x100000>; /* 1MB total capacity */ }; }; }; 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 diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c index d18d23ee7c9e..96eda6e3dc5c 100644 --- a/drivers/mtd/spi-nor/everspin.c +++ b/drivers/mtd/spi-nor/everspin.c @@ -5,9 +5,335 @@ */ #include - +#include +#include #include "core.h" +/* Optimization for 200 MHz: 20 Dummy Cycles are typically required */ +#define EVERSPIN_MRAM_DUMMY_CYCLES 8 +#define EVERSPIN_MRAM_DUMMY_CYCLES_FAST 20 + +#define EVERSPIN_MRAM_SPEED_MHZ 200000000 + +#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile configuration register */ +#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 */ +#define SPINOR_OP_EV_OCTAL_FAST_READ 0xCB /* Everspin Octal Fast Read (8-8-8) */ +#define SPINOR_OP_EV_OCTAL_PAGE_PROG 0x82 /* Everspin Octal Page Program (8-8-8) */ + +/** + * 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 (SPINOR_OP_SRSTEN: 0x66) */ + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 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 (SPINOR_OP_SRST: 0x99) */ + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 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_read_reg_octal - Reads a register in 8s-8s-8s mode + * Uses Opcode 0x85, 3-byte address, and 8 dummy cycles. + */ +static int everspin_mram_read_reg_octal(struct spi_nor *nor, u32 addr, u8 *val) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_RD_ANY_REG, 8), + SPI_MEM_OP_ADDR(3, addr, 8), + SPI_MEM_OP_DUMMY(8, 8), /* Fixed 8 dummies for reg read */ + SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 8)); + int ret; + + ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_8_8_8); + if (ret) + return ret; + + *val = nor->bouncebuf[0]; + return 0; +} + +/** + * everspin_mram_write_reg_octal - Writes a register in 8s-8s-8s mode + * Uses Opcode 0x81, 3-byte address, and 0 dummy cycles. + */ +static int everspin_mram_write_reg_octal(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, 8), + SPI_MEM_OP_ADDR(3, addr, 8), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 8)); + + nor->bouncebuf[0] = val; + + /* WREN is required before writing to volatile registers */ + spi_nor_write_enable(nor); + + return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8); +} + +/** + * 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 (Octal-STR, %d Dummies) ...\n", + EVERSPIN_MRAM_DUMMY_CYCLES); + + /* 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: Set Dummy Cycles in CFR1V */ + everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR1V, EVERSPIN_MRAM_DUMMY_CYCLES); + + /* 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, + + /* EVERSPIN_MRAM_DUMMY_CYCLES cycles, sent on 8 lanes */ + SPI_MEM_OP_DUMMY(EVERSPIN_MRAM_DUMMY_CYCLES, 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, + + /* EVERSPIN_MRAM_DUMMY_CYCLES Dummy Cycles, sent on 8 lanes */ + SPI_MEM_OP_DUMMY(EVERSPIN_MRAM_DUMMY_CYCLES, 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_setup - Finalizes the SPI setup and escalates frequency + * @nor: pointer to 'struct spi_nor' + * @hwcaps: pointer to 'struct spi_nor_hwcaps' + * + * This hook is called after default_init/late_init but before the first + * data transfer. It is the ideal place to switch from safe scan frequency + * to high-performance operational frequency. + */ +static int everspin_mram_setup(struct spi_nor *nor, + const struct spi_nor_hwcaps *hwcaps) +{ + struct spi_nor_flash_parameter *params = nor->params; + struct spi_device *spi = nor->spimem->spi; + int ret; + u8 val = 0; + + /* + * Request high speed frequency. + */ + spi->max_speed_hz = EVERSPIN_MRAM_SPEED_MHZ; + + dev_info(nor->dev, "Escalating speed to %u Hz in setup hook\n", + spi->max_speed_hz); + + /* Update Dummy Bytes to serve the higher rate EVERSPIN_MRAM_SPEED_MHZ */ + nor->read_dummy = EVERSPIN_MRAM_DUMMY_CYCLES_FAST; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], 0, + EVERSPIN_MRAM_DUMMY_CYCLES_FAST, + SPINOR_OP_EV_OCTAL_FAST_READ, SNOR_PROTO_8_8_8); + everspin_mram_write_reg_octal(nor, SPINOR_REG_MT_CFR1V, + EVERSPIN_MRAM_DUMMY_CYCLES_FAST); + everspin_mram_read_reg_octal(nor, SPINOR_REG_MT_CFR1V, &val); + + dev_info(nor->dev, "Updating Dummy Bytes to %d (read: %d)\n", nor->read_dummy, val); + + /* + * Force the controller driver (cadence-qspi) to re-run + * cqspi_config_baudrate_div() and update hardware registers. + */ + ret = spi_setup(spi); + if (ret) { + dev_err(nor->dev, "Failed to update SPI setup (%d)\n", ret); + return ret; + } + + return 0; +} + +/** + * 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"); + + /* Bypass core hwcaps restrictions. + * We need this as a workaround. + */ + params->hwcaps.mask |= SNOR_HWCAPS_READ | SNOR_HWCAPS_PP; + + /* 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 = SPINOR_OP_EV_OCTAL_FAST_READ; + /* Set global read dummy cycles */ + nor->read_dummy = EVERSPIN_MRAM_DUMMY_CYCLES; + nor->program_opcode = SPINOR_OP_EV_OCTAL_PAGE_PROG; + nor->addr_nbytes = 3; + params->addr_nbytes = 3; + + /* Disable WIP Polling (Fixes the 40s Timeout) */ + params->ready = everspin_mram_ready_noop; + + /* Align Page Size for Controller Stability */ + /* Cadence OSPI handles 256-byte pages more reliably in Octal mode */ + params->page_size = 256; + + /* Map Opcodes to standard slots for MTD core */ + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], 0, + EVERSPIN_MRAM_DUMMY_CYCLES, + SPINOR_OP_EV_OCTAL_FAST_READ, SNOR_PROTO_8_8_8); + + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], + SPINOR_OP_EV_OCTAL_PAGE_PROG, SNOR_PROTO_8_8_8); + + nor->params->setup = everspin_mram_setup; + + 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 +345,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, }, }; diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 3e0480aadf9d..541afcf32f9a 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1195,6 +1195,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi) const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz; void __iomem *reg_base = cqspi->iobase; u32 reg, div; + u32 actual_clk; /* Recalculate the baudrate divisor based on QSPI specification. */ div = DIV_ROUND_UP(ref_clk_hz, 2 * cqspi->sclk) - 1; @@ -1207,10 +1208,25 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi) cqspi->sclk, ref_clk_hz/((div+1)*2)); } + /* Calculate actual clock for logging purposes */ + actual_clk = ref_clk_hz / (2 * (div + 1)); + + dev_info(&cqspi->pdev->dev, + "CQSPI Clock Config: Ref=%u Hz, Target=%u Hz, Divisor=%u, Result=%u Hz\n", + ref_clk_hz, cqspi->sclk, div, actual_clk); + reg = readl(reg_base + CQSPI_REG_CONFIG); + + /* Log old register value for deep debugging */ + dev_info(&cqspi->pdev->dev, "Old CONFIG_REG: 0x%08x\n", reg); + reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); reg |= (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + dev_info(&cqspi->pdev->dev, "New CONFIG_REG: 0x%08x (BaudDiv field updated)\n", + readl(reg_base + CQSPI_REG_CONFIG)); } static void cqspi_readdata_capture(struct cqspi_st *cqspi,