diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts index 06e642948f3..6f28374d976 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts @@ -268,7 +268,7 @@ ipc0: &cpuapp_cpurad_ipc { status = "okay"; mx25uw63: mx25uw6345g@0 { - compatible = "jedec,mspi-nor"; + compatible = "mxicy,mx25u", "jedec,mspi-nor"; status = "disabled"; reg = <0>; jedec-id = [c2 84 37]; diff --git a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts index a7b61861bed..6abddbeafcc 100644 --- a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts +++ b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts @@ -254,7 +254,7 @@ ipc0: &cpuapp_cpurad_ipc { status = "okay"; mx25uw63: mx25uw6345g@0 { - compatible = "jedec,mspi-nor"; + compatible = "mxicy,mx25u", "jedec,mspi-nor"; status = "disabled"; reg = <0>; jedec-id = [c2 84 37]; diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 455076a7a9b..140a5300308 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -6,250 +6,58 @@ #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 - -#define FLASH_MX25R_LH_MASK BIT(1) - -static uint8_t hp_payload[3] = {SPI_NOR_QE_BIT, 0x0, FLASH_MX25R_LH_MASK}; - -struct flash_mspi_nor_data { - struct k_sem acquired; - struct mspi_xfer_packet packet; - struct mspi_xfer xfer; -}; +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_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; -}; + memset(&dev_data->xfer, 0, sizeof(dev_data->xfer)); + memset(&dev_data->packet, 0, sizeof(dev_data->packet)); -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 page_program; - struct flash_mspi_nor_cmd sector_erase; - struct flash_mspi_nor_cmd chip_erase; - struct flash_mspi_nor_cmd mode_change; - struct flash_mspi_nor_cmd sfdp; - uint8_t mode_payload; -}; + 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; -static const 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, - }, - .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, - }, - .mode_change = { - .dir = MSPI_TX, - .cmd = SPI_NOR_CMD_WRSR, - .cmd_length = 1, - }, - .mode_payload = 0, - }, - [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, - }, - .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, - }, - .mode_change = { - .dir = MSPI_TX, - .cmd = SPI_NOR_CMD_WRSR, - .cmd_length = 1, - .force_single = true, - }, - .mode_payload = SPI_NOR_QE_BIT, - }, - [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, - }, - .mode_change = { - .dir = MSPI_TX, - .cmd = SPI_NOR_CMD_WR_CFGREG2, - .cmd_length = 1, - .addr_length = 4, - }, - .mode_payload = 0x01, - }, -}; + 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; -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]; - const struct flash_mspi_nor_cmds *jedec_cmds; -}; + 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, - dev_config->mspi_nor_cfg_mask, cfg); + 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, const struct mspi_dev_cfg *cfg) +static int acquire(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; @@ -264,8 +72,12 @@ static int acquire(const struct device *dev, const struct mspi_dev_cfg *cfg) /* This acquires the MSPI controller and reconfigures it * if needed for the flash device. */ - rc = dev_cfg_apply(dev, cfg); - if (rc == 0) { + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + dev_config->mspi_nor_cfg_mask, + &dev_config->mspi_nor_cfg); + if (rc < 0) { + LOG_ERR("mspi_dev_config() failed: %d", rc); + } else { return 0; } @@ -301,30 +113,6 @@ static inline uint16_t dev_page_size(const struct device *dev) return SPI_NOR_PAGE_SIZE; } -static void 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; - - memset(&dev_data->xfer, 0, sizeof(dev_data->xfer)); - memset(&dev_data->packet, 0, sizeof(dev_data->packet)); - - 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 api_read(const struct device *dev, off_t addr, void *dest, size_t size) { @@ -341,13 +129,23 @@ static int api_read(const struct device *dev, off_t addr, void *dest, return -EINVAL; } - rc = acquire(dev, &dev_config->mspi_nor_cfg); + rc = acquire(dev); + if (rc < 0) { + 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 */ - command_set(dev, &dev_config->jedec_cmds->read); + 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; @@ -364,24 +162,25 @@ static int api_read(const struct device *dev, off_t addr, void *dest, return 0; } -static int status_get(const struct device *dev, const struct mspi_dev_cfg *curr_cfg, - uint8_t *status) +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; int rc; /* Enter command mode */ - if (curr_cfg != &dev_config->mspi_nor_init_cfg) { + 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 initial dev_cfg failed: %d", rc); - return rc; - } + if (rc < 0) { + LOG_ERR("Switching to dev_cfg failed: %d", rc); + return rc; } - command_set(dev, &dev_config->jedec_cmds->status); + flash_mspi_command_set(dev, &dev_config->jedec_cmds->status); dev_data->packet.data_buf = status; dev_data->packet.num_bytes = sizeof(uint8_t); @@ -392,29 +191,19 @@ static int status_get(const struct device *dev, const struct mspi_dev_cfg *curr_ return rc; } - /* Exit command mode */ - if (curr_cfg != &dev_config->mspi_nor_init_cfg) { - rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); - - if (rc < 0) { - LOG_ERR("Switching to default dev_cfg failed: %d", rc); - } - } - return rc; } -static int wait_until_ready(const struct device *dev, const struct mspi_dev_cfg *curr_cfg, - k_timeout_t poll_period) +static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) { int rc; uint8_t status_reg; while (true) { - rc = status_get(dev, curr_cfg, &status_reg); + rc = status_get(dev, &status_reg); if (rc < 0) { - LOG_ERR("Status xfer failed: %d", rc); + LOG_ERR("Wait until ready - status xfer failed: %d", rc); return rc; } @@ -432,8 +221,19 @@ 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; - command_set(dev, &dev_config->jedec_cmds->write_en); + 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); } @@ -454,7 +254,7 @@ static int api_write(const struct device *dev, off_t addr, const void *src, return -EINVAL; } - rc = acquire(dev, &dev_config->mspi_nor_cfg); + rc = acquire(dev); if (rc < 0) { return rc; } @@ -470,7 +270,17 @@ static int api_write(const struct device *dev, off_t addr, const void *src, break; } - command_set(dev, &dev_config->jedec_cmds->page_program); + 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; @@ -485,7 +295,7 @@ static int api_write(const struct device *dev, off_t addr, const void *src, src = (const uint8_t *)src + to_write; size -= to_write; - rc = wait_until_ready(dev, &dev_config->mspi_nor_cfg, K_MSEC(1)); + rc = wait_until_ready(dev, K_MSEC(1)); if (rc < 0) { break; } @@ -515,7 +325,7 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) return -EINVAL; } - rc = acquire(dev, &dev_config->mspi_nor_init_cfg); + rc = acquire(dev); if (rc < 0) { return rc; } @@ -529,11 +339,31 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) if (size == flash_size) { /* Chip erase. */ - command_set(dev, &dev_config->jedec_cmds->chip_erase); + 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. */ - command_set(dev, &dev_config->jedec_cmds->sector_erase); + 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; @@ -546,7 +376,7 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) break; } - rc = wait_until_ready(dev, &dev_config->mspi_nor_init_cfg, K_MSEC(1)); + rc = wait_until_ready(dev, K_MSEC(1)); if (rc < 0) { break; } @@ -576,14 +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; - command_set(dev, &dev_config->jedec_cmds->id); + 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) { - LOG_ERR("mspi_transceive() failed: %d\n", rc); + LOG_ERR("Read JEDEC ID failed: %d\n", rc); } return rc; @@ -613,20 +453,29 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, return 0; } - rc = acquire(dev, dev_config->jedec_cmds->sfdp.force_single ? - &dev_config->mspi_nor_init_cfg : &dev_config->mspi_nor_cfg); + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + 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; } - command_set(dev, &dev_config->jedec_cmds->sfdp); + 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; } @@ -637,10 +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) { - const struct flash_mspi_nor_config *dev_config = dev->config; - - int rc = acquire(dev, dev_config->jedec_cmds->id.force_single ? - &dev_config->mspi_nor_init_cfg : &dev_config->mspi_nor_cfg); + int rc = acquire(dev); if (rc < 0) { return rc; } @@ -668,70 +514,165 @@ static int dev_pm_action_cb(const struct device *dev, return 0; } -static int switch_mode(const struct device *dev, uint8_t *id) +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; - /* For octal mode: - * 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. - */ - if ((dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_OCTAL) && - (memcmp(id, dev_config->jedec_id, JESD216_READ_ID_LEN) != 0)) { - return 0; - } - - command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en); - int rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + 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; } - command_set(dev, &dev_config->jedec_cmds->mode_change); + 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; - /* MX25R specific: - * For quad io mode above 8 MHz and single io mode above 33 MHz, - * high performance mode needs to be enabled. - */ - if (((dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_QUAD_1_4_4) && - (dev_config->mspi_nor_cfg.freq > MHZ(8))) || - ((dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) && - (dev_config->mspi_nor_cfg.freq > MHZ(33)))) { - dev_data->packet.data_buf = hp_payload; - dev_data->packet.num_bytes = sizeof(hp_payload); + 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 { - dev_data->packet.data_buf = (uint8_t *)&dev_config->jedec_cmds->mode_payload; - dev_data->packet.num_bytes = sizeof(dev_config->jedec_cmds->mode_payload); + /* TODO: handle all DW15 QER values */ + return -ENOTSUP; } - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + 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; + enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; uint8_t id[JESD216_READ_ID_LEN] = {0}; int rc; - rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, &dev_config->mspi_nor_init_cfg); + rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + if (rc < 0) { - LOG_ERR("Failed to set initial device config: %d", rc); return rc; } - command_set(dev, &commands[MSPI_IO_MODE_SINGLE].id); + /* 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); + + if (rc < 0) { + LOG_ERR("Failed to switch to single line mode: %d", rc); + return rc; + } + + 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); @@ -742,26 +683,22 @@ static int flash_chip_init(const struct device *dev) return rc; } - rc = switch_mode(dev, id); + rc = default_io_mode(dev); if (rc < 0) { - LOG_ERR("Failed to switch mode: %d", rc); + LOG_ERR("Failed to switch to default io mode: %d", rc); return rc; } - rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, - dev_config->jedec_cmds->id.force_single ? - &dev_config->mspi_nor_init_cfg : &dev_config->mspi_nor_cfg); - if (rc < 0) { - LOG_ERR("Failed to set device config: %d", rc); - return rc; - } - - rc = read_jedec_id(dev, id); - if (rc < 0) { - LOG_ERR("Failed to read JEDEC ID in final line 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 (!dev_config->jedec_cmds->id.force_single) { + rc = read_jedec_id(dev, id); + if (rc < 0) { + LOG_ERR("Failed to read JEDEC ID in final line mode: %d", rc); + return rc; + } } if (memcmp(id, dev_config->jedec_id, sizeof(id)) != 0) { @@ -799,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); @@ -882,6 +788,13 @@ static DEVICE_API(flash, drv_api) = { #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, @@ -938,6 +851,8 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 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__*/ diff --git a/drivers/flash/spi_nor.h b/drivers/flash/spi_nor.h index 141b67718ef..a918e0a99d0 100644 --- a/drivers/flash/spi_nor.h +++ b/drivers/flash/spi_nor.h @@ -14,7 +14,6 @@ /* Status register bits */ #define SPI_NOR_WIP_BIT BIT(0) /* Write in progress */ #define SPI_NOR_WEL_BIT BIT(1) /* Write enable latch */ -#define SPI_NOR_QE_BIT BIT(6) /* Quad enable */ /* Flash opcodes */ #define SPI_NOR_CMD_WRSR 0x01 /* Write status register */