From b6642bb99657a27691be178d9addbf1f6d631791 Mon Sep 17 00:00:00 2001 From: Marcin Szymczyk Date: Fri, 7 Feb 2025 15:01:18 +0100 Subject: [PATCH] drivers: flash: mspi_nor: support MODE_SINGLE and MODE_QUAD_1_4_4 Extend driver to support single lane and 1-4-4 IO modes. Move flash chip quirks to a separate file. Signed-off-by: Marcin Szymczyk --- drivers/flash/flash_mspi_nor.c | 552 +++++++++++++++++--------- drivers/flash/flash_mspi_nor.h | 248 ++++++++++++ drivers/flash/flash_mspi_nor_quirks.h | 185 +++++++++ 3 files changed, 798 insertions(+), 187 deletions(-) create mode 100644 drivers/flash/flash_mspi_nor.h create mode 100644 drivers/flash/flash_mspi_nor_quirks.h diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index a5ced1e0070..140a5300308 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -6,47 +6,56 @@ #define DT_DRV_COMPAT jedec_mspi_nor -#include #include -#include #include #include #include -#include "jesd216.h" -#include "spi_nor.h" +#include "flash_mspi_nor.h" +#include "flash_mspi_nor_quirks.h" LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); -#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) -#define WITH_RESET_GPIO 1 -#endif +void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_nor_cmd *cmd) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + const struct flash_mspi_nor_config *dev_config = dev->config; -struct flash_mspi_nor_data { - struct k_sem acquired; - struct mspi_xfer_packet packet; - struct mspi_xfer xfer; -}; + memset(&dev_data->xfer, 0, sizeof(dev_data->xfer)); + memset(&dev_data->packet, 0, sizeof(dev_data->packet)); -struct flash_mspi_nor_config { - const struct device *bus; - uint32_t flash_size; - struct mspi_dev_id mspi_id; - struct mspi_dev_cfg mspi_cfg; - enum mspi_dev_cfg_mask mspi_cfg_mask; -#if defined(CONFIG_MSPI_XIP) - struct mspi_xip_cfg xip_cfg; -#endif -#if defined(WITH_RESET_GPIO) - struct gpio_dt_spec reset; - uint32_t reset_pulse_us; - uint32_t reset_recovery_us; -#endif -#if defined(CONFIG_FLASH_PAGE_LAYOUT) - struct flash_pages_layout layout; -#endif - uint8_t jedec_id[SPI_NOR_MAX_ID_LEN]; -}; + dev_data->xfer.xfer_mode = MSPI_PIO; + dev_data->xfer.packets = &dev_data->packet; + dev_data->xfer.num_packet = 1; + dev_data->xfer.timeout = 10; + + dev_data->xfer.cmd_length = cmd->cmd_length; + dev_data->xfer.addr_length = cmd->addr_length; + dev_data->xfer.tx_dummy = (cmd->dir == MSPI_TX) ? + cmd->tx_dummy : dev_config->mspi_nor_cfg.tx_dummy; + dev_data->xfer.rx_dummy = (cmd->dir == MSPI_RX) ? + cmd->rx_dummy : dev_config->mspi_nor_cfg.rx_dummy; + + dev_data->packet.dir = cmd->dir; + dev_data->packet.cmd = cmd->cmd; +} + +static int dev_cfg_apply(const struct device *dev, const struct mspi_dev_cfg *cfg) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + if (dev_data->curr_cfg == cfg) { + return 0; + } + + int rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_ALL, cfg); + if (rc < 0) { + LOG_ERR("Failed to set device config: %p error: %d", cfg, rc); + } + return rc; +} static int acquire(const struct device *dev) { @@ -64,8 +73,8 @@ static int acquire(const struct device *dev) * if needed for the flash device. */ rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - dev_config->mspi_cfg_mask, - &dev_config->mspi_cfg); + dev_config->mspi_nor_cfg_mask, + &dev_config->mspi_nor_cfg); if (rc < 0) { LOG_ERR("mspi_dev_config() failed: %d", rc); } else { @@ -125,12 +134,18 @@ static int api_read(const struct device *dev, off_t addr, void *dest, return rc; } + if (dev_config->jedec_cmds->read.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + return rc; + } + /* TODO: get rid of all these hard-coded values for MX25Ux chips */ - dev_data->xfer.cmd_length = 2; - dev_data->xfer.addr_length = 4; - dev_data->xfer.rx_dummy = 20; - dev_data->packet.dir = MSPI_RX; - dev_data->packet.cmd = SPI_NOR_OCMD_RD; + flash_mspi_command_set(dev, &dev_config->jedec_cmds->read); dev_data->packet.address = addr; dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = size; @@ -140,35 +155,58 @@ static int api_read(const struct device *dev, off_t addr, void *dest, release(dev); if (rc < 0) { - LOG_ERR("SPI_NOR_OCMD_RD xfer failed: %d", rc); + LOG_ERR("Read xfer failed: %d", rc); return rc; } return 0; } -static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) +static int status_get(const struct device *dev, uint8_t *status) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; - uint8_t status_reg; int rc; + /* Enter command mode */ + if (dev_config->jedec_cmds->status.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + LOG_ERR("Switching to dev_cfg failed: %d", rc); + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->status); + dev_data->packet.data_buf = status; + dev_data->packet.num_bytes = sizeof(uint8_t); + + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + + if (rc < 0) { + LOG_ERR("Status xfer failed: %d", rc); + return rc; + } + + return rc; +} + +static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) +{ + int rc; + uint8_t status_reg; + while (true) { - dev_data->xfer.cmd_length = 2; - dev_data->xfer.addr_length = 4; - dev_data->xfer.rx_dummy = 4; - dev_data->packet.dir = MSPI_RX; - dev_data->packet.cmd = SPI_NOR_OCMD_RDSR; - dev_data->packet.address = 0; - dev_data->packet.data_buf = &status_reg; - dev_data->packet.num_bytes = sizeof(status_reg); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = status_get(dev, &status_reg); + if (rc < 0) { - LOG_ERR("SPI_NOR_OCMD_RDSR xfer failed: %d", rc); + LOG_ERR("Wait until ready - status xfer failed: %d", rc); return rc; } + if (!(status_reg & SPI_NOR_WIP_BIT)) { break; } @@ -179,6 +217,26 @@ static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) return 0; } +static int write_enable(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + if (dev_config->jedec_cmds->write_en.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->write_en); + return mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); +} + static int api_write(const struct device *dev, off_t addr, const void *src, size_t size) { @@ -207,29 +265,29 @@ static int api_write(const struct device *dev, off_t addr, const void *src, uint16_t page_left = page_size - page_offset; uint16_t to_write = (uint16_t)MIN(size, page_left); - dev_data->xfer.cmd_length = 2; - dev_data->xfer.tx_dummy = 0; - dev_data->packet.dir = MSPI_TX; - - dev_data->xfer.addr_length = 0; - dev_data->packet.cmd = SPI_NOR_OCMD_WREN; - dev_data->packet.num_bytes = 0; - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); - if (rc < 0) { - LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc); + if (write_enable(dev) < 0) { + LOG_ERR("Write enable xfer failed: %d", rc); break; } - dev_data->xfer.addr_length = 4; - dev_data->packet.cmd = SPI_NOR_OCMD_PAGE_PRG; + if (dev_config->jedec_cmds->page_program.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->page_program); dev_data->packet.address = addr; dev_data->packet.data_buf = (uint8_t *)src; dev_data->packet.num_bytes = to_write; rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); if (rc < 0) { - LOG_ERR("SPI_NOR_OCMD_PAGE_PRG xfer failed: %d", rc); + LOG_ERR("Page program xfer failed: %d", rc); break; } @@ -273,32 +331,40 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) } while (size > 0) { - dev_data->xfer.cmd_length = 2; - dev_data->xfer.tx_dummy = 0; - dev_data->packet.dir = MSPI_TX; - dev_data->packet.num_bytes = 0; - - dev_data->xfer.addr_length = 0; - dev_data->packet.cmd = SPI_NOR_OCMD_WREN; - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = write_enable(dev); if (rc < 0) { - LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc); + LOG_ERR("Write enable failed."); break; } if (size == flash_size) { /* Chip erase. */ - dev_data->xfer.addr_length = 0; - dev_data->packet.cmd = SPI_NOR_OCMD_CE; + if (dev_config->jedec_cmds->chip_erase.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->chip_erase); size -= flash_size; } else { /* Sector erase. */ - dev_data->xfer.addr_length = 4; - dev_data->packet.cmd = SPI_NOR_OCMD_SE; - dev_data->packet.address = addr; + if (dev_config->jedec_cmds->sector_erase.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->sector_erase); + dev_data->packet.address = addr; addr += SPI_NOR_SECTOR_SIZE; size -= SPI_NOR_SECTOR_SIZE; } @@ -340,19 +406,24 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) struct flash_mspi_nor_data *dev_data = dev->data; int rc; - dev_data->xfer.cmd_length = 2; - dev_data->xfer.addr_length = 4; - dev_data->xfer.rx_dummy = 4; - dev_data->packet.dir = MSPI_RX; - dev_data->packet.cmd = JESD216_OCMD_READ_ID; - dev_data->packet.address = 0; + if (dev_config->jedec_cmds->id.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->id); dev_data->packet.data_buf = id; dev_data->packet.num_bytes = JESD216_READ_ID_LEN; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); if (rc < 0) { - printk("mspi_transceive() failed: %d\n", rc); - return rc; + LOG_ERR("Read JEDEC ID failed: %d\n", rc); } return rc; @@ -387,18 +458,24 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, return rc; } - dev_data->xfer.cmd_length = 2; - dev_data->xfer.addr_length = 4; - dev_data->xfer.rx_dummy = 20; - dev_data->packet.dir = MSPI_RX; - dev_data->packet.cmd = JESD216_OCMD_READ_SFDP; + if (dev_config->jedec_cmds->sfdp.force_single) { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + } else { + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + } + + if (rc < 0) { + return rc; + } + + flash_mspi_command_set(dev, &dev_config->jedec_cmds->sfdp); dev_data->packet.address = addr; dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = size; rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); if (rc < 0) { - printk("JESD216_OCMD_READ_SFDP xfer failed: %d\n", rc); + printk("Read SFDP xfer failed: %d\n", rc); return rc; } @@ -409,9 +486,7 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, static int api_read_jedec_id(const struct device *dev, uint8_t *id) { - int rc = 0; - - rc = acquire(dev); + int rc = acquire(dev); if (rc < 0) { return rc; } @@ -439,85 +514,191 @@ static int dev_pm_action_cb(const struct device *dev, return 0; } +static int quad_enable_set(const struct device *dev, bool enable) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("Failed to set write enable: %d", rc); + return rc; + } + + if (dev_config->dw15_qer == JESD216_DW15_QER_VAL_S1B6) { + const struct flash_mspi_nor_cmd cmd_status = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_WRSR, + .cmd_length = 1, + }; + uint8_t mode_payload = enable ? BIT(6) : 0; + + flash_mspi_command_set(dev, &cmd_status); + dev_data->packet.data_buf = &mode_payload; + dev_data->packet.num_bytes = sizeof(mode_payload); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + + if (rc < 0) { + LOG_ERR("Failed to enable/disable quad mode: %d", rc); + return rc; + } + } else { + /* TODO: handle all DW15 QER values */ + return -ENOTSUP; + } + + rc = wait_until_ready(dev, K_USEC(1)); + + if (rc < 0) { + LOG_ERR("Failed waiting until device ready after enabling quad: %d", rc); + return rc; + } + + return 0; +} + + +static int default_io_mode(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; + int rc = 0; + + /* For Quad 1-1-4 and 1-4-4, entering or leaving mode is defined in JEDEC216 BFP DW15 QER */ + if (io_mode == MSPI_IO_MODE_SINGLE) { + rc = quad_enable_set(dev, false); + } else if ((io_mode == MSPI_IO_MODE_QUAD_1_1_4) || (io_mode == MSPI_IO_MODE_QUAD_1_4_4)) { + rc = quad_enable_set(dev, true); + } + + if (rc < 0) { + LOG_ERR("Failed to modify Quad Enable bit: %d", rc); + } + + if ((dev_config->quirks != NULL) && (dev_config->quirks->post_switch_mode != NULL)) { + rc = dev_config->quirks->post_switch_mode(dev); + } + + if (rc < 0) { + LOG_ERR("Failed to change IO mode: %d\n", rc); + return rc; + } + + return dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); +} + +#if defined(WITH_RESET_GPIO) +static int gpio_reset(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + int rc; + + if (dev_config->reset.port) { + if (!gpio_is_ready_dt(&dev_config->reset)) { + LOG_ERR("Device %s is not ready", + dev_config->reset.port->name); + return -ENODEV; + } + + rc = gpio_pin_configure_dt(&dev_config->reset, + GPIO_OUTPUT_ACTIVE); + if (rc < 0) { + LOG_ERR("Failed to activate RESET: %d", rc); + return -EIO; + } + + if (dev_config->reset_pulse_us != 0) { + k_busy_wait(dev_config->reset_pulse_us); + } + + rc = gpio_pin_set_dt(&dev_config->reset, 0); + if (rc < 0) { + LOG_ERR("Failed to deactivate RESET: %d", rc); + return -EIO; + } + + if (dev_config->reset_recovery_us != 0) { + k_busy_wait(dev_config->reset_recovery_us); + } + } + + return 0; +} +#endif + static int flash_chip_init(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; - struct mspi_dev_cfg init_dev_cfg = dev_config->mspi_cfg; + enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; uint8_t id[JESD216_READ_ID_LEN] = {0}; int rc; - init_dev_cfg.freq = MHZ(1); - init_dev_cfg.io_mode = MSPI_IO_MODE_SINGLE; + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); - rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, &init_dev_cfg); if (rc < 0) { - LOG_ERR("Failed to set initial device config: %d", rc); return rc; } - dev_data->xfer.xfer_mode = MSPI_PIO; - dev_data->xfer.packets = &dev_data->packet; - dev_data->xfer.num_packet = 1; - dev_data->xfer.timeout = 10; + /* Some chips reuse RESET pin for data in Quad modes: + * force single line mode before resetting. + */ + if ((io_mode == MSPI_IO_MODE_SINGLE) || (io_mode == MSPI_IO_MODE_QUAD_1_1_4) || + (io_mode == MSPI_IO_MODE_QUAD_1_4_4)) { + rc = quad_enable_set(dev, false); - dev_data->xfer.cmd_length = 1; - dev_data->xfer.addr_length = 0; - dev_data->xfer.tx_dummy = 0; - dev_data->xfer.rx_dummy = 0; + if (rc < 0) { + LOG_ERR("Failed to switch to single line mode: %d", rc); + return rc; + } - dev_data->packet.dir = MSPI_RX; - dev_data->packet.cmd = JESD216_CMD_READ_ID; + rc = wait_until_ready(dev, K_USEC(1)); + + if (rc < 0) { + LOG_ERR("Failed waiting for device after switch to single line: %d", rc); + return rc; + } + } + +#if defined(WITH_RESET_GPIO) + rc = gpio_reset(dev); + + if (rc < 0) { + LOG_ERR("Failed to reset with GPIO: %d", rc); + return rc; + } +#endif + + flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].id); dev_data->packet.data_buf = id; dev_data->packet.num_bytes = sizeof(id); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); if (rc < 0) { - LOG_ERR("Failed to read JEDEC ID in single line mode: %d", rc); + LOG_ERR("Failed to read JEDEC ID in initial line mode: %d", rc); return rc; } - /* - * If the read ID does not match the one from DTS, assume the flash - * is already in the Octa I/O mode, so switching it is not needed. + rc = default_io_mode(dev); + + if (rc < 0) { + LOG_ERR("Failed to switch to default io mode: %d", rc); + return rc; + } + + /* Reading JEDEC ID for mode that forces single lane would be redundant, + * since it switches back to single lane mode. Use ID from previous read. */ - if (memcmp(id, dev_config->jedec_id, sizeof(id)) == 0) { - static const uint8_t enable_sopi[] = { 0x01 }; - - dev_data->packet.dir = MSPI_TX; - dev_data->packet.cmd = SPI_NOR_CMD_WREN; - dev_data->packet.num_bytes = 0; - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + if (!dev_config->jedec_cmds->id.force_single) { + rc = read_jedec_id(dev, id); if (rc < 0) { - LOG_ERR("SPI_NOR_CMD_WREN xfer failed: %d", rc); + LOG_ERR("Failed to read JEDEC ID in final line mode: %d", rc); return rc; } - - dev_data->xfer.addr_length = 4; - dev_data->packet.cmd = SPI_NOR_CMD_WR_CFGREG2; - dev_data->packet.address = 0; - dev_data->packet.data_buf = (uint8_t *)&enable_sopi; - dev_data->packet.num_bytes = sizeof(enable_sopi); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); - if (rc < 0) { - printk("SPI_NOR_CMD_WR_CFGREG2 xfer failed: %d\n", rc); - return rc; - } - } - - rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, &dev_config->mspi_cfg); - if (rc < 0) { - LOG_ERR("Failed to set device config: %d", rc); - return rc; - } - - rc = read_jedec_id(dev, id); - if (rc < 0) { - return rc; } if (memcmp(id, dev_config->jedec_id, sizeof(id)) != 0) { @@ -555,37 +736,6 @@ static int drv_init(const struct device *dev) return -ENODEV; } -#if defined(WITH_RESET_GPIO) - if (dev_config->reset.port) { - if (!gpio_is_ready_dt(&dev_config->reset)) { - LOG_ERR("Device %s is not ready", - dev_config->reset.port->name); - return -ENODEV; - } - - rc = gpio_pin_configure_dt(&dev_config->reset, - GPIO_OUTPUT_ACTIVE); - if (rc < 0) { - LOG_ERR("Failed to activate RESET: %d", rc); - return -EIO; - } - - if (dev_config->reset_pulse_us != 0) { - k_busy_wait(dev_config->reset_pulse_us); - } - - rc = gpio_pin_set_dt(&dev_config->reset, 0); - if (rc < 0) { - LOG_ERR("Failed to deactivate RESET: %d", rc); - return -EIO; - } - - if (dev_config->reset_recovery_us != 0) { - k_busy_wait(dev_config->reset_recovery_us); - } - } -#endif - rc = pm_device_runtime_get(dev_config->bus); if (rc < 0) { LOG_ERR("pm_device_runtime_get() failed: %d", rc); @@ -624,7 +774,27 @@ static DEVICE_API(flash, drv_api) = { #endif }; +#define FLASH_INITIAL_CONFIG(inst) \ +{ \ + .ce_num = DT_INST_PROP_OR(inst, mspi_hardware_ce_num, 0), \ + .freq = MIN(DT_INST_PROP(inst, mspi_max_frequency), MHZ(50)), \ + .io_mode = MSPI_IO_MODE_SINGLE, \ + .data_rate = MSPI_DATA_RATE_SINGLE, \ + .cpp = MSPI_CPP_MODE_0, \ + .endian = MSPI_XFER_BIG_ENDIAN, \ + .ce_polarity = MSPI_CE_ACTIVE_LOW, \ + .dqs_enable = false, \ +} + #define FLASH_SIZE_INST(inst) (DT_INST_PROP(inst, size) / 8) +#define FLASH_CMDS(inst) &commands[DT_INST_ENUM_IDX(inst, mspi_io_mode)] +#define FLASH_QUIRKS(inst) FLASH_MSPI_QUIRKS_GET(DT_DRV_INST(inst)) + +#define FLASH_DW15_QER_VAL(inst) _CONCAT(JESD216_DW15_QER_VAL_, \ + DT_INST_STRING_TOKEN(inst, quad_enable_requirements)) +#define FLASH_DW15_QER(inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, quad_enable_requirements), \ + (FLASH_DW15_QER_VAL(inst)), (JESD216_DW15_QER_VAL_NONE)) + #if defined(CONFIG_FLASH_PAGE_LAYOUT) BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0, @@ -651,17 +821,22 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == #endif #define FLASH_MSPI_NOR_INST(inst) \ - BUILD_ASSERT(DT_INST_ENUM_IDX(inst, mspi_io_mode) == \ - MSPI_IO_MODE_OCTAL, \ - "Only Octal I/O mode is supported for now"); \ + BUILD_ASSERT((DT_INST_ENUM_IDX(inst, mspi_io_mode) == \ + MSPI_IO_MODE_SINGLE) || \ + (DT_INST_ENUM_IDX(inst, mspi_io_mode) == \ + MSPI_IO_MODE_QUAD_1_4_4) || \ + (DT_INST_ENUM_IDX(inst, mspi_io_mode) == \ + MSPI_IO_MODE_OCTAL), \ + "Only 1x, 1-4-4 and 8x I/O modes are supported for now"); \ PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \ static struct flash_mspi_nor_data dev##inst##_data; \ static const struct flash_mspi_nor_config dev##inst##_config = { \ .bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \ .flash_size = FLASH_SIZE_INST(inst), \ .mspi_id = MSPI_DEVICE_ID_DT_INST(inst), \ - .mspi_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \ - .mspi_cfg_mask = DT_PROP(DT_INST_BUS(inst), \ + .mspi_nor_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \ + .mspi_nor_init_cfg = FLASH_INITIAL_CONFIG(inst), \ + .mspi_nor_cfg_mask = DT_PROP(DT_INST_BUS(inst), \ software_multiperipheral) \ ? MSPI_DEVICE_CONFIG_ALL \ : MSPI_DEVICE_CONFIG_NONE, \ @@ -675,6 +850,9 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == / 1000,)) \ FLASH_PAGE_LAYOUT_DEFINE(inst) \ .jedec_id = DT_INST_PROP(inst, jedec_id), \ + .jedec_cmds = FLASH_CMDS(inst), \ + .quirks = FLASH_QUIRKS(inst), \ + .dw15_qer = FLASH_DW15_QER(inst), \ }; \ FLASH_PAGE_LAYOUT_CHECK(inst) \ DEVICE_DT_INST_DEFINE(inst, \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h new file mode 100644 index 00000000000..42489606ffe --- /dev/null +++ b/drivers/flash/flash_mspi_nor.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_MSPI_NOR_H__ +#define __FLASH_MSPI_NOR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "jesd216.h" +#include "spi_nor.h" + +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) +#define WITH_RESET_GPIO 1 +#endif + +struct flash_mspi_nor_config { + const struct device *bus; + uint32_t flash_size; + struct mspi_dev_id mspi_id; + struct mspi_dev_cfg mspi_nor_cfg; + struct mspi_dev_cfg mspi_nor_init_cfg; + enum mspi_dev_cfg_mask mspi_nor_cfg_mask; +#if defined(CONFIG_MSPI_XIP) + struct mspi_xip_cfg xip_cfg; +#endif +#if defined(WITH_RESET_GPIO) + struct gpio_dt_spec reset; + uint32_t reset_pulse_us; + uint32_t reset_recovery_us; +#endif +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + struct flash_pages_layout layout; +#endif + uint8_t jedec_id[SPI_NOR_MAX_ID_LEN]; + struct flash_mspi_nor_cmds *jedec_cmds; + struct flash_mspi_nor_quirks *quirks; + uint8_t dw15_qer; +}; + +struct flash_mspi_nor_data { + struct k_sem acquired; + struct mspi_xfer_packet packet; + struct mspi_xfer xfer; + struct mspi_dev_cfg *curr_cfg; +}; + +struct flash_mspi_nor_cmd { + enum mspi_xfer_direction dir; + uint32_t cmd; + uint16_t tx_dummy; + uint16_t rx_dummy; + uint8_t cmd_length; + uint8_t addr_length; + bool force_single; +}; + +struct flash_mspi_nor_cmds { + struct flash_mspi_nor_cmd id; + struct flash_mspi_nor_cmd write_en; + struct flash_mspi_nor_cmd read; + struct flash_mspi_nor_cmd status; + struct flash_mspi_nor_cmd config; + struct flash_mspi_nor_cmd page_program; + struct flash_mspi_nor_cmd sector_erase; + struct flash_mspi_nor_cmd chip_erase; + struct flash_mspi_nor_cmd sfdp; +}; + +struct flash_mspi_nor_cmds commands[] = { + [MSPI_IO_MODE_SINGLE] = { + .id = { + .dir = MSPI_RX, + .cmd = JESD216_CMD_READ_ID, + .cmd_length = 1, + }, + .write_en = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_WREN, + .cmd_length = 1, + }, + .read = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_READ_FAST, + .cmd_length = 1, + .addr_length = 3, + .rx_dummy = 8, + }, + .status = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_RDSR, + .cmd_length = 1, + }, + .config = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_RDCR, + .cmd_length = 1, + }, + .page_program = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_PP, + .cmd_length = 1, + .addr_length = 3, + }, + .sector_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_SE, + .cmd_length = 1, + .addr_length = 3, + }, + .chip_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_CE, + .cmd_length = 1, + }, + .sfdp = { + .dir = MSPI_RX, + .cmd = JESD216_CMD_READ_SFDP, + .cmd_length = 1, + .addr_length = 3, + .rx_dummy = 8, + }, + }, + [MSPI_IO_MODE_QUAD_1_4_4] = { + .id = { + .dir = MSPI_RX, + .cmd = JESD216_CMD_READ_ID, + .cmd_length = 1, + .force_single = true, + }, + .write_en = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_WREN, + .cmd_length = 1, + }, + .read = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_4READ, + .cmd_length = 1, + .addr_length = 3, + .rx_dummy = 6, + }, + .status = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_RDSR, + .cmd_length = 1, + .force_single = true, + }, + .config = { + .dir = MSPI_RX, + .cmd = SPI_NOR_CMD_RDCR, + .cmd_length = 1, + .force_single = true, + }, + .page_program = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_PP_1_4_4, + .cmd_length = 1, + .addr_length = 3, + }, + .sector_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_SE, + .cmd_length = 1, + .addr_length = 3, + .force_single = true, + }, + .chip_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_CE, + .cmd_length = 1, + }, + .sfdp = { + .dir = MSPI_RX, + .cmd = JESD216_CMD_READ_SFDP, + .cmd_length = 1, + .addr_length = 3, + .rx_dummy = 8, + .force_single = true, + }, + }, + [MSPI_IO_MODE_OCTAL] = { + .id = { + .dir = MSPI_RX, + .cmd = JESD216_OCMD_READ_ID, + .cmd_length = 2, + .addr_length = 4, + .rx_dummy = 4 + }, + .write_en = { + .dir = MSPI_TX, + .cmd = SPI_NOR_OCMD_WREN, + .cmd_length = 2, + }, + .read = { + .dir = MSPI_RX, + .cmd = SPI_NOR_OCMD_RD, + .cmd_length = 2, + .addr_length = 4, + .rx_dummy = 20, + }, + .status = { + .dir = MSPI_RX, + .cmd = SPI_NOR_OCMD_RDSR, + .cmd_length = 2, + .addr_length = 4, + .rx_dummy = 4, + }, + .page_program = { + .dir = MSPI_TX, + .cmd = SPI_NOR_OCMD_PAGE_PRG, + .cmd_length = 2, + .addr_length = 4, + }, + .sector_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_OCMD_SE, + .cmd_length = 2, + .addr_length = 4, + }, + .chip_erase = { + .dir = MSPI_TX, + .cmd = SPI_NOR_OCMD_CE, + .cmd_length = 2, + }, + .sfdp = { + .dir = MSPI_RX, + .cmd = JESD216_OCMD_READ_SFDP, + .cmd_length = 2, + .addr_length = 4, + .rx_dummy = 20, + }, + }, +}; + +void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_nor_cmd *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /*__FLASH_MSPI_NOR_H__*/ diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h new file mode 100644 index 00000000000..ba840bd7926 --- /dev/null +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_MSPI_NOR_QUIRKS_H__ +#define __FLASH_MSPI_NOR_QUIRKS_H__ + +/* Flash chip specific quirks */ +struct flash_mspi_nor_quirks { + /* Called after switching to default IO mode. */ + int (*post_switch_mode)(const struct device *dev); +}; + +/* Extend this macro when adding new flash chip with quirks */ +#define FLASH_MSPI_QUIRKS_GET(node) \ + COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25r, okay), \ + (&flash_quirks_mxicy_mx25r), \ + (COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25u, okay), \ + (&flash_quirks_mxicy_mx25u), \ + (NULL)))) + +#if DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25r) + +#define MXICY_MX25R_LH_MASK BIT(1) +#define MXICY_MX25R_QE_MASK BIT(6) +#define MXICY_MX25R_REGS_LEN 3 + +static uint8_t mxicy_mx25r_hp_payload[MXICY_MX25R_REGS_LEN] = { + MXICY_MX25R_QE_MASK, 0x0, MXICY_MX25R_LH_MASK +}; + +/* For quad io mode above 8 MHz and single io mode above 33 MHz, + * high performance mode needs to be enabled. + */ +static inline bool needs_hp(enum mspi_io_mode io_mode, uint32_t freq) +{ + if ((io_mode == MSPI_IO_MODE_QUAD_1_1_4) || (io_mode == MSPI_IO_MODE_QUAD_1_4_4)) { + if (freq > MHZ(8)) { + return true; + } + } else if (io_mode == MSPI_IO_MODE_SINGLE) { + if (freq > MHZ(33)) { + return true; + } + } + + return false; +} + +static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; + uint32_t freq = dev_config->mspi_nor_cfg.freq; + int rc; + uint8_t status; + uint8_t config[MXICY_MX25R_REGS_LEN - 1]; + + if (!needs_hp(io_mode, freq)) { + return 0; + } + + /* Wait for previous write to finish */ + do { + flash_mspi_command_set(dev, &dev_config->jedec_cmds->status); + dev_data->packet.data_buf = &status; + dev_data->packet.num_bytes = sizeof(status); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + if (rc < 0) { + return rc; + } + } while (status & SPI_NOR_WIP_BIT); + + /* Write enable */ + flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + return rc; + } + + /* Write status and config registers */ + const struct flash_mspi_nor_cmd cmd_status = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_WRSR, + .cmd_length = 1, + }; + + flash_mspi_command_set(dev, &cmd_status); + dev_data->packet.data_buf = mxicy_mx25r_hp_payload; + dev_data->packet.num_bytes = sizeof(mxicy_mx25r_hp_payload); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + if (rc < 0) { + return rc; + } + + /* Wait for write to end and verify status register */ + do { + flash_mspi_command_set(dev, &dev_config->jedec_cmds->status); + dev_data->packet.data_buf = &status; + dev_data->packet.num_bytes = sizeof(status); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + if (rc < 0) { + return rc; + } + } while (status & SPI_NOR_WIP_BIT); + + if (status != mxicy_mx25r_hp_payload[0]) { + return -EIO; + } + + /* Verify configuration registers */ + flash_mspi_command_set(dev, &dev_config->jedec_cmds->config); + dev_data->packet.data_buf = config; + dev_data->packet.num_bytes = sizeof(config); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + if (rc < 0) { + return rc; + } + + for (uint8_t i = 0; i < MXICY_MX25R_REGS_LEN - 1; i++) { + if (config[i] != mxicy_mx25r_hp_payload[i + 1]) { + return -EIO; + } + } + + return 0; +} + +struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25r = { + .post_switch_mode = mxicy_mx25r_post_switch_mode, +}; + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25r) */ + +#if DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25u) + +#define MXICY_MX25R_OE_MASK BIT(0) + +static uint8_t mxicy_mx25u_oe_payload = MXICY_MX25R_OE_MASK; + +static inline int mxicy_mx25u_post_switch_mode(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; + int rc; + + if (io_mode != MSPI_IO_MODE_OCTAL) { + return 0; + } + + /* Write enable */ + flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + return rc; + } + + /* Write config register 2 */ + const struct flash_mspi_nor_cmd cmd_status = { + .dir = MSPI_TX, + .cmd = SPI_NOR_CMD_WR_CFGREG2, + .cmd_length = 1, + .addr_length = 4, + }; + + flash_mspi_command_set(dev, &cmd_status); + dev_data->packet.data_buf = &mxicy_mx25u_oe_payload; + dev_data->packet.num_bytes = sizeof(mxicy_mx25u_oe_payload); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + return rc; +} + +struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25u = { + .post_switch_mode = mxicy_mx25u_post_switch_mode, +}; + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25u) */ + +#endif /*__FLASH_MSPI_NOR_QUIRKS_H__*/