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 08e6c0d56c80..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 */ @@ -196,21 +199,24 @@ compatible = "jedec,spi-nor"; reg = <0x0>; - /* 80 MHz Target */ - spi-max-frequency = <80000000>; + /* 50 MHz for Startup */ + spi-max-frequency = <50000000>; - /* PHY mode is highly recommended for >50MHz on Cadence controllers */ + /* 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; - /* Read delay: Start with 0. - If you get bit errors, try cdns,read-delay = <1>; */ - cdns,read-delay = <0>; - - /* Tighten timings for 80MHz operation (T=12.5ns) */ - cdns,tshsl-ns = <25>; /* Chip Select high pulse width */ - cdns,tsd2d-ns = <25>; /* Delay between two back-to-back transfers */ - cdns,tchsh-ns = <6>; /* CS# hold time */ - cdns,tslch-ns = <6>; /* CS# setup time */ + /* 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>; diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c index de8f391a53d4..96eda6e3dc5c 100644 --- a/drivers/mtd/spi-nor/everspin.c +++ b/drivers/mtd/spi-nor/everspin.c @@ -9,10 +9,19 @@ #include #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 */ +/* 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 @@ -22,8 +31,8 @@ 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), + /* 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); @@ -32,8 +41,8 @@ static int everspin_mram_software_reset(struct spi_nor *nor) if (ret) return ret; - /* Software Reset (0x99) */ - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(0x99, 1), + /* 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); @@ -63,6 +72,47 @@ static int everspin_mram_write_reg(struct spi_nor *nor, u32 addr, u8 val) 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 */ @@ -92,7 +142,8 @@ static void everspin_mram_default_init(struct spi_nor *nor) */ everspin_mram_software_reset(nor); - dev_info(nor->dev, "Starting Everspin MRAM initialization ...\n"); + 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), @@ -108,8 +159,8 @@ static void everspin_mram_default_init(struct spi_nor *nor) everspin_mram_unlock(nor); } - /* Configuration: 8 dummy bytes */ - everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR1V, 8); + /* 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); @@ -127,8 +178,8 @@ static void everspin_mram_default_init(struct spi_nor *nor) /* Address: None */ SPI_MEM_OP_NO_ADDR, - /* Dummy: 8 cycles, sent on 8 lanes (as per your table) */ - SPI_MEM_OP_DUMMY(8, 8), + /* 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) @@ -152,8 +203,8 @@ static void everspin_mram_default_init(struct spi_nor *nor) /* No address phase */ SPI_MEM_OP_NO_ADDR, - /* 8 Dummy Cycles, sent on 8 lanes */ - SPI_MEM_OP_DUMMY(8, 8), + /* 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) @@ -183,6 +234,55 @@ 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 */ @@ -192,33 +292,39 @@ static int everspin_mram_late_init(struct spi_nor *nor) dev_info(nor->dev, "Finalizing 8s-8s-8s STR: Write/Read fully functional.\n"); - /* 1. Bypass core hwcaps restrictions */ + /* Bypass core hwcaps restrictions. + * We need this as a workaround. + */ params->hwcaps.mask |= SNOR_HWCAPS_READ | SNOR_HWCAPS_PP; - /* 2. Global Octal STR Protocol Settings */ + /* 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->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; - /* 3. Disable WIP Polling (Fixes the 40s Timeout) */ + /* Disable WIP Polling (Fixes the 40s Timeout) */ params->ready = everspin_mram_ready_noop; - /* 4. Align Page Size for Controller Stability */ + /* 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(¶ms->reads[SNOR_CMD_READ], - 0, 8, 0xCB, SNOR_PROTO_8_8_8); + /* 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], - 0x82, SNOR_PROTO_8_8_8); + SPINOR_OP_EV_OCTAL_PAGE_PROG, SNOR_PROTO_8_8_8); + + nor->params->setup = everspin_mram_setup; return 0; }