| /* |
| * Copyright (c) 2023 Nuvoton Technology Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT nuvoton_npcx_fiu_nor |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/flash.h> |
| #include <zephyr/drivers/flash/npcx_flash_api_ex.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h> |
| #include <soc.h> |
| #ifdef CONFIG_USERSPACE |
| #include <zephyr/syscall.h> |
| #include <zephyr/syscall_handler.h> |
| #endif |
| |
| #include "flash_npcx_fiu_qspi.h" |
| #include "spi_nor.h" |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(flash_npcx_fiu_nor, CONFIG_FLASH_LOG_LEVEL); |
| |
| #define BLOCK_64K_SIZE KB(64) |
| #define BLOCK_4K_SIZE KB(4) |
| |
| /* Device config */ |
| struct flash_npcx_nor_config { |
| /* QSPI bus device for mutex control and bus configuration */ |
| const struct device *qspi_bus; |
| /* Mapped address for flash read via direct access */ |
| uintptr_t mapped_addr; |
| /* Size of nor device in bytes, from size property */ |
| uint32_t flash_size; |
| /* Maximum chip erase time-out in ms */ |
| uint32_t max_timeout; |
| /* SPI Nor device configuration on QSPI bus */ |
| struct npcx_qspi_cfg qspi_cfg; |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| struct flash_pages_layout layout; |
| #endif |
| }; |
| |
| /* Device data */ |
| struct flash_npcx_nor_data { |
| /* Specific control operation for Quad-SPI Nor Flash */ |
| uint32_t operation; |
| }; |
| |
| static const struct flash_parameters flash_npcx_parameters = { |
| .write_block_size = 1, |
| .erase_value = 0xff, |
| }; |
| |
| #define DT_INST_QUAD_EN_PROP_OR(inst) \ |
| COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, quad_enable_requirements), \ |
| (_CONCAT(JESD216_DW15_QER_VAL_, \ |
| DT_INST_STRING_TOKEN(inst, quad_enable_requirements))), \ |
| ((JESD216_DW15_QER_VAL_NONE))) |
| |
| static inline bool is_within_region(off_t addr, size_t size, off_t region_start, |
| size_t region_size) |
| { |
| return (addr >= region_start && |
| (addr < (region_start + region_size)) && |
| ((addr + size) <= (region_start + region_size))); |
| } |
| |
| static int flash_npcx_uma_transceive(const struct device *dev, struct npcx_uma_cfg *cfg, |
| uint32_t flags) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| struct flash_npcx_nor_data *data = dev->data; |
| int ret; |
| |
| /* Lock SPI bus and configure it if needed */ |
| qspi_npcx_fiu_mutex_lock_configure(config->qspi_bus, &config->qspi_cfg, |
| data->operation); |
| |
| /* Execute UMA transaction */ |
| ret = qspi_npcx_fiu_uma_transceive(config->qspi_bus, cfg, flags); |
| |
| /* Unlock SPI bus */ |
| qspi_npcx_fiu_mutex_unlock(config->qspi_bus); |
| |
| return ret; |
| } |
| |
| /* NPCX UMA functions for SPI NOR flash */ |
| static int flash_npcx_uma_cmd_only(const struct device *dev, uint8_t opcode) |
| { |
| struct npcx_uma_cfg cfg = { .opcode = opcode}; |
| |
| return flash_npcx_uma_transceive(dev, &cfg, 0); /* opcode only */ |
| } |
| |
| static int flash_npcx_uma_cmd_by_addr(const struct device *dev, uint8_t opcode, |
| uint32_t addr) |
| { |
| struct npcx_uma_cfg cfg = { .opcode = opcode}; |
| |
| cfg.addr.u32 = sys_cpu_to_be32(addr); |
| return flash_npcx_uma_transceive(dev, &cfg, NPCX_UMA_ACCESS_ADDR); |
| } |
| |
| static int flash_npcx_uma_read(const struct device *dev, uint8_t opcode, |
| uint8_t *dst, const size_t size) |
| { |
| struct npcx_uma_cfg cfg = { .opcode = opcode, |
| .rx_buf = dst, |
| .rx_count = size}; |
| |
| return flash_npcx_uma_transceive(dev, &cfg, NPCX_UMA_ACCESS_READ); |
| } |
| |
| static int flash_npcx_uma_write(const struct device *dev, uint8_t opcode, |
| uint8_t *src, const size_t size) |
| { |
| struct npcx_uma_cfg cfg = { .opcode = opcode, |
| .tx_buf = src, |
| .tx_count = size}; |
| |
| return flash_npcx_uma_transceive(dev, &cfg, NPCX_UMA_ACCESS_WRITE); |
| } |
| |
| static int flash_npcx_uma_write_by_addr(const struct device *dev, uint8_t opcode, |
| uint8_t *src, const size_t size, uint32_t addr) |
| { |
| struct npcx_uma_cfg cfg = { .opcode = opcode, |
| .tx_buf = src, |
| .tx_count = size}; |
| |
| cfg.addr.u32 = sys_cpu_to_be32(addr); |
| return flash_npcx_uma_transceive(dev, &cfg, NPCX_UMA_ACCESS_WRITE | |
| NPCX_UMA_ACCESS_ADDR); |
| } |
| |
| /* Local SPI NOR flash functions */ |
| static int flash_npcx_nor_wait_until_ready(const struct device *dev) |
| { |
| int ret; |
| uint8_t reg; |
| const struct flash_npcx_nor_config *config = dev->config; |
| int64_t st = k_uptime_get(); |
| |
| do { |
| ret = flash_npcx_uma_read(dev, SPI_NOR_CMD_RDSR, ®, sizeof(reg)); |
| if (ret != 0) { |
| return ret; |
| } else if ((reg & SPI_NOR_WIP_BIT) == 0) { |
| return 0; |
| } |
| |
| } while ((k_uptime_get() - st) < config->max_timeout); |
| |
| return -EBUSY; |
| } |
| |
| static int flash_npcx_nor_read_status_regs(const struct device *dev, uint8_t *sts_reg) |
| { |
| int ret = flash_npcx_uma_read(dev, SPI_NOR_CMD_RDSR, sts_reg, 1); |
| |
| if (ret != 0) { |
| return ret; |
| } |
| return flash_npcx_uma_read(dev, SPI_NOR_CMD_RDSR2, sts_reg + 1, 1); |
| } |
| |
| static int flash_npcx_nor_write_status_regs(const struct device *dev, uint8_t *sts_reg) |
| { |
| int ret; |
| |
| ret = flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = flash_npcx_uma_write(dev, SPI_NOR_CMD_WRSR, sts_reg, 2); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| return flash_npcx_nor_wait_until_ready(dev); |
| } |
| |
| /* Flash API functions */ |
| #if defined(CONFIG_FLASH_JESD216_API) |
| static int flash_npcx_nor_read_jedec_id(const struct device *dev, uint8_t *id) |
| { |
| if (id == NULL) { |
| return -EINVAL; |
| } |
| |
| return flash_npcx_uma_read(dev, SPI_NOR_CMD_RDID, id, SPI_NOR_MAX_ID_LEN); |
| } |
| |
| static int flash_npcx_nor_read_sfdp(const struct device *dev, off_t addr, |
| void *data, size_t size) |
| { |
| uint8_t sfdp_addr[4]; |
| struct npcx_uma_cfg cfg = { .opcode = JESD216_CMD_READ_SFDP, |
| .tx_buf = sfdp_addr, |
| .tx_count = 4, |
| .rx_buf = data, |
| .rx_count = size}; |
| |
| if (data == NULL) { |
| return -EINVAL; |
| } |
| |
| /* CMD_READ_SFDP needs a 24-bit address followed by a dummy byte */ |
| sfdp_addr[0] = (addr >> 16) & 0xff; |
| sfdp_addr[1] = (addr >> 8) & 0xff; |
| sfdp_addr[2] = addr & 0xff; |
| return flash_npcx_uma_transceive(dev, &cfg, NPCX_UMA_ACCESS_WRITE | |
| NPCX_UMA_ACCESS_READ); |
| } |
| #endif /* CONFIG_FLASH_JESD216_API */ |
| |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| static void flash_npcx_nor_pages_layout(const struct device *dev, |
| const struct flash_pages_layout **layout, |
| size_t *layout_size) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| |
| *layout = &config->layout; |
| *layout_size = 1; |
| } |
| #endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
| |
| static int flash_npcx_nor_read(const struct device *dev, off_t addr, |
| void *data, size_t size) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| struct flash_npcx_nor_data *dev_data = dev->data; |
| |
| /* Out of the region of nor flash device? */ |
| if (!is_within_region(addr, size, 0, config->flash_size)) { |
| return -EINVAL; |
| } |
| |
| /* Lock/Unlock SPI bus also for DRA mode */ |
| qspi_npcx_fiu_mutex_lock_configure(config->qspi_bus, &config->qspi_cfg, |
| dev_data->operation); |
| |
| /* Trigger Direct Read Access (DRA) via reading memory mapped-address */ |
| memcpy(data, (void *)(config->mapped_addr + addr), size); |
| |
| qspi_npcx_fiu_mutex_unlock(config->qspi_bus); |
| |
| return 0; |
| } |
| |
| static int flash_npcx_nor_erase(const struct device *dev, off_t addr, size_t size) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| int ret = 0; |
| |
| /* Out of the region of nor flash device? */ |
| if (!is_within_region(addr, size, 0, config->flash_size)) { |
| LOG_ERR("Addr %ld, size %d are out of range", addr, size); |
| return -EINVAL; |
| } |
| |
| /* address must be sector-aligned */ |
| if (!SPI_NOR_IS_SECTOR_ALIGNED(addr)) { |
| LOG_ERR("Addr %ld is not sector-aligned", addr); |
| return -EINVAL; |
| } |
| |
| /* size must be a multiple of sectors */ |
| if ((size % BLOCK_4K_SIZE) != 0) { |
| LOG_ERR("Size %d is not a multiple of sectors", size); |
| return -EINVAL; |
| } |
| |
| /* Select erase opcode by size */ |
| if (size == config->flash_size) { |
| flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); |
| /* Send chip erase command */ |
| flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_CE); |
| return flash_npcx_nor_wait_until_ready(dev); |
| } |
| |
| while (size > 0) { |
| flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); |
| /* Send page/block erase command with addr */ |
| if ((size >= BLOCK_64K_SIZE) && SPI_NOR_IS_64K_ALIGNED(addr)) { |
| flash_npcx_uma_cmd_by_addr(dev, SPI_NOR_CMD_BE, addr); |
| addr += BLOCK_64K_SIZE; |
| size -= BLOCK_64K_SIZE; |
| } else { |
| flash_npcx_uma_cmd_by_addr(dev, SPI_NOR_CMD_SE, addr); |
| addr += BLOCK_4K_SIZE; |
| size -= BLOCK_4K_SIZE; |
| } |
| ret = flash_npcx_nor_wait_until_ready(dev); |
| if (ret != 0) { |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int flash_npcx_nor_write(const struct device *dev, off_t addr, |
| const void *data, size_t size) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| uint8_t *tx_buf = (uint8_t *)data; |
| int ret = 0; |
| size_t sz_write; |
| |
| /* Out of the region of nor flash device? */ |
| if (!is_within_region(addr, size, 0, config->flash_size)) { |
| return -EINVAL; |
| } |
| |
| /* Don't write more than a page. */ |
| if (size > SPI_NOR_PAGE_SIZE) { |
| sz_write = SPI_NOR_PAGE_SIZE; |
| } else { |
| sz_write = size; |
| } |
| |
| /* |
| * Correct the size of first write to not go through page boundary and |
| * make the address of next write to align to page boundary. |
| */ |
| if (((addr + sz_write - 1U) / SPI_NOR_PAGE_SIZE) != (addr / SPI_NOR_PAGE_SIZE)) { |
| sz_write -= (addr + sz_write) & (SPI_NOR_PAGE_SIZE - 1); |
| } |
| |
| while (size > 0) { |
| /* Start to write */ |
| flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); |
| ret = flash_npcx_uma_write_by_addr(dev, SPI_NOR_CMD_PP, tx_buf, |
| sz_write, addr); |
| if (ret != 0) { |
| break; |
| } |
| |
| /* Wait for writing completed */ |
| ret = flash_npcx_nor_wait_until_ready(dev); |
| if (ret != 0) { |
| break; |
| } |
| |
| size -= sz_write; |
| tx_buf += sz_write; |
| addr += sz_write; |
| |
| if (size > SPI_NOR_PAGE_SIZE) { |
| sz_write = SPI_NOR_PAGE_SIZE; |
| } else { |
| sz_write = size; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static const struct flash_parameters * |
| flash_npcx_nor_get_parameters(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return &flash_npcx_parameters; |
| }; |
| |
| #ifdef CONFIG_FLASH_EX_OP_ENABLED |
| static int flash_npcx_nor_ex_exec_uma(const struct device *dev, |
| const struct npcx_ex_ops_uma_in *op_in, |
| const struct npcx_ex_ops_uma_out *op_out) |
| { |
| int flag = 0; |
| struct npcx_uma_cfg cfg; |
| |
| if (op_in == NULL) { |
| return -EINVAL; |
| } |
| |
| /* Organize a UMA transaction */ |
| cfg.opcode = op_in->opcode; |
| if (op_in->tx_count != 0) { |
| cfg.tx_buf = op_in->tx_buf; |
| cfg.tx_count = op_in->tx_count; |
| flag |= NPCX_UMA_ACCESS_WRITE; |
| } |
| |
| if (op_in->addr_count != 0) { |
| cfg.addr.u32 = sys_cpu_to_be32(op_in->addr); |
| flag |= NPCX_UMA_ACCESS_ADDR; |
| } |
| |
| if (op_out != NULL && op_in->rx_count != 0) { |
| cfg.rx_buf = op_out->rx_buf; |
| cfg.rx_count = op_in->rx_count; |
| flag |= NPCX_UMA_ACCESS_READ; |
| } |
| |
| return flash_npcx_uma_transceive(dev, &cfg, flag); |
| } |
| |
| static int flash_npcx_nor_ex_set_spi_spec(const struct device *dev, |
| const struct npcx_ex_ops_qspi_oper_in *op_in) |
| { |
| struct flash_npcx_nor_data *data = dev->data; |
| |
| /* Cannot disable write protection of internal flash */ |
| if ((data->operation & NPCX_EX_OP_INT_FLASH_WP) != 0) { |
| if ((op_in->mask & NPCX_EX_OP_INT_FLASH_WP) != 0 && !op_in->enable) { |
| return -EINVAL; |
| } |
| } |
| |
| if (op_in->enable) { |
| data->operation |= op_in->mask; |
| } else { |
| data->operation &= ~op_in->mask; |
| } |
| |
| return 0; |
| } |
| |
| static int flash_npcx_nor_ex_get_spi_spec(const struct device *dev, |
| struct npcx_ex_ops_qspi_oper_out *op_out) |
| { |
| struct flash_npcx_nor_data *data = dev->data; |
| |
| op_out->oper = data->operation; |
| return 0; |
| } |
| |
| static int flash_npcx_nor_ex_op(const struct device *dev, uint16_t code, |
| const uintptr_t in, void *out) |
| { |
| #ifdef CONFIG_USERSPACE |
| bool syscall_trap = z_syscall_trap(); |
| #endif |
| int ret; |
| |
| switch (code) { |
| case FLASH_NPCX_EX_OP_EXEC_UMA: |
| { |
| struct npcx_ex_ops_uma_in *op_in = (struct npcx_ex_ops_uma_in *)in; |
| struct npcx_ex_ops_uma_out *op_out = (struct npcx_ex_ops_uma_out *)out; |
| #ifdef CONFIG_USERSPACE |
| struct npcx_ex_ops_uma_in in_copy; |
| struct npcx_ex_ops_uma_out out_copy; |
| |
| if (syscall_trap) { |
| Z_OOPS(z_user_from_copy(&in_copy, op_in, sizeof(in_copy))); |
| op_in = &in_copy; |
| op_out = &out_copy; |
| } |
| #endif |
| |
| ret = flash_npcx_nor_ex_exec_uma(dev, op_in, op_out); |
| #ifdef CONFIG_USERSPACE |
| if (ret == 0 && syscall_trap) { |
| Z_OOPS(z_user_to_copy(out, op_out, sizeof(out_copy))); |
| } |
| #endif |
| break; |
| } |
| case FLASH_NPCX_EX_OP_SET_QSPI_OPER: |
| { |
| struct npcx_ex_ops_qspi_oper_in *op_in = (struct npcx_ex_ops_qspi_oper_in *)in; |
| #ifdef CONFIG_USERSPACE |
| struct npcx_ex_ops_qspi_oper_in in_copy; |
| |
| if (syscall_trap) { |
| Z_OOPS(z_user_from_copy(&in_copy, op_in, sizeof(in_copy))); |
| op_in = &in_copy; |
| } |
| #endif |
| ret = flash_npcx_nor_ex_set_spi_spec(dev, op_in); |
| break; |
| } |
| case FLASH_NPCX_EX_OP_GET_QSPI_OPER: |
| { |
| struct npcx_ex_ops_qspi_oper_out *op_out = |
| (struct npcx_ex_ops_qspi_oper_out *)out; |
| #ifdef CONFIG_USERSPACE |
| struct npcx_ex_ops_qspi_oper_out out_copy; |
| |
| if (syscall_trap) { |
| op_out = &out_copy; |
| } |
| #endif |
| ret = flash_npcx_nor_ex_get_spi_spec(dev, op_out); |
| #ifdef CONFIG_USERSPACE |
| if (ret == 0 && syscall_trap) { |
| Z_OOPS(z_user_to_copy(out, op_out, sizeof(out_copy))); |
| } |
| #endif |
| break; |
| } |
| default: |
| ret = -ENOTSUP; |
| break; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static const struct flash_driver_api flash_npcx_nor_driver_api = { |
| .read = flash_npcx_nor_read, |
| .write = flash_npcx_nor_write, |
| .erase = flash_npcx_nor_erase, |
| .get_parameters = flash_npcx_nor_get_parameters, |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| .page_layout = flash_npcx_nor_pages_layout, |
| #endif |
| #if defined(CONFIG_FLASH_JESD216_API) |
| .sfdp_read = flash_npcx_nor_read_sfdp, |
| .read_jedec_id = flash_npcx_nor_read_jedec_id, |
| #endif |
| #ifdef CONFIG_FLASH_EX_OP_ENABLED |
| .ex_op = flash_npcx_nor_ex_op, |
| #endif |
| }; |
| |
| static int flash_npcx_nor_init(const struct device *dev) |
| { |
| const struct flash_npcx_nor_config *config = dev->config; |
| int ret; |
| |
| if (!IS_ENABLED(CONFIG_FLASH_NPCX_FIU_NOR_INIT)) { |
| return 0; |
| } |
| |
| /* Enable quad access of spi NOR flash */ |
| if (config->qspi_cfg.qer_type != JESD216_DW15_QER_NONE) { |
| uint8_t qe_idx, qe_bit, sts_reg[2]; |
| /* Read status registers first */ |
| ret = flash_npcx_nor_read_status_regs(dev, sts_reg); |
| if (ret != 0) { |
| LOG_ERR("Enable quad access: read reg failed %d!", ret); |
| return ret; |
| } |
| switch (config->qspi_cfg.qer_type) { |
| case JESD216_DW15_QER_S1B6: |
| qe_idx = 1; |
| qe_bit = 6; |
| break; |
| case JESD216_DW15_QER_S2B1v1: |
| __fallthrough; |
| case JESD216_DW15_QER_S2B1v4: |
| __fallthrough; |
| case JESD216_DW15_QER_S2B1v5: |
| qe_idx = 2; |
| qe_bit = 1; |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| /* Set QE bit in status register */ |
| sts_reg[qe_idx - 1] |= BIT(qe_bit); |
| ret = flash_npcx_nor_write_status_regs(dev, sts_reg); |
| if (ret != 0) { |
| LOG_ERR("Enable quad access: write reg failed %d!", ret); |
| return ret; |
| } |
| } |
| |
| /* Enable 4-byte address of spi NOR flash */ |
| if (config->qspi_cfg.enter_4ba != 0) { |
| bool wr_en = (config->qspi_cfg.enter_4ba & 0x02) != 0; |
| |
| if (wr_en) { |
| ret = flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); |
| if (ret != 0) { |
| LOG_ERR("Enable 4byte addr: WREN failed %d!", ret); |
| return ret; |
| } |
| } |
| ret = flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_4BA); |
| if (ret != 0) { |
| LOG_ERR("Enable 4byte addr: 4BA failed %d!", ret); |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| #define NPCX_FLASH_NOR_INIT(n) \ |
| BUILD_ASSERT(DT_INST_QUAD_EN_PROP_OR(n) == JESD216_DW15_QER_NONE || \ |
| DT_INST_STRING_TOKEN(n, rd_mode) == NPCX_RD_MODE_FAST_DUAL, \ |
| "Fast Dual IO read must be selected in Quad mode"); \ |
| PINCTRL_DT_INST_DEFINE(n); \ |
| static const struct flash_npcx_nor_config flash_npcx_nor_config_##n = { \ |
| .qspi_bus = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(n))), \ |
| .mapped_addr = DT_INST_PROP(n, mapped_addr), \ |
| .flash_size = DT_INST_PROP(n, size) / 8, \ |
| .max_timeout = DT_INST_PROP(n, max_timeout), \ |
| .qspi_cfg = { \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| .flags = DT_INST_PROP(n, qspi_flags), \ |
| .enter_4ba = DT_INST_PROP_OR(n, enter_4byte_addr, 0), \ |
| .qer_type = DT_INST_QUAD_EN_PROP_OR(n), \ |
| .rd_mode = DT_INST_STRING_TOKEN(n, rd_mode), \ |
| }, \ |
| IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, ( \ |
| .layout = { \ |
| .pages_count = DT_INST_PROP(n, size) / \ |
| (8 * SPI_NOR_PAGE_SIZE), \ |
| .pages_size = SPI_NOR_PAGE_SIZE, \ |
| },)) \ |
| }; \ |
| static struct flash_npcx_nor_data flash_npcx_nor_data_##n; \ |
| DEVICE_DT_INST_DEFINE(n, flash_npcx_nor_init, NULL, \ |
| &flash_npcx_nor_data_##n, &flash_npcx_nor_config_##n, \ |
| POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, \ |
| &flash_npcx_nor_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(NPCX_FLASH_NOR_INIT) |