diff --git a/drivers/flash/Kconfig.mspi b/drivers/flash/Kconfig.mspi index d0f244957d2..5740f3618d7 100644 --- a/drivers/flash/Kconfig.mspi +++ b/drivers/flash/Kconfig.mspi @@ -35,10 +35,29 @@ menuconfig FLASH_MSPI_NOR select FLASH_MSPI select FLASH_HAS_EXPLICIT_ERASE select FLASH_JESD216 - select GPIO if $(dt_compat_any_has_prop,$(DT_COMPAT_JEDEC_MSPI_NOR),reset-gpios) + select GPIO if $(dt_compat_any_has_prop,$(DT_COMPAT_JEDEC_MSPI_NOR),reset-gpios) \ + || $(dt_compat_any_has_prop,$(DT_COMPAT_JEDEC_MSPI_NOR),supply-gpios) if FLASH_MSPI_NOR +config FLASH_MSPI_NOR_USE_SFDP + bool "Use Serial Flash Discoverable Parameters (SFDP)" + default $(dt_compat_any_has_prop,$(DT_COMPAT_JEDEC_MSPI_NOR),sfdp-bfp) + help + Use information from SFDP for setting up flash command transfers + and for configuring the flash chip. + + Currently, only build time processing of SFDP structures is supported, + based on dts arrays provided as flash node properties like `sfdp-bfp`, + `sfdp-ff05`, and `sfdp-ff84`. Depending on the IO mode used, some or + all of these properties are required for building the flash driver. + Data for these properties can be obtained with the `drivers/jesd216` + sample. If a given SFDP table is not available in a flash chip, + the related property can be defined as an empty array - this will + prevent a related build assertion from failing and default values + will be assigned to parameters that would otherwise be read from + that SFDP table. + config FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE int "Page size to use for FLASH_LAYOUT feature" depends on FLASH_PAGE_LAYOUT diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index bbda4c6361d..a634b3d9118 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -14,11 +14,20 @@ #include "flash_mspi_nor.h" #include "flash_mspi_nor_sfdp.h" -#define INVALID_DTS_RX_DUMMY 0xFF +LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); + +#define XIP_DEV_CFG_MASK (MSPI_DEVICE_CONFIG_CMD_LEN | \ + MSPI_DEVICE_CONFIG_ADDR_LEN | \ + MSPI_DEVICE_CONFIG_READ_CMD | \ + MSPI_DEVICE_CONFIG_WRITE_CMD | \ + MSPI_DEVICE_CONFIG_RX_DUMMY | \ + MSPI_DEVICE_CONFIG_TX_DUMMY) + +#define NON_XIP_DEV_CFG_MASK (MSPI_DEVICE_CONFIG_ALL & ~XIP_DEV_CFG_MASK) static void set_up_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); + uint8_t cmd, bool mem_access); 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); @@ -27,8 +36,6 @@ static int cmd_wrsr(const struct device *dev, uint8_t op_code, #include "flash_mspi_nor_quirks.h" -LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); - static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) { const struct flash_mspi_nor_config *dev_config = dev->config; @@ -57,8 +64,21 @@ static void set_up_xfer_with_addr(const struct device *dev, dev_data->packet.address = addr; } +static uint16_t get_extended_command(const struct device *dev, + uint8_t cmd) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + uint8_t cmd_extension = cmd; + + if (dev_data->cmd_info.cmd_extension == CMD_EXTENSION_INVERSE) { + cmd_extension = ~cmd_extension; + } + + return ((uint16_t)cmd << 8) | cmd_extension; +} + static int perform_xfer(const struct device *dev, - uint8_t cmd, bool data_transfer) + uint8_t cmd, bool mem_access) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; @@ -67,15 +87,8 @@ static int perform_xfer(const struct device *dev, if (dev_data->cmd_info.cmd_extension != CMD_EXTENSION_NONE && dev_data->in_target_io_mode) { - uint8_t cmd_extension = cmd; - - if (dev_data->cmd_info.cmd_extension == CMD_EXTENSION_INVERSE) { - cmd_extension = ~cmd_extension; - } - dev_data->xfer.cmd_length = 2; - dev_data->packet.cmd = ((uint16_t)cmd << 8) - | cmd_extension; + dev_data->packet.cmd = get_extended_command(dev, cmd); } else { dev_data->xfer.cmd_length = 1; dev_data->packet.cmd = cmd; @@ -86,9 +99,9 @@ static int perform_xfer(const struct device *dev, /* 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. + } else if (mem_access) { + /* For commands accessing the flash memory (read and program), + * ensure that the target IO mode is active. */ if (!dev_data->in_target_io_mode) { cfg = &dev_config->mspi_nor_cfg; @@ -119,7 +132,7 @@ static int perform_xfer(const struct device *dev, return rc; } - dev_data->in_target_io_mode = data_transfer; + dev_data->in_target_io_mode = mem_access; } rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, @@ -132,83 +145,6 @@ static int perform_xfer(const struct device *dev, return 0; } -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; - int rc; - - k_sem_take(&dev_data->acquired, K_FOREVER); - - rc = pm_device_runtime_get(dev_config->bus); - 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, - 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; - } - - (void)pm_device_runtime_put(dev_config->bus); - } - - k_sem_give(&dev_data->acquired); - return rc; -} - -static void release(const struct device *dev) -{ - const struct flash_mspi_nor_config *dev_config = dev->config; - struct flash_mspi_nor_data *dev_data = dev->data; - - /* This releases the MSPI controller. */ - (void)mspi_get_channel_status(dev_config->bus, 0); - - (void)pm_device_runtime_put(dev_config->bus); - - k_sem_give(&dev_data->acquired); -} - -static inline uint32_t dev_flash_size(const struct device *dev) -{ - const struct flash_mspi_nor_config *dev_config = dev->config; - - return dev_config->flash_size; -} - -static inline uint16_t dev_page_size(const struct device *dev) -{ - const struct flash_mspi_nor_config *dev_config = dev->config; - - return dev_config->page_size; -} - -static inline -const struct jesd216_erase_type *dev_erase_types(const struct device *dev) -{ - struct flash_mspi_nor_data *dev_data = dev->data; - - return dev_data->erase_types; -} - static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) { struct flash_mspi_nor_data *dev_data = dev->data; @@ -295,10 +231,103 @@ static int cmd_wrsr(const struct device *dev, uint8_t op_code, return 0; } +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; + int rc; + + k_sem_take(&dev_data->acquired, K_FOREVER); + + rc = pm_device_runtime_get(dev_config->bus); + 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 = NON_XIP_DEV_CFG_MASK; + } 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, + 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; + } + + (void)pm_device_runtime_put(dev_config->bus); + } + + k_sem_give(&dev_data->acquired); + return rc; +} + +static void release(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + /* This releases the MSPI controller. */ + (void)mspi_get_channel_status(dev_config->bus, 0); + + (void)pm_device_runtime_put(dev_config->bus); + + k_sem_give(&dev_data->acquired); +} + +static inline uint32_t dev_flash_size(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + + return dev_config->flash_size; +} + +static inline uint16_t dev_page_size(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + + return dev_config->page_size; +} + +static inline +const struct jesd216_erase_type *dev_erase_types(const struct device *dev) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + + return dev_data->erase_types; +} + +static uint8_t get_rx_dummy(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + /* If the number of RX dummy cycles is specified in dts, use that value. */ + if (dev_config->rx_dummy_specified) { + return dev_config->mspi_nor_cfg.rx_dummy; + } + + /* Since it's not yet possible to specify mode bits with MSPI API, + * treat mode bit cycles as just dummy. + */ + return dev_data->cmd_info.read_mode_bit_cycles + + dev_data->cmd_info.read_dummy_cycles; +} + 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; @@ -317,12 +346,7 @@ static int api_read(const struct device *dev, off_t addr, void *dest, } set_up_xfer_with_addr(dev, MSPI_RX, addr); - if (dev_config->dts_rx_dummy != INVALID_DTS_RX_DUMMY) { - dev_data->xfer.rx_dummy = dev_config->dts_rx_dummy; - } else { - dev_data->xfer.rx_dummy = dev_data->cmd_info.read_mode_clocks - + dev_data->cmd_info.read_dummy_clocks; - } + dev_data->xfer.rx_dummy = get_rx_dummy(dev); dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = size; rc = perform_xfer(dev, dev_data->cmd_info.read_cmd, true); @@ -480,6 +504,12 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) return rc; } +static int api_get_size(const struct device *dev, uint64_t *size) +{ + *size = dev_flash_size(dev); + return 0; +} + static const struct flash_parameters *api_get_parameters(const struct device *dev) { @@ -493,6 +523,33 @@ struct flash_parameters *api_get_parameters(const struct device *dev) return ¶meters; } +static int sfdp_read(const struct device *dev, off_t addr, void *dest, + size_t size) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + set_up_xfer(dev, MSPI_RX); + if (dev_data->in_target_io_mode) { + dev_data->xfer.rx_dummy = dev_data->cmd_info.sfdp_dummy_20 + ? 20 : 8; + dev_data->xfer.addr_length = dev_data->cmd_info.sfdp_addr_4 + ? 4 : 3; + } else { + dev_data->xfer.rx_dummy = 8; + dev_data->xfer.addr_length = 3; + } + dev_data->packet.address = addr; + dev_data->packet.data_buf = dest; + dev_data->packet.num_bytes = size; + rc = perform_xfer(dev, JESD216_CMD_READ_SFDP, false); + if (rc < 0) { + LOG_ERR("Read SFDP xfer failed: %d", rc); + } + + return rc; +} + static int read_jedec_id(const struct device *dev, uint8_t *id) { struct flash_mspi_nor_data *dev_data = dev->data; @@ -530,7 +587,6 @@ static void api_page_layout(const struct device *dev, static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, size_t size) { - struct flash_mspi_nor_data *dev_data = dev->data; int rc; if (size == 0) { @@ -542,23 +598,7 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, return rc; } - set_up_xfer(dev, MSPI_RX); - if (dev_data->in_target_io_mode) { - dev_data->xfer.rx_dummy = dev_data->cmd_info.sfdp_dummy_20 - ? 20 : 8; - dev_data->xfer.addr_length = dev_data->cmd_info.sfdp_addr_4 - ? 4 : 3; - } else { - dev_data->xfer.rx_dummy = 8; - dev_data->xfer.addr_length = 3; - } - dev_data->packet.address = addr; - dev_data->packet.data_buf = dest; - dev_data->packet.num_bytes = size; - rc = perform_xfer(dev, JESD216_CMD_READ_SFDP, false); - if (rc < 0) { - LOG_ERR("Read SFDP xfer failed: %d", rc); - } + rc = sfdp_read(dev, addr, dest, size); release(dev); @@ -695,7 +735,7 @@ static int octal_enable_set(const struct device *dev, bool enable) uint8_t status_reg; int rc; - if (dev_data->switch_info.octal_enable_req != BFP_DW19_OER_VAL_S2B3) { + if (dev_data->switch_info.octal_enable_req != OCTAL_ENABLE_REQ_S2B3) { LOG_ERR("Unknown Octal Enable Requirement: %u", dev_data->switch_info.octal_enable_req); return -ENOTSUP; @@ -767,8 +807,7 @@ static int switch_to_target_io_mode(const struct device *dev) if (dev_data->switch_info.quad_enable_req != JESD216_DW15_QER_VAL_NONE) { 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; + io_mode == MSPI_IO_MODE_QUAD_1_4_4; rc = quad_enable_set(dev, quad_needed); if (rc < 0) { @@ -777,10 +816,9 @@ static int switch_to_target_io_mode(const struct device *dev) } } - if (dev_data->switch_info.octal_enable_req != BFP_DW19_OER_VAL_NONE) { + if (dev_data->switch_info.octal_enable_req != OCTAL_ENABLE_REQ_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; + io_mode == MSPI_IO_MODE_OCTAL_1_8_8; rc = octal_enable_set(dev, octal_needed); if (rc < 0) { @@ -806,7 +844,7 @@ static int switch_to_target_io_mode(const struct device *dev) } return mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_ALL, + NON_XIP_DEV_CFG_MASK, &dev_config->mspi_nor_cfg); } @@ -936,6 +974,8 @@ 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; uint8_t id[JESD216_READ_ID_LEN] = {0}; + uint16_t dts_cmd = 0; + uint32_t sfdp_signature; bool flash_reset = false; int rc; @@ -943,6 +983,7 @@ static int flash_chip_init(const struct device *dev) MSPI_DEVICE_CONFIG_ALL, &dev_config->mspi_nor_init_cfg); if (rc < 0) { + LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } @@ -990,6 +1031,55 @@ static int flash_chip_init(const struct device *dev) rc = dev_config->quirks->pre_init(dev); } + /* Allow users to specify commands for Read and Page Program operations + * through dts to override what was taken from SFDP and perhaps altered + * in the pre_init quirk. Also the number of dummy cycles for the Read + * operation can be overridden this way, see get_rx_dummy(). + */ + if (dev_config->mspi_nor_cfg.read_cmd != 0) { + dts_cmd = (uint16_t)dev_config->mspi_nor_cfg.read_cmd; + if (dev_config->mspi_nor_cfg.cmd_length > 1) { + dev_data->cmd_info.read_cmd = (uint8_t)(dts_cmd >> 8); + } else { + dev_data->cmd_info.read_cmd = (uint8_t)dts_cmd; + } + } + if (dev_config->mspi_nor_cfg.write_cmd != 0) { + dts_cmd = (uint16_t)dev_config->mspi_nor_cfg.write_cmd; + if (dev_config->mspi_nor_cfg.cmd_length > 1) { + dev_data->cmd_info.pp_cmd = (uint8_t)(dts_cmd >> 8); + } else { + dev_data->cmd_info.pp_cmd = (uint8_t)dts_cmd; + } + } + if (dts_cmd != 0) { + if (dev_config->mspi_nor_cfg.cmd_length <= 1) { + dev_data->cmd_info.cmd_extension = CMD_EXTENSION_NONE; + } else if ((dts_cmd & 0xFF) == ((dts_cmd >> 8) & 0xFF)) { + dev_data->cmd_info.cmd_extension = CMD_EXTENSION_SAME; + } else { + dev_data->cmd_info.cmd_extension = CMD_EXTENSION_INVERSE; + } + } + + if (dev_config->jedec_id_specified) { + rc = read_jedec_id(dev, id); + if (rc < 0) { + LOG_ERR("Failed to read JEDEC ID: %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", + id[0], id[1], id[2], + dev_config->jedec_id[0], + dev_config->jedec_id[1], + dev_config->jedec_id[2]); + return -ENODEV; + } + } + rc = switch_to_target_io_mode(dev); if (rc < 0) { LOG_ERR("Failed to switch to target io mode: %d", rc); @@ -998,28 +1088,57 @@ static int flash_chip_init(const struct device *dev) dev_data->in_target_io_mode = true; - rc = read_jedec_id(dev, id); - if (rc < 0) { - LOG_ERR("Failed to read JEDEC ID: %d", rc); - return rc; - } + if (IS_ENABLED(CONFIG_FLASH_MSPI_NOR_USE_SFDP)) { + /* Read the SFDP signature to test if communication with + * the flash chip can be successfully performed after switching + * to target IO mode. + */ + rc = sfdp_read(dev, 0, &sfdp_signature, sizeof(sfdp_signature)); + if (rc < 0) { + LOG_ERR("Failed to read SFDP signature: %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", - id[0], id[1], id[2], - dev_config->jedec_id[0], - dev_config->jedec_id[1], - dev_config->jedec_id[2]); - return -ENODEV; + if (sfdp_signature != JESD216_SFDP_MAGIC) { + LOG_ERR("SFDP signature mismatch: %08x, expected: %08x", + sfdp_signature, JESD216_SFDP_MAGIC); + return -ENODEV; + } } #if defined(CONFIG_MSPI_XIP) /* Enable XIP access for this chip if specified so in DT. */ if (dev_config->xip_cfg.enable) { + struct mspi_dev_cfg mspi_cfg = { + .addr_length = dev_data->cmd_info.uses_4byte_addr + ? 4 : 3, + .rx_dummy = get_rx_dummy(dev), + }; + + if (dev_data->cmd_info.cmd_extension != CMD_EXTENSION_NONE) { + mspi_cfg.cmd_length = 2; + mspi_cfg.read_cmd = get_extended_command(dev, + dev_data->cmd_info.read_cmd); + mspi_cfg.write_cmd = get_extended_command(dev, + dev_data->cmd_info.pp_cmd); + } else { + mspi_cfg.cmd_length = 1; + mspi_cfg.read_cmd = dev_data->cmd_info.read_cmd; + mspi_cfg.write_cmd = dev_data->cmd_info.pp_cmd; + } + + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + XIP_DEV_CFG_MASK, &mspi_cfg); + if (rc < 0) { + LOG_ERR("Failed to configure controller for XIP: %d", + rc); + return rc; + } + rc = mspi_xip_config(dev_config->bus, &dev_config->mspi_id, &dev_config->xip_cfg); if (rc < 0) { + LOG_ERR("Failed to enable XIP: %d", rc); return rc; } } @@ -1063,18 +1182,6 @@ static int drv_init(const struct device *dev) return rc; } - /* Allow users to specify commands for Read and Page Program operations - * through dts and override what was taken from SFDP. Also the number - * of dummy cycles for the Read operation can be overridden this way - * (see api_read() and dts_rx_dummy). - */ - if (dev_config->dts_read_command != 0) { - dev_data->cmd_info.read_cmd = dev_config->dts_read_command; - } - if (dev_config->dts_write_command != 0) { - dev_data->cmd_info.pp_cmd = dev_config->dts_write_command; - } - if (dev_data->cmd_info.read_cmd == 0) { LOG_ERR("Read command not defined for %s, " "use \"read-command\" property to specify it.", @@ -1092,10 +1199,10 @@ static int drv_init(const struct device *dev) LOG_DBG("%s - size: %u, page %u%s", dev->name, dev_flash_size(dev), dev_page_size(dev), dev_data->cmd_info.uses_4byte_addr ? ", 4-byte addressing" : ""); - LOG_DBG("- read command: 0x%02X with %u mode and %u dummy cycles", + LOG_DBG("- read command: 0x%02X with %u mode bit and %u dummy cycles", dev_data->cmd_info.read_cmd, - dev_data->cmd_info.read_mode_clocks, - dev_data->cmd_info.read_dummy_clocks); + dev_data->cmd_info.read_mode_bit_cycles, + dev_data->cmd_info.read_dummy_cycles); LOG_DBG("- page program command: 0x%02X", dev_data->cmd_info.pp_cmd); LOG_DBG("- erase types:"); @@ -1117,6 +1224,7 @@ static DEVICE_API(flash, drv_api) = { .read = api_read, .write = api_write, .erase = api_erase, + .get_size = api_get_size, .get_parameters = api_get_parameters, #if defined(CONFIG_FLASH_PAGE_LAYOUT) .page_layout = api_page_layout, @@ -1139,16 +1247,6 @@ static DEVICE_API(flash, drv_api) = { .dqs_enable = false, \ } -#define FLASH_SIZE(inst) \ - (DT_INST_NODE_HAS_PROP(inst, size) \ - ? DT_INST_PROP(inst, size) / 8 \ - : BFP_FLASH_DENSITY(SFDP_DW(inst, sfdp_bfp, 2)) / 8) - -#define FLASH_PAGE_EXP(inst) SFDP_FIELD(inst, sfdp_bfp, 11, GENMASK(7, 4)) -#define FLASH_PAGE_SIZE(inst) \ - (FLASH_PAGE_EXP(inst) ? BIT(FLASH_PAGE_EXP(inst)) \ - : SPI_NOR_PAGE_SIZE) - #define FLASH_QUIRKS(inst) FLASH_MSPI_QUIRKS_GET(DT_DRV_INST(inst)) #define IO_MODE_FLAGS(io_mode) \ @@ -1188,9 +1286,6 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ #define FLASH_MSPI_NOR_INST(inst) \ SFDP_BUILD_ASSERTS(inst); \ - BUILD_ASSERT(DT_INST_PROP_OR(inst, rx_dummy, 0) != INVALID_DTS_RX_DUMMY,\ - "Invalid number of RX dummy clocks specified for " \ - DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \ PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \ DEFAULT_ERASE_TYPES_DEFINE(inst); \ static struct flash_mspi_nor_data dev##inst##_data; \ @@ -1213,15 +1308,13 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ / 1000, \ .transfer_timeout = DT_INST_PROP(inst, transfer_timeout), \ FLASH_PAGE_LAYOUT_DEFINE(inst) \ - .jedec_id = DT_INST_PROP(inst, jedec_id), \ + .jedec_id = DT_INST_PROP_OR(inst, jedec_id, {0}), \ .quirks = FLASH_QUIRKS(inst), \ .default_erase_types = DEFAULT_ERASE_TYPES(inst), \ .default_cmd_info = DEFAULT_CMD_INFO(inst), \ .default_switch_info = DEFAULT_SWITCH_INFO(inst), \ - .dts_read_command = DT_INST_PROP_OR(inst, read_command, 0), \ - .dts_write_command = DT_INST_PROP_OR(inst, write_command, 0), \ - .dts_rx_dummy = DT_INST_PROP_OR(inst, rx_dummy, \ - INVALID_DTS_RX_DUMMY), \ + .jedec_id_specified = DT_INST_NODE_HAS_PROP(inst, jedec_id), \ + .rx_dummy_specified = DT_INST_NODE_HAS_PROP(inst, rx_dummy), \ .multiperipheral_bus = DT_PROP(DT_INST_BUS(inst), \ software_multiperipheral), \ IO_MODE_FLAGS(DT_INST_ENUM_IDX(inst, mspi_io_mode)), \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index 950606fe3a8..480bccce472 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -30,14 +30,17 @@ extern "C" { #define CMD_EXTENSION_SAME 1 #define CMD_EXTENSION_INVERSE 2 +#define OCTAL_ENABLE_REQ_NONE 0 +#define OCTAL_ENABLE_REQ_S2B3 1 + #define ENTER_4BYTE_ADDR_NONE 0 #define ENTER_4BYTE_ADDR_B7 1 #define ENTER_4BYTE_ADDR_06_B7 2 struct flash_mspi_nor_cmd_info { uint8_t read_cmd; - uint8_t read_mode_clocks : 3; - uint8_t read_dummy_clocks : 5; + uint8_t read_mode_bit_cycles : 3; + uint8_t read_dummy_cycles : 5; uint8_t pp_cmd; bool uses_4byte_addr : 1; /* BFP, 18th DWORD, bits 30-29 */ @@ -92,9 +95,8 @@ struct flash_mspi_nor_config { const struct jesd216_erase_type *default_erase_types; struct flash_mspi_nor_cmd_info default_cmd_info; struct flash_mspi_nor_switch_info default_switch_info; - uint8_t dts_read_command; - uint8_t dts_rx_dummy; - uint8_t dts_write_command; + bool jedec_id_specified : 1; + bool rx_dummy_specified : 1; bool multiperipheral_bus : 1; bool multi_io_cmd : 1; bool single_io_addr : 1; diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index 7e2a75fa70c..d58fbea4ff9 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -123,21 +123,30 @@ struct flash_mspi_nor_quirks flash_quirks_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; + uint8_t opi_enable; int rc; if (io_mode != MSPI_IO_MODE_OCTAL) { return 0; } + /* + * TODO - replace this with a generic routine that uses information + * from SFDP header FF87 (Status, Control and Configuration + * Register Map) + */ + + if (dev_config->mspi_nor_cfg.data_rate == MSPI_DATA_RATE_DUAL) { + opi_enable = BIT(1); + } else { + opi_enable = BIT(0); + } + /* Write enable */ rc = cmd_wren(dev); if (rc < 0) { @@ -148,8 +157,8 @@ static inline int mxicy_mx25u_post_switch_mode(const struct device *dev) set_up_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); + dev_data->packet.data_buf = &opi_enable; + dev_data->packet.num_bytes = sizeof(opi_enable); return perform_xfer(dev, SPI_NOR_CMD_WR_CFGREG2, false); } @@ -157,12 +166,41 @@ static int mxicy_mx25u_pre_init(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; + static const uint8_t dummy_cycles[8] = { + 20, 18, 16, 14, 12, 10, 8, 6 + }; + uint8_t cfg_reg; + int rc; + + if (dev_config->mspi_nor_cfg.io_mode != MSPI_IO_MODE_OCTAL) { + return 0; + } - if (dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_OCTAL && - dev_config->mspi_nor_cfg.data_rate == MSPI_DATA_RATE_SINGLE) { + if (dev_config->mspi_nor_cfg.data_rate == MSPI_DATA_RATE_SINGLE) { dev_data->cmd_info.cmd_extension = CMD_EXTENSION_INVERSE; } + /* + * TODO - replace this with a generic routine that uses information + * from SFDP header FF87 (Status, Control and Configuration + * Register Map) + */ + + /* Read configured number of dummy cycles for memory reading commands. */ + set_up_xfer(dev, MSPI_RX); + dev_data->xfer.addr_length = 4; + dev_data->packet.address = 0x300; + dev_data->packet.data_buf = &cfg_reg; + dev_data->packet.num_bytes = sizeof(cfg_reg); + rc = perform_xfer(dev, SPI_NOR_CMD_RD_CFGREG2, false); + if (rc < 0) { + LOG_ERR("Failed to read Dummy Cycle from CFGREG2"); + return rc; + } + + dev_data->cmd_info.read_mode_bit_cycles = 0; + dev_data->cmd_info.read_dummy_cycles = dummy_cycles[cfg_reg & 0x7]; + return 0; } diff --git a/drivers/flash/flash_mspi_nor_sfdp.h b/drivers/flash/flash_mspi_nor_sfdp.h index b2a8ba80bec..8dbecff01e8 100644 --- a/drivers/flash/flash_mspi_nor_sfdp.h +++ b/drivers/flash/flash_mspi_nor_sfdp.h @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#ifdef CONFIG_FLASH_MSPI_NOR_USE_SFDP + #define BFP_DW16_SOFT_RESET_66_99 BIT(4) #define BFP_DW16_4B_ADDR_ENTER_B7 BIT(0) @@ -14,9 +16,6 @@ #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 - /* 32-bit words in SFDP arrays in devicetree are stored in little-endian byte * order. See jedec,jesd216.yaml */ @@ -44,29 +43,6 @@ #define SFDP_FIELD(inst, prop, dw_no, mask) \ FIELD_GET(mask, SFDP_DW(inst, prop, dw_no)) -#define BFP_FLASH_DENSITY(dw2) \ - ((dw2 & BIT(31)) \ - ? BIT(dw2 & BIT_MASK(31)) \ - : dw2) - -/* Quad Enable Requirement from 15th DWORD of BFP */ -#define BSP_DW15_QER(inst) \ - DT_INST_ENUM_IDX_OR(inst, quad_enable_requirements, \ - SFDP_FIELD(inst, sfdp_bfp, 15, GENMASK(22, 20))) - -/* Octal Enable Requirement from 19th DWORD of BFP */ -#define BSP_DW19_OER(inst) \ - SFDP_FIELD(inst, sfdp_bfp, 19, GENMASK(22, 20)) - -#define BFP_DW1_ADDRESS_BYTES(inst) \ - SFDP_FIELD(inst, sfdp_bfp, 1, GENMASK(18, 17)) - -#define BFP_DW18_CMD_EXT(inst) \ - SFDP_FIELD(inst, sfdp_bfp, 18, GENMASK(30, 29)) - -#define BFP_ENTER_4BYTE_ADDR_METHODS(inst) \ - SFDP_FIELD(inst, sfdp_bfp, 16, GENMASK(31, 24)) - #define USES_8D_8D_8D(inst) \ (DT_INST_ENUM_IDX(inst, mspi_io_mode) == MSPI_IO_MODE_OCTAL && \ DT_INST_ENUM_IDX(inst, mspi_data_rate) == MSPI_DATA_RATE_DUAL) @@ -119,18 +95,32 @@ #define USES_OCTAL_IO(inst) \ (DT_INST_ENUM_IDX(inst, mspi_io_mode) == MSPI_IO_MODE_OCTAL) +#define BFP_DW1_ADDRESS_BYTES(inst) \ + SFDP_FIELD(inst, sfdp_bfp, 1, GENMASK(18, 17)) + #define USES_4BYTE_ADDR(inst) \ (USES_OCTAL_IO(inst) || \ DT_INST_PROP(inst, use_4byte_addressing) || \ BFP_DW1_ADDRESS_BYTES(inst) == JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_4B) +#define BFP_ENTER_4BYTE_ADDR_METHODS(inst) \ + SFDP_FIELD(inst, sfdp_bfp, 16, GENMASK(31, 24)) + +#define HAS_4BYTE_ADDR_CMDS(inst) \ + (BFP_ENTER_4BYTE_ADDR_METHODS(inst) & BFP_DW16_4B_ADDR_PER_CMD) + +#define BFP_DW18_CMD_EXT(inst) \ + SFDP_FIELD(inst, sfdp_bfp, 18, GENMASK(30, 29)) + #define CMD_EXTENSION(inst) \ (!USES_8D_8D_8D(inst) ? CMD_EXTENSION_NONE : \ (BFP_DW18_CMD_EXT(inst) \ == BFP_DW18_CMD_EXT_INV) ? CMD_EXTENSION_INVERSE \ : CMD_EXTENSION_SAME) - +/* 1st DWORD of 4-byte Address Instruction Table (ID FF84) indicates commands + * that are supported by the chip. + */ #define FF84_DW1_BIT(inst, bit) (SFDP_DW(inst, sfdp_ff84, 1) & BIT(bit)) #define SFDP_CMD_PP(inst) \ @@ -179,13 +169,13 @@ 0 #define DEFAULT_CMD_INFO(inst) { \ - .pp_cmd = USES_4BYTE_ADDR(inst) \ + .pp_cmd = USES_4BYTE_ADDR(inst) && HAS_4BYTE_ADDR_CMDS(inst) \ ? SFDP_CMD_PP_4B(inst) \ : SFDP_CMD_PP(inst), \ - .read_cmd = USES_4BYTE_ADDR(inst) \ + .read_cmd = USES_4BYTE_ADDR(inst) && HAS_4BYTE_ADDR_CMDS(inst) \ ? SFDP_CMD_FAST_READ_4B(inst) \ : SFDP_CMD_FAST_READ(inst), \ - .read_mode_clocks = \ + .read_mode_bit_cycles = \ USES_1S_8S_8S(inst) ? SFDP_FIELD(inst, sfdp_bfp, 17, GENMASK(7, 5)) : \ USES_1S_1S_8S(inst) ? SFDP_FIELD(inst, sfdp_bfp, 17, GENMASK(23, 21)) : \ USES_4S_4D_4D(inst) ? SFDP_FIELD(inst, sfdp_bfp, 23, GENMASK(23, 21)) : \ @@ -200,9 +190,9 @@ USES_1S_1D_1D(inst) ? SFDP_FIELD(inst, sfdp_bfp, 22, GENMASK(7, 5)) : \ USES_1S_1S_1S(inst) ? 0 : \ 0, \ - .read_dummy_clocks = DT_INST_PROP_OR(inst, rx_dummy, \ - USES_8D_8D_8D(inst) ? 20 : \ - USES_8S_8S_8S(inst) ? 20 : \ + .read_dummy_cycles = DT_INST_PROP_OR(inst, rx_dummy, \ + USES_8D_8D_8D(inst) ? SFDP_FIELD(inst, sfdp_ff05, 6, GENMASK(4, 0)) : \ + USES_8S_8S_8S(inst) ? SFDP_FIELD(inst, sfdp_ff05, 6, GENMASK(9, 5)) : \ USES_1S_8S_8S(inst) ? SFDP_FIELD(inst, sfdp_bfp, 17, GENMASK(4, 0)) : \ USES_1S_1S_8S(inst) ? SFDP_FIELD(inst, sfdp_bfp, 17, GENMASK(20, 16)) : \ USES_4S_4D_4D(inst) ? SFDP_FIELD(inst, sfdp_bfp, 23, GENMASK(20, 16)) : \ @@ -299,11 +289,21 @@ .exp = 0x0C }})) #define DEFAULT_ERASE_TYPES(inst) \ - USES_4BYTE_ADDR(inst) ? dev##inst##_erase_types_4b \ - : dev##inst##_erase_types + USES_4BYTE_ADDR(inst) && HAS_4BYTE_ADDR_CMDS(inst) \ + ? dev##inst##_erase_types_4b \ + : dev##inst##_erase_types + +#define BFP_DW15_QER(inst) \ + SFDP_FIELD(inst, sfdp_bfp, 15, GENMASK(22, 20)) + +#define BFP_DW19_OER(inst) \ + SFDP_FIELD(inst, sfdp_bfp, 19, GENMASK(22, 20)) #define ENTER_4BYTE_ADDR(inst) \ (!USES_4BYTE_ADDR(inst) ? ENTER_4BYTE_ADDR_NONE : \ + (BFP_ENTER_4BYTE_ADDR_METHODS(inst) \ + & (BFP_DW16_4B_ADDR_PER_CMD | \ + BFP_DW16_4B_ADDR_ALWAYS)) ? ENTER_4BYTE_ADDR_NONE : \ (BFP_ENTER_4BYTE_ADDR_METHODS(inst) \ & BFP_DW16_4B_ADDR_ENTER_B7) ? ENTER_4BYTE_ADDR_B7 : \ (BFP_ENTER_4BYTE_ADDR_METHODS(inst) \ @@ -311,10 +311,27 @@ ENTER_4BYTE_ADDR_NONE) #define DEFAULT_SWITCH_INFO(inst) { \ - .quad_enable_req = BSP_DW15_QER(inst), \ - .octal_enable_req = BSP_DW19_OER(inst), \ + .quad_enable_req = BFP_DW15_QER(inst), \ + .octal_enable_req = BFP_DW19_OER(inst), \ .enter_4byte_addr = ENTER_4BYTE_ADDR(inst) } +#define BFP_FLASH_SIZE(dw2) \ + ((dw2 & BIT(31)) \ + ? BIT(MIN(31, (dw2 & BIT_MASK(31)) - 3)) \ + : dw2 / 8) + +#define FLASH_SIZE(inst) \ + (DT_INST_NODE_HAS_PROP(inst, size) \ + ? DT_INST_PROP(inst, size) / 8 \ + : BFP_FLASH_SIZE(SFDP_DW(inst, sfdp_bfp, 2))) + +#define BFP_FLASH_PAGE_EXP(inst) SFDP_FIELD(inst, sfdp_bfp, 11, GENMASK(7, 4)) + +#define FLASH_PAGE_SIZE(inst) \ + (BFP_FLASH_PAGE_EXP(inst) \ + ? BIT(BFP_FLASH_PAGE_EXP(inst)) \ + : SPI_NOR_PAGE_SIZE) + #define SFDP_BUILD_ASSERTS(inst) \ BUILD_ASSERT(DT_INST_NODE_HAS_PROP(inst, sfdp_bfp), \ "sfdp-bfp property needed in " \ @@ -350,3 +367,55 @@ & BFP_DW16_SOFT_RESET_66_99), \ "Cannot use 66h/99h soft reset sequence for " \ DT_NODE_FULL_NAME(DT_DRV_INST(inst))) + +#else + +#define USES_4BYTE_ADDR(inst) \ + (DT_INST_ENUM_IDX(inst, mspi_io_mode) == MSPI_IO_MODE_OCTAL || \ + DT_INST_PROP(inst, use_4byte_addressing)) + +#define DEFAULT_CMD_INFO(inst) { \ + .pp_cmd = USES_4BYTE_ADDR(inst) \ + ? SPI_NOR_CMD_PP_4B \ + : SPI_NOR_CMD_PP, \ + .read_cmd = USES_4BYTE_ADDR(inst) \ + ? SPI_NOR_CMD_READ_FAST_4B \ + : SPI_NOR_CMD_READ_FAST, \ + .read_mode_bit_cycles = 0, \ + .read_dummy_cycles = 8, \ + .uses_4byte_addr = USES_4BYTE_ADDR(inst), \ + .cmd_extension = CMD_EXTENSION_NONE, \ + .sfdp_addr_4 = false, \ + .sfdp_dummy_20 = false, \ + .rdsr_addr_4 = false, \ + .rdsr_dummy = 0, \ + .rdid_addr_4 = false, \ + .rdid_dummy = 0, } + +#define DEFAULT_ERASE_TYPES_DEFINE(inst) \ + static const struct jesd216_erase_type \ + dev##inst##_erase_types[JESD216_NUM_ERASE_TYPES] = \ + {{ .cmd = SPI_NOR_CMD_SE, \ + .exp = 0x0C }}; \ + static const struct jesd216_erase_type \ + dev##inst##_erase_types_4b[JESD216_NUM_ERASE_TYPES] = \ + {{ .cmd = SPI_NOR_CMD_SE_4B, \ + .exp = 0x0C }} + +#define DEFAULT_ERASE_TYPES(inst) \ + USES_4BYTE_ADDR(inst) ? dev##inst##_erase_types_4b \ + : dev##inst##_erase_types + +#define DEFAULT_SWITCH_INFO(inst) { \ + .quad_enable_req = DT_INST_ENUM_IDX_OR(inst, quad_enable_requirements, \ + JESD216_DW15_QER_VAL_NONE), \ + .octal_enable_req = OCTAL_ENABLE_REQ_NONE, \ + .enter_4byte_addr = ENTER_4BYTE_ADDR_NONE } + +#define FLASH_SIZE(inst) (DT_INST_PROP(inst, size) / 8) + +#define FLASH_PAGE_SIZE(inst) SPI_NOR_PAGE_SIZE + +#define SFDP_BUILD_ASSERTS(inst) + +#endif /* CONFIG_FLASH_MSPI_NOR_USE_SFDP */ diff --git a/dts/bindings/mtd/jedec,mspi-nor.yaml b/dts/bindings/mtd/jedec,mspi-nor.yaml index 06b72bf951c..348e11cd983 100644 --- a/dts/bindings/mtd/jedec,mspi-nor.yaml +++ b/dts/bindings/mtd/jedec,mspi-nor.yaml @@ -5,7 +5,20 @@ description: Generic NOR flash on MSPI bus compatible: "jedec,mspi-nor" -include: [mspi-device.yaml, "jedec,spi-nor-common.yaml"] +include: + - name: mspi-device.yaml + - name: jedec,spi-nor-common.yaml + property-allowlist: + - jedec-id + - size + - sfdp-bfp + - sfdp-ff05 + - sfdp-ff84 + - quad-enable-requirements + - has-dpd + - dpd-wakeup-sequence + - t-enter-dpd + - t-exit-dpd properties: reset-gpios: @@ -37,9 +50,10 @@ properties: type: boolean description: | Indicates that 4-byte addressing is to be used in communication with - the flash chip. The driver will use dedicated instruction codes for - commands that require addresses (like Read, Page Program, or Erase) - and will switch the flash chip to 4-byte addressing mode if necessary. + the flash chip. The driver will use dedicated 4-byte address instruction + codes for commands that require addresses (like Read, Page Program, + or Erase) if those are supported by the flash chip, or if necessary, + it will switch the chip to 4-byte addressing mode. initial-soft-reset: type: boolean diff --git a/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index 98d67e2ad08..789c47d3a47 100644 --- a/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -2,11 +2,16 @@ status = "okay"; }; +&gpio6 { + status = "okay"; +}; + +&exmif { + status = "okay"; +}; + &mx25uw63 { - read-command = <0xEC13>; - command-length = "INSTR_2_BYTE"; - address-length = "ADDR_4_BYTE"; - rx-dummy = <20>; + status = "okay"; xip-config = <1 0 0x20000000 0>; }; diff --git a/samples/application_development/code_relocation_nocopy/sample.yaml b/samples/application_development/code_relocation_nocopy/sample.yaml index 37e009bc987..62929419c53 100644 --- a/samples/application_development/code_relocation_nocopy/sample.yaml +++ b/samples/application_development/code_relocation_nocopy/sample.yaml @@ -6,6 +6,7 @@ tests: platform_allow: - qemu_cortex_m3 - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp - stm32f769i_disco - stm32h7b3i_dk - stm32h573i_dk