From 639f6a341e2737be5b453afcf8b99397937cfdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Fri, 11 Jul 2025 14:30:40 +0200 Subject: [PATCH 1/5] drivers: flash_mspi_nor: Get info from dts SFDP arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get parameters for standard commands and requirements for enabling Quad and Octal modes from dts uint8-arrays containing data read from SFDP tables for particular flash chips. Signed-off-by: Andrzej Głąbek --- .../nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts | 4 ++ .../nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts | 4 ++ drivers/flash/flash_mspi_nor.c | 68 +++++++++++++++++-- drivers/flash/flash_mspi_nor.h | 8 ++- dts/bindings/mtd/jedec,jesd216.yaml | 6 ++ 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts index 9cbc9035d4f88..04eb4da7ed2a8 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts @@ -278,6 +278,10 @@ ipc0: &cpuapp_cpurad_ipc { 30 b0 30 b0 f4 bd d5 5c 00 00 00 ff 10 10 00 20 00 00 00 00 00 00 7c 23 48 00 00 00 00 00 88 88 ]; + sfdp-ff05 = [ + 00 ee c0 69 72 72 71 71 00 d8 f7 f6 00 0a 00 00 + 14 45 98 80 + ]; size = <67108864>; has-dpd; t-enter-dpd = <10000>; diff --git a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts index f4cce12f4b1a0..c6f982fae1c5b 100644 --- a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts +++ b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts @@ -260,6 +260,10 @@ ipc0: &cpuapp_cpurad_ipc { 30 b0 30 b0 f4 bd d5 5c 00 00 00 ff 10 10 00 20 00 00 00 00 00 00 7c 23 48 00 00 00 00 00 88 88 ]; + sfdp-ff05 = [ + 00 ee c0 69 72 72 71 71 00 d8 f7 f6 00 0a 00 00 + 14 45 98 80 + ]; size = <67108864>; has-dpd; t-enter-dpd = <10000>; diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index b7ca6341d3aec..7e370d8f0c704 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -12,6 +12,13 @@ #include #include "flash_mspi_nor.h" + +#define BFP_DW18_CMD_EXT_SAME 0 +#define BFP_DW18_CMD_EXT_INV 1 + +#define BFP_DW19_OER_VAL_NONE 0 +#define BFP_DW19_OER_VAL_S2B3 1 + #include "flash_mspi_nor_quirks.h" LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); @@ -829,11 +836,47 @@ extern const struct flash_mspi_nor_cmds mspi_io_mode_not_supported; #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)) - +/* 32-bit words in SFDP arrays in devicetree are stored in little-endian byte + * order. See jedec,jesd216.yaml + */ +#define SFDP_DW_BYTE_0_IDX(dw_no) \ + UTIL_DEC(UTIL_DEC(UTIL_DEC(UTIL_DEC(UTIL_X2(UTIL_X2(dw_no)))))) +#define SFDP_DW_BYTE_1_IDX(dw_no) \ + UTIL_DEC(UTIL_DEC(UTIL_DEC(UTIL_X2(UTIL_X2(dw_no))))) +#define SFDP_DW_BYTE_2_IDX(dw_no) \ + UTIL_DEC(UTIL_DEC(UTIL_X2(UTIL_X2(dw_no)))) +#define SFDP_DW_BYTE_3_IDX(dw_no) \ + UTIL_DEC(UTIL_X2(UTIL_X2(dw_no))) +#define SFDP_DW(inst, prop, dw_no) \ + ((DT_INST_PROP_BY_IDX(inst, prop, SFDP_DW_BYTE_3_IDX(dw_no)) << 24) | \ + (DT_INST_PROP_BY_IDX(inst, prop, SFDP_DW_BYTE_2_IDX(dw_no)) << 16) | \ + (DT_INST_PROP_BY_IDX(inst, prop, SFDP_DW_BYTE_1_IDX(dw_no)) << 8) | \ + (DT_INST_PROP_BY_IDX(inst, prop, SFDP_DW_BYTE_0_IDX(dw_no)) << 0)) + +#define SFDP_FIELD(inst, prop, dw_no, mask) \ + COND_CODE_1(DT_INST_PROP_HAS_IDX(inst, prop, SFDP_DW_BYTE_3_IDX(dw_no)), \ + (FIELD_GET(mask, SFDP_DW(inst, prop, dw_no))), \ + (0)) + +#define FLASH_DW15_QER(inst) \ + DT_INST_ENUM_IDX_OR(inst, quad_enable_requirements, \ + SFDP_FIELD(inst, sfdp_bfp, 15, GENMASK(22, 20))) + +#define OCTAL_SFDP_ENTRIES(inst) \ + COND_CODE_1(IS_EQ(DT_INST_ENUM_IDX(inst, mspi_io_mode), _MSPI_IO_MODE_OCTAL), \ + (.dw19_oer = SFDP_FIELD(inst, sfdp_bfp, 19, GENMASK(22, 20)), \ + .cmd_ext_inv = SFDP_FIELD(inst, sfdp_bfp, 18, GENMASK(30, 29)) \ + == BFP_DW18_CMD_EXT_INV, \ + .sfdp_addr_4 = SFDP_FIELD(inst, sfdp_ff05, 1, BIT(31)) == 0, \ + .sfdp_dummy_20 = SFDP_FIELD(inst, sfdp_ff05, 1, BIT(30)) == 1, \ + .rdsr_addr_4 = SFDP_FIELD(inst, sfdp_ff05, 1, BIT(29)) == 1, \ + .rdsr_dummy = SFDP_FIELD(inst, sfdp_ff05, 1, BIT(28)) ? 8 : 4), \ + (.dw19_oer = BFP_DW19_OER_VAL_NONE, \ + .cmd_ext_inv = false, \ + .sfdp_addr_4 = false, \ + .sfdp_dummy_20 = false, \ + .rdsr_addr_4 = false, \ + .rdsr_dummy = 0)) #if defined(CONFIG_FLASH_PAGE_LAYOUT) BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0, @@ -867,6 +910,20 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == (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"); \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(inst, sfdp_bfp), \ + "sfdp-bfp property needed in " \ + DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ + BUILD_ASSERT((DT_INST_ENUM_IDX(inst, mspi_io_mode) \ + != MSPI_IO_MODE_OCTAL) || \ + DT_INST_NODE_HAS_PROP(inst, sfdp_ff05), \ + "sfdp-ff05 property needed in " \ + DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ + BUILD_ASSERT((DT_INST_ENUM_IDX(inst, mspi_io_mode) \ + != MSPI_IO_MODE_OCTAL) || \ + (SFDP_FIELD(inst, sfdp_bfp, 18, GENMASK(30, 29)) \ + <= BFP_DW18_CMD_EXT_INV), \ + "Unsupported Octal Command Extension mode in " \ + DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ 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 = { \ @@ -892,6 +949,7 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == .jedec_cmds = FLASH_CMDS(inst), \ .quirks = FLASH_QUIRKS(inst), \ .dw15_qer = FLASH_DW15_QER(inst), \ + OCTAL_SFDP_ENTRIES(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 index 35a0bff4c2159..3597c8b7320a9 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -41,7 +41,13 @@ struct flash_mspi_nor_config { uint8_t jedec_id[SPI_NOR_MAX_ID_LEN]; const struct flash_mspi_nor_cmds *jedec_cmds; struct flash_mspi_nor_quirks *quirks; - uint8_t dw15_qer; + uint8_t dw15_qer : 3; + uint8_t dw19_oer : 3; + bool cmd_ext_inv : 1; + bool sfdp_addr_4 : 1; + bool sfdp_dummy_20 : 1; + bool rdsr_addr_4 : 1; + uint8_t rdsr_dummy : 4; }; struct flash_mspi_nor_data { diff --git a/dts/bindings/mtd/jedec,jesd216.yaml b/dts/bindings/mtd/jedec,jesd216.yaml index c55440456c2ac..40b852a379661 100644 --- a/dts/bindings/mtd/jedec,jesd216.yaml +++ b/dts/bindings/mtd/jedec,jesd216.yaml @@ -35,6 +35,12 @@ properties: information in cases were runtime retrieval of SFDP data is not desired. + sfdp-ff05: + type: uint8-array + description: | + Contains the 32-bit words in little-endian byte order from the + JESD216 Serial Flash Discoverable Parameters xSPI Profile 1.0 table. + quad-enable-requirements: type: string enum: From 28e8046899cef5ad3ee4b9c56a413567c8b24779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Fri, 11 Jul 2025 14:46:28 +0200 Subject: [PATCH 2/5] drivers: flash_mspi_nor: Refactor handling of commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use standard operation codes and parameters from SFDP for handling most of the used commands (except for read, write, and erase for now) - Use common functions for reading and writing of status registers and for enabling write operations - Switch IO mode in a common function that perfors a transfer and do it only when required for a given command Signed-off-by: Andrzej Głąbek --- drivers/flash/flash_mspi_nor.c | 423 ++++++++++++++------------ drivers/flash/flash_mspi_nor.h | 99 +----- drivers/flash/flash_mspi_nor_quirks.h | 56 +--- 3 files changed, 242 insertions(+), 336 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 7e370d8f0c704..49a78f6d2b457 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -19,14 +19,22 @@ #define BFP_DW19_OER_VAL_NONE 0 #define BFP_DW19_OER_VAL_S2B3 1 +static void setup_xfer(const struct device *dev, enum mspi_xfer_direction dir); +static int perform_xfer(const struct device *dev, + uint8_t cmd, bool in_target_io_mode); +static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr); +static int wait_until_ready(const struct device *dev, k_timeout_t poll_period); +static int cmd_wren(const struct device *dev); +static int cmd_wrsr(const struct device *dev, uint8_t op_code, + uint8_t sr_cnt, uint8_t *sr); + #include "flash_mspi_nor_quirks.h" LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); -void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_nor_cmd *cmd) +static void setup_xfer(const struct device *dev, enum mspi_xfer_direction dir) { 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)); @@ -36,32 +44,94 @@ void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_no 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; + dev_data->packet.dir = dir; } -static int dev_cfg_apply(const struct device *dev, const struct mspi_dev_cfg *cfg) +static int perform_xfer(const struct device *dev, + uint8_t cmd, bool data_transfer) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; + const struct mspi_dev_cfg *cfg = NULL; + int rc; - if (dev_data->curr_cfg == cfg) { - return 0; + if (cmd) { + if (dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_OCTAL && + dev_data->in_target_io_mode) { + uint8_t cmd_extension = dev_config->cmd_ext_inv + ? (uint8_t)~cmd + : cmd; + + dev_data->xfer.cmd_length = 2; + dev_data->packet.cmd = ((uint16_t)cmd << 8) + | cmd_extension; + } else { + dev_data->xfer.cmd_length = 1; + dev_data->packet.cmd = cmd; + } + } + + if (dev_config->multi_io_cmd || + dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) { + /* If multiple IO lines are used in all the transfer phases + * or in none of them, there's no need to switch the IO mode. + */ + } else if (data_transfer) { + /* For data transfer commands (read and program), ensure that + * the target IO mode is active. + */ + if (!dev_data->in_target_io_mode) { + cfg = &dev_config->mspi_nor_cfg; + } + } else { + /* For all other commands, switch to Single IO mode if a given + * command needs the data or address phase and in the target IO + * mode multiple IO lines are used in these phases. + */ + if (dev_data->in_target_io_mode) { + if (dev_data->packet.num_bytes != 0 || + (dev_data->xfer.addr_length != 0 && + !dev_config->single_io_addr)) { + /* Only the IO mode is to be changed, so the + * initial configuration structure can be used + * for this operation. + */ + cfg = &dev_config->mspi_nor_init_cfg; + } + } + } + + if (cfg) { + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_IO_MODE, cfg); + if (rc < 0) { + LOG_ERR("%s: dev_config() failed: %d", __func__, rc); + return rc; + } + + dev_data->in_target_io_mode = data_transfer; } - int rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, cfg); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); if (rc < 0) { - LOG_ERR("Failed to set device config: %p error: %d", cfg, rc); + LOG_ERR("%s: transceive() failed: %d", __func__, rc); + return rc; } - return rc; + + return 0; +} + +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; + + setup_xfer(dev, cmd->dir); + dev_data->packet.cmd = cmd->cmd; + dev_data->xfer.cmd_length = cmd->cmd_length; + dev_data->xfer.addr_length = cmd->addr_length; + dev_data->xfer.tx_dummy = cmd->tx_dummy; + dev_data->xfer.rx_dummy = cmd->rx_dummy; } static int acquire(const struct device *dev) @@ -76,15 +146,26 @@ static int acquire(const struct device *dev) if (rc < 0) { LOG_ERR("pm_device_runtime_get() failed: %d", rc); } else { + enum mspi_dev_cfg_mask mask; + + if (dev_config->multiperipheral_bus) { + mask = MSPI_DEVICE_CONFIG_ALL; + } else { + mask = MSPI_DEVICE_CONFIG_NONE; + } + /* This acquires the MSPI controller and reconfigures it * if needed for the flash device. */ rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - dev_config->mspi_nor_cfg_mask, - &dev_config->mspi_nor_cfg); + mask, &dev_config->mspi_nor_cfg); if (rc < 0) { LOG_ERR("mspi_dev_config() failed: %d", rc); } else { + if (dev_config->multiperipheral_bus) { + dev_data->in_target_io_mode = true; + } + return 0; } @@ -120,128 +201,127 @@ static inline uint16_t dev_page_size(const struct device *dev) return SPI_NOR_PAGE_SIZE; } -static int api_read(const struct device *dev, off_t addr, void *dest, - size_t size) +static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; - const uint32_t flash_size = dev_flash_size(dev); int rc; - if (size == 0) { - return 0; + setup_xfer(dev, MSPI_RX); + if (dev_data->in_target_io_mode) { + dev_data->xfer.rx_dummy = dev_config->rdsr_dummy; + dev_data->xfer.addr_length = dev_config->rdsr_addr_4 ? 4 : 0; } - - if ((addr < 0) || ((addr + size) > flash_size)) { - return -EINVAL; - } - - rc = acquire(dev); + dev_data->packet.num_bytes = sizeof(uint8_t); + dev_data->packet.data_buf = sr; + rc = perform_xfer(dev, op_code, false); if (rc < 0) { + LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, rc); 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); - } + return 0; +} - if (rc < 0) { - return rc; +static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) +{ + int rc; + uint8_t status_reg; + + while (true) { + rc = cmd_rdsr(dev, SPI_NOR_CMD_RDSR, &status_reg); + if (rc < 0) { + LOG_ERR("%s - status xfer failed: %d", __func__, rc); + return rc; + } + + if (!(status_reg & SPI_NOR_WIP_BIT)) { + break; + } + + k_sleep(poll_period); } - /* TODO: get rid of all these hard-coded values for MX25Ux chips */ - 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; - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + return 0; +} - release(dev); +static int cmd_wren(const struct device *dev) +{ + int rc; + setup_xfer(dev, MSPI_TX); + rc = perform_xfer(dev, SPI_NOR_CMD_WREN, false); if (rc < 0) { - LOG_ERR("Read xfer failed: %d", rc); + LOG_ERR("%s failed: %d", __func__, rc); return rc; } return 0; } -static int status_get(const struct device *dev, uint8_t *status) +static int cmd_wrsr(const struct device *dev, uint8_t op_code, + uint8_t sr_cnt, uint8_t *sr) { - 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 (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); + rc = cmd_wren(dev); + if (rc < 0) { + return rc; } + setup_xfer(dev, MSPI_TX); + dev_data->packet.num_bytes = sr_cnt; + dev_data->packet.data_buf = sr; + rc = perform_xfer(dev, op_code, false); if (rc < 0) { - LOG_ERR("Switching to dev_cfg failed: %d", rc); + LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, 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); - + rc = wait_until_ready(dev, K_USEC(1)); if (rc < 0) { - LOG_ERR("Status xfer failed: %d", rc); return rc; } - return rc; + return 0; } -static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) +static int api_read(const struct device *dev, off_t addr, void *dest, + size_t size) { + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + const uint32_t flash_size = dev_flash_size(dev); int rc; - uint8_t status_reg; - - while (true) { - rc = status_get(dev, &status_reg); - if (rc < 0) { - LOG_ERR("Wait until ready - status xfer failed: %d", rc); - return rc; - } - - if (!(status_reg & SPI_NOR_WIP_BIT)) { - break; - } + if (size == 0) { + return 0; + } - k_sleep(poll_period); + if ((addr < 0) || ((addr + size) > flash_size)) { + return -EINVAL; } - return 0; -} + rc = acquire(dev); + if (rc < 0) { + return rc; + } -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; + 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; + rc = perform_xfer(dev, 0, true); - 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); - } + release(dev); if (rc < 0) { + LOG_ERR("Read xfer failed: %d", rc); 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); + return 0; } static int api_write(const struct device *dev, off_t addr, const void *src, @@ -272,27 +352,15 @@ 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); - if (write_enable(dev) < 0) { - LOG_ERR("Write enable xfer failed: %d", rc); + if (cmd_wren(dev) < 0) { break; } - 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); + rc = perform_xfer(dev, 0, true); if (rc < 0) { LOG_ERR("Page program xfer failed: %d", rc); break; @@ -338,45 +406,24 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) } while (size > 0) { - rc = write_enable(dev); - if (rc < 0) { - LOG_ERR("Write enable failed."); + if (cmd_wren(dev) < 0) { break; } if (size == flash_size) { /* 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. */ - 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; } - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = perform_xfer(dev, 0, false); if (rc < 0) { LOG_ERR("Erase command 0x%02x xfer failed: %d", dev_data->packet.cmd, rc); @@ -413,24 +460,16 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) struct flash_mspi_nor_data *dev_data = dev->data; int rc; - 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; + setup_xfer(dev, MSPI_RX); + if (dev_data->in_target_io_mode) { + dev_data->xfer.rx_dummy = dev_config->rdsr_dummy; + dev_data->xfer.addr_length = dev_config->rdsr_addr_4 ? 4 : 0; } - - 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); + rc = perform_xfer(dev, SPI_NOR_CMD_RDID, false); if (rc < 0) { - LOG_ERR("Read JEDEC ID failed: %d\n", rc); + LOG_ERR("Read JEDEC ID failed: %d", rc); } return rc; @@ -465,25 +504,20 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, return rc; } - if (dev_config->jedec_cmds->sfdp.force_single) { - rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg); + setup_xfer(dev, MSPI_RX); + if (dev_data->in_target_io_mode) { + dev_data->xfer.rx_dummy = dev_config->sfdp_dummy_20 ? 20 : 8; + dev_data->xfer.addr_length = dev_config->sfdp_addr_4 ? 4 : 3; } else { - rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + dev_data->xfer.rx_dummy = 8; + dev_data->xfer.addr_length = 3; } - - 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); + rc = perform_xfer(dev, JESD216_CMD_READ_SFDP, false); if (rc < 0) { - printk("Read SFDP xfer failed: %d\n", rc); - return rc; + LOG_ERR("Read SFDP xfer failed: %d", rc); } release(dev); @@ -524,30 +558,18 @@ static int dev_pm_action_cb(const struct device *dev, 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_single.write_en); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = cmd_wren(dev); 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); - + rc = cmd_wrsr(dev, SPI_NOR_CMD_WRSR, 1, &mode_payload); if (rc < 0) { LOG_ERR("Failed to enable/disable quad mode: %d", rc); return rc; @@ -567,8 +589,7 @@ static int quad_enable_set(const struct device *dev, bool enable) return 0; } - -static int default_io_mode(const struct device *dev) +static int switch_to_target_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; @@ -595,11 +616,13 @@ static int default_io_mode(const struct device *dev) } if (rc < 0) { - LOG_ERR("Failed to change IO mode: %d\n", rc); + LOG_ERR("Failed to change IO mode: %d", rc); return rc; } - return dev_cfg_apply(dev, &dev_config->mspi_nor_cfg); + return mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_ALL, + &dev_config->mspi_nor_cfg); } #if defined(WITH_RESET_GPIO) @@ -649,12 +672,15 @@ static int flash_chip_init(const struct device *dev) uint8_t id[JESD216_READ_ID_LEN] = {0}; int rc; - 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, + &dev_config->mspi_nor_init_cfg); if (rc < 0) { return rc; } + dev_data->in_target_io_mode = false; + /* Some chips reuse RESET pin for data in Quad modes: * force single line mode before resetting. */ @@ -686,35 +712,20 @@ static int flash_chip_init(const struct device *dev) } #endif - flash_mspi_command_set(dev, &commands_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); + rc = switch_to_target_io_mode(dev); if (rc < 0) { - LOG_ERR("Failed to read JEDEC ID in initial line mode: %d", rc); + LOG_ERR("Failed to switch to target io mode: %d", rc); return rc; } - rc = default_io_mode(dev); + dev_data->in_target_io_mode = true; + rc = read_jedec_id(dev, id); if (rc < 0) { - LOG_ERR("Failed to switch to default io mode: %d", rc); + LOG_ERR("Failed to read JEDEC ID: %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) { LOG_ERR("JEDEC ID mismatch, read: %02x %02x %02x, " "expected: %02x %02x %02x", @@ -878,6 +889,17 @@ extern const struct flash_mspi_nor_cmds mspi_io_mode_not_supported; .rdsr_addr_4 = false, \ .rdsr_dummy = 0)) +#define IO_MODE_FLAGS(io_mode) \ + .multi_io_cmd = (io_mode == MSPI_IO_MODE_DUAL || \ + io_mode == MSPI_IO_MODE_QUAD || \ + io_mode == MSPI_IO_MODE_OCTAL || \ + io_mode == MSPI_IO_MODE_HEX || \ + io_mode == MSPI_IO_MODE_HEX_8_8_16 || \ + io_mode == MSPI_IO_MODE_HEX_8_16_16), \ + .single_io_addr = (io_mode == MSPI_IO_MODE_DUAL_1_1_2 || \ + io_mode == MSPI_IO_MODE_QUAD_1_1_4 || \ + io_mode == MSPI_IO_MODE_OCTAL_1_1_8) + #if defined(CONFIG_FLASH_PAGE_LAYOUT) BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0, "MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096"); @@ -932,10 +954,6 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == .mspi_id = MSPI_DEVICE_ID_DT_INST(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, \ IF_ENABLED(CONFIG_MSPI_XIP, \ (.xip_cfg = MSPI_XIP_CONFIG_DT_INST(inst),)) \ IF_ENABLED(WITH_RESET_GPIO, \ @@ -948,8 +966,11 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == .jedec_id = DT_INST_PROP(inst, jedec_id), \ .jedec_cmds = FLASH_CMDS(inst), \ .quirks = FLASH_QUIRKS(inst), \ + .multiperipheral_bus = DT_PROP(DT_INST_BUS(inst), \ + software_multiperipheral), \ .dw15_qer = FLASH_DW15_QER(inst), \ OCTAL_SFDP_ENTRIES(inst), \ + IO_MODE_FLAGS(DT_INST_ENUM_IDX(inst, mspi_io_mode)), \ }; \ 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 index 3597c8b7320a9..c2148dca8a068 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -26,7 +26,6 @@ struct flash_mspi_nor_config { 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 @@ -41,6 +40,7 @@ struct flash_mspi_nor_config { uint8_t jedec_id[SPI_NOR_MAX_ID_LEN]; const struct flash_mspi_nor_cmds *jedec_cmds; struct flash_mspi_nor_quirks *quirks; + bool multiperipheral_bus : 1; uint8_t dw15_qer : 3; uint8_t dw19_oer : 3; bool cmd_ext_inv : 1; @@ -48,13 +48,15 @@ struct flash_mspi_nor_config { bool sfdp_dummy_20 : 1; bool rdsr_addr_4 : 1; uint8_t rdsr_dummy : 4; + bool multi_io_cmd : 1; + bool single_io_addr : 1; }; struct flash_mspi_nor_data { struct k_sem acquired; struct mspi_xfer_packet packet; struct mspi_xfer xfer; - struct mspi_dev_cfg *curr_cfg; + bool in_target_io_mode; }; struct flash_mspi_nor_cmd { @@ -64,32 +66,16 @@ struct flash_mspi_nor_cmd { 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; }; const struct flash_mspi_nor_cmds commands_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, @@ -97,16 +83,6 @@ const struct flash_mspi_nor_cmds commands_single = { .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, @@ -124,27 +100,9 @@ const struct flash_mspi_nor_cmds commands_single = { .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, - }, }; const struct flash_mspi_nor_cmds commands_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, @@ -152,18 +110,6 @@ const struct flash_mspi_nor_cmds commands_quad_1_4_4 = { .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, @@ -175,36 +121,15 @@ const struct flash_mspi_nor_cmds commands_quad_1_4_4 = { .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, - }, }; const struct flash_mspi_nor_cmds commands_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, @@ -212,13 +137,6 @@ const struct flash_mspi_nor_cmds commands_octal = { .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, @@ -236,17 +154,8 @@ const struct flash_mspi_nor_cmds commands_octal = { .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 diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index eb40ad1938aa1..f90bc8cb32ca7 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -64,45 +64,29 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) } /* 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); + rc = wait_until_ready(dev, K_USEC(1)); + if (rc < 0) { + return rc; + } /* Write enable */ - flash_mspi_command_set(dev, &commands_single.write_en); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = cmd_wren(dev); 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); + setup_xfer(dev, MSPI_TX); 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); + rc = perform_xfer(dev, SPI_NOR_CMD_WRSR, false); 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); + rc = cmd_rdsr(dev, SPI_NOR_CMD_RDSR, &status); if (rc < 0) { return rc; } @@ -113,10 +97,10 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) } /* Verify configuration registers */ - flash_mspi_command_set(dev, &dev_config->jedec_cmds->config); - dev_data->packet.data_buf = config; + setup_xfer(dev, MSPI_RX); dev_data->packet.num_bytes = sizeof(config); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer); + dev_data->packet.data_buf = config; + rc = perform_xfer(dev, SPI_NOR_CMD_RDCR, false); if (rc < 0) { return rc; } @@ -154,26 +138,18 @@ static inline int mxicy_mx25u_post_switch_mode(const struct device *dev) } /* Write enable */ - flash_mspi_command_set(dev, &commands_single.write_en); - rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, - &dev_data->xfer); + rc = cmd_wren(dev); 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); + setup_xfer(dev, MSPI_TX); + dev_data->xfer.addr_length = 4; + dev_data->packet.address = 0; 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; + return perform_xfer(dev, SPI_NOR_CMD_WR_CFGREG2, false); } struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25u = { From 4d2f018510979820594b91f8f73dbf25642e603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Fri, 11 Jul 2025 15:24:07 +0200 Subject: [PATCH 3/5] drivers: flash_mspi_nor: Complete handling of QER, add handling of OER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete implementation of quad_enable_set() by adding support for all possible Quad Enable Requirements (QER) as specified by the SFDP JEDEC standard (JESD216). Add also corresponding octal_enable_set() to handle Octal Enable Requirements. Signed-off-by: Andrzej Głąbek --- drivers/flash/flash_mspi_nor.c | 155 ++++++++++++++++++++++---- drivers/flash/flash_mspi_nor_quirks.h | 6 - 2 files changed, 135 insertions(+), 26 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 49a78f6d2b457..e8e18bb2d79f4 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -558,31 +558,139 @@ static int dev_pm_action_cb(const struct device *dev, static int quad_enable_set(const struct device *dev, bool enable) { const struct flash_mspi_nor_config *dev_config = dev->config; + uint8_t op_code; + uint8_t qe_bit; + uint8_t status_reg; + uint8_t payload_len; + uint8_t payload[2]; int rc; - rc = cmd_wren(dev); + switch (dev_config->dw15_qer) { + case JESD216_DW15_QER_VAL_S1B6: + op_code = SPI_NOR_CMD_RDSR; + qe_bit = BIT(6); + break; + case JESD216_DW15_QER_VAL_S2B7: + /* Use special Read status register 2 instruction. */ + op_code = 0x3F; + qe_bit = BIT(7); + break; + case JESD216_DW15_QER_VAL_S2B1v1: + case JESD216_DW15_QER_VAL_S2B1v4: + case JESD216_DW15_QER_VAL_S2B1v5: + case JESD216_DW15_QER_VAL_S2B1v6: + op_code = SPI_NOR_CMD_RDSR2; + qe_bit = BIT(1); + break; + default: + LOG_ERR("Unknown Quad Enable Requirement: %u", + dev_config->dw15_qer); + return -ENOTSUP; + } + + rc = cmd_rdsr(dev, op_code, &status_reg); if (rc < 0) { - LOG_ERR("Failed to set write enable: %d", rc); return rc; } - if (dev_config->dw15_qer == JESD216_DW15_QER_VAL_S1B6) { - uint8_t mode_payload = enable ? BIT(6) : 0; + if (((status_reg & qe_bit) != 0) == enable) { + /* Nothing to do, the QE bit is already set properly. */ + return 0; + } + + status_reg ^= qe_bit; - rc = cmd_wrsr(dev, SPI_NOR_CMD_WRSR, 1, &mode_payload); + switch (dev_config->dw15_qer) { + default: + case JESD216_DW15_QER_VAL_S1B6: + payload_len = 1; + op_code = SPI_NOR_CMD_WRSR; + break; + case JESD216_DW15_QER_VAL_S2B7: + payload_len = 1; + /* Use special Write status register 2 instruction. */ + op_code = 0x3E; + break; + case JESD216_DW15_QER_VAL_S2B1v1: + case JESD216_DW15_QER_VAL_S2B1v4: + case JESD216_DW15_QER_VAL_S2B1v5: + payload_len = 2; + op_code = SPI_NOR_CMD_WRSR; + break; + case JESD216_DW15_QER_VAL_S2B1v6: + payload_len = 1; + op_code = SPI_NOR_CMD_WRSR2; + break; + } + + if (payload_len == 1) { + payload[0] = status_reg; + } else { + payload[1] = status_reg; + + /* When the Write Status command is to be sent with two data + * bytes (this is the case for S2B1v1, S2B1v4, and S2B1v5 QER + * values), the first status register needs to be read and + * sent as the first byte, so that its value is not modified. + */ + rc = cmd_rdsr(dev, SPI_NOR_CMD_RDSR, &payload[0]); if (rc < 0) { - LOG_ERR("Failed to enable/disable quad mode: %d", rc); return rc; } - } else { - /* TODO: handle all DW15 QER values */ + } + + rc = cmd_wrsr(dev, op_code, payload_len, payload); + if (rc < 0) { + return rc; + } + + return 0; +} + +static int octal_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; + uint8_t op_code; + uint8_t oe_bit; + uint8_t status_reg; + int rc; + + if (dev_config->dw19_oer != BFP_DW19_OER_VAL_S2B3) { + LOG_ERR("Unknown Octal Enable Requirement: %u", + dev_config->dw15_qer); return -ENOTSUP; } - rc = wait_until_ready(dev, K_USEC(1)); + oe_bit = BIT(3); + /* Use special Read status register 2 instruction 0x65 with one address + * byte 0x02 and one dummy byte. + */ + op_code = 0x65; + setup_xfer(dev, MSPI_RX); + dev_data->xfer.rx_dummy = 8; + dev_data->xfer.addr_length = 1; + dev_data->packet.address = 0x02; + dev_data->packet.num_bytes = sizeof(uint8_t); + dev_data->packet.data_buf = &status_reg; + rc = perform_xfer(dev, op_code, false); + if (rc < 0) { + LOG_ERR("cmd_rdsr 0x%02x failed: %d", op_code, rc); + return rc; + } + + if (((status_reg & oe_bit) != 0) == enable) { + /* Nothing to do, the OE bit is already set properly. */ + return 0; + } + + status_reg ^= oe_bit; + + /* Use special Write status register 2 instruction to clear the bit. */ + op_code = (status_reg & oe_bit) ? SPI_NOR_CMD_WRSR2 : 0x3E; + rc = cmd_wrsr(dev, op_code, 1, &status_reg); if (rc < 0) { - LOG_ERR("Failed waiting until device ready after enabling quad: %d", rc); return rc; } @@ -596,22 +704,29 @@ static int switch_to_target_io_mode(const struct device *dev) int rc = 0; if (dev_config->dw15_qer != JESD216_DW15_QER_VAL_NONE) { - /* 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); - } + bool quad_needed = io_mode == MSPI_IO_MODE_QUAD_1_1_4 || + io_mode == MSPI_IO_MODE_QUAD_1_4_4 || + io_mode == MSPI_IO_MODE_QUAD; + rc = quad_enable_set(dev, quad_needed); 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)) { + if (dev_config->dw19_oer != BFP_DW19_OER_VAL_NONE) { + bool octal_needed = io_mode == MSPI_IO_MODE_OCTAL_1_1_8 || + io_mode == MSPI_IO_MODE_OCTAL_1_8_8 || + io_mode == MSPI_IO_MODE_OCTAL; + + rc = octal_enable_set(dev, octal_needed); + if (rc < 0) { + LOG_ERR("Failed to modify Octal Enable bit: %d", rc); + } + } + + if (dev_config->quirks != NULL && + dev_config->quirks->post_switch_mode != NULL) { rc = dev_config->quirks->post_switch_mode(dev); } diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index f90bc8cb32ca7..ff0e3b3980805 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -63,12 +63,6 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) return 0; } - /* Wait for previous write to finish */ - rc = wait_until_ready(dev, K_USEC(1)); - if (rc < 0) { - return rc; - } - /* Write enable */ rc = cmd_wren(dev); if (rc < 0) { From 7b485649122ff9e8334c28ac7b44033cc806ee16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Fri, 11 Jul 2025 16:54:05 +0200 Subject: [PATCH 4/5] drivers: flash_mspi_nor: Add Soft Reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add implementation of the most common Soft Reset routine (sequence of reset enable instruction 0x66 and reset instruction 0x99). Signed-off-by: Andrzej Głąbek --- drivers/flash/flash_mspi_nor.c | 93 +++++++++++++++++++++++++++- drivers/flash/flash_mspi_nor.h | 6 +- dts/bindings/mtd/jedec,mspi-nor.yaml | 6 ++ 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index e8e18bb2d79f4..c211b495c07b6 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -13,6 +13,8 @@ #include "flash_mspi_nor.h" +#define BFP_DW16_SOFT_RESET_66_99 0x10 + #define BFP_DW18_CMD_EXT_SAME 0 #define BFP_DW18_CMD_EXT_INV 1 @@ -779,6 +781,77 @@ static int gpio_reset(const struct device *dev) } #endif +#if defined(WITH_SOFT_RESET) +static int soft_reset_66_99(const struct device *dev) +{ + int rc; + + setup_xfer(dev, MSPI_TX); + rc = perform_xfer(dev, SPI_NOR_CMD_RESET_EN, false); + if (rc < 0) { + LOG_ERR("CMD_RESET_EN failed: %d", rc); + return rc; + } + + setup_xfer(dev, MSPI_TX); + rc = perform_xfer(dev, SPI_NOR_CMD_RESET_MEM, false); + if (rc < 0) { + LOG_ERR("CMD_RESET_MEM failed: %d", rc); + return rc; + } + + return 0; +} + +static int soft_reset(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 the flash may expect commands sent in multi-line mode, + * send additionally the reset sequence this way. + */ + if (dev_config->multi_io_cmd) { + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_IO_MODE, + &dev_config->mspi_nor_cfg); + if (rc < 0) { + LOG_ERR("%s: dev_config() failed: %d", __func__, rc); + return rc; + } + + dev_data->in_target_io_mode = true; + + rc = soft_reset_66_99(dev); + if (rc < 0) { + return rc; + } + + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_IO_MODE, + &dev_config->mspi_nor_init_cfg); + if (rc < 0) { + LOG_ERR("%s: dev_config() failed: %d", __func__, rc); + return rc; + } + + dev_data->in_target_io_mode = false; + } + + rc = soft_reset_66_99(dev); + if (rc < 0) { + return rc; + } + + if (dev_config->reset_recovery_us != 0) { + k_busy_wait(dev_config->reset_recovery_us); + } + + return 0; +} +#endif /* WITH_SOFT_RESET */ + static int flash_chip_init(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; @@ -827,6 +900,15 @@ static int flash_chip_init(const struct device *dev) } #endif +#if defined(WITH_SOFT_RESET) + if (dev_config->initial_soft_reset) { + rc = soft_reset(dev); + if (rc < 0) { + return rc; + } + } +#endif + rc = switch_to_target_io_mode(dev); if (rc < 0) { LOG_ERR("Failed to switch to target io mode: %d", rc); @@ -1061,6 +1143,11 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == <= BFP_DW18_CMD_EXT_INV), \ "Unsupported Octal Command Extension mode in " \ DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ + BUILD_ASSERT(!DT_INST_PROP(inst, initial_soft_reset) || \ + (SFDP_FIELD(inst, sfdp_bfp, 16, GENMASK(13, 8)) \ + & BFP_DW16_SOFT_RESET_66_99), \ + "Cannot use 66h/99h soft reset sequence for " \ + DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ 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 = { \ @@ -1073,10 +1160,10 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == (.xip_cfg = MSPI_XIP_CONFIG_DT_INST(inst),)) \ IF_ENABLED(WITH_RESET_GPIO, \ (.reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \ - .reset_pulse_us = DT_INST_PROP_OR(inst, t_reset_pulse, 0) \ - / 1000, \ + .reset_pulse_us = DT_INST_PROP_OR(inst, t_reset_pulse, 0) \ + / 1000,)) \ .reset_recovery_us = DT_INST_PROP_OR(inst, t_reset_recovery, 0) \ - / 1000,)) \ + / 1000, \ FLASH_PAGE_LAYOUT_DEFINE(inst) \ .jedec_id = DT_INST_PROP(inst, jedec_id), \ .jedec_cmds = FLASH_CMDS(inst), \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index c2148dca8a068..82161822ec598 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -19,6 +19,9 @@ extern "C" { #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) #define WITH_RESET_GPIO 1 #endif +#if DT_ANY_INST_HAS_BOOL_STATUS_OKAY(initial_soft_reset) +#define WITH_SOFT_RESET 1 +#endif struct flash_mspi_nor_config { const struct device *bus; @@ -32,8 +35,8 @@ struct flash_mspi_nor_config { #if defined(WITH_RESET_GPIO) struct gpio_dt_spec reset; uint32_t reset_pulse_us; - uint32_t reset_recovery_us; #endif + uint32_t reset_recovery_us; #if defined(CONFIG_FLASH_PAGE_LAYOUT) struct flash_pages_layout layout; #endif @@ -41,6 +44,7 @@ struct flash_mspi_nor_config { const struct flash_mspi_nor_cmds *jedec_cmds; struct flash_mspi_nor_quirks *quirks; bool multiperipheral_bus : 1; + bool initial_soft_reset : 1; uint8_t dw15_qer : 3; uint8_t dw19_oer : 3; bool cmd_ext_inv : 1; diff --git a/dts/bindings/mtd/jedec,mspi-nor.yaml b/dts/bindings/mtd/jedec,mspi-nor.yaml index 5bad8f54b5dc6..0e3ca4b39b495 100644 --- a/dts/bindings/mtd/jedec,mspi-nor.yaml +++ b/dts/bindings/mtd/jedec,mspi-nor.yaml @@ -22,3 +22,9 @@ properties: type: int description: | Minimum time, in nanoseconds, the flash chip needs to recover after reset. + + initial-soft-reset: + type: boolean + description: | + When set, the flash driver performs software reset of the flash chip + at initialization. From 64f66e8129c84b0649d61f8563ed786fc65045ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Mon, 14 Jul 2025 11:51:50 +0200 Subject: [PATCH 5/5] drivers: flash_mspi_nor: Remove undesirable initial Quad disabling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a GPIO reset for a flash chip that has a dual function pin (RESET# or SIO3) and is to be used in Quad mode is rather a bad idea and so is clearing of the Quad Enable bit at every initialization of the flash driver, since this bit is usually non-volatile, so such operation means unnecessary wearing of the flash chip. Soft Reset should be use instead in such case. Signed-off-by: Andrzej Głąbek --- drivers/flash/flash_mspi_nor.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index c211b495c07b6..e693b8233ba96 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -856,7 +856,6 @@ 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; @@ -869,28 +868,6 @@ static int flash_chip_init(const struct device *dev) dev_data->in_target_io_mode = false; - /* Some chips reuse RESET pin for data in Quad modes: - * force single line mode before resetting. - */ - if (dev_config->dw15_qer != JESD216_DW15_QER_VAL_NONE && - (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);