diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index c34a62804ea..b78ce14e78c 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -38,6 +38,8 @@ zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_SAM flash_sam.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NIOS2_QSPI soc_flash_nios2_qspi.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_GECKO flash_gecko.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_RV32M1 soc_flash_rv32m1.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_QSPI flash_npcx_fiu_qspi.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_NOR flash_npcx_fiu_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_QSPI flash_stm32_qspi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_OSPI flash_stm32_ospi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_MX25UM51345G flash_mcux_flexspi_mx25um51345g.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 84ee3d9769f..2229d520705 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -118,6 +118,8 @@ source "drivers/flash/Kconfig.mcux" source "drivers/flash/Kconfig.nios2_qspi" +source "drivers/flash/Kconfig.npcx_fiu" + source "drivers/flash/Kconfig.gecko" source "drivers/flash/Kconfig.nor" diff --git a/drivers/flash/Kconfig.npcx_fiu b/drivers/flash/Kconfig.npcx_fiu new file mode 100644 index 00000000000..d46d7f2e0dc --- /dev/null +++ b/drivers/flash/Kconfig.npcx_fiu @@ -0,0 +1,34 @@ +# NPCX Flash driver configuration options + +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config FLASH_NPCX_FIU_QSPI + bool "Nuvoton NPCX QSPI Bus Flash driver" + default y + depends on DT_HAS_NUVOTON_NPCX_FIU_QSPI_ENABLED + help + This option enables the QSPI Bus Flash driver for NPCX family of + processors. + +config FLASH_NPCX_FIU_NOR + bool "Nuvoton NPCX embedded controller (EC) QSPI NOR Flash driver" + default y + depends on DT_HAS_NUVOTON_NPCX_FIU_NOR_ENABLED + depends on FLASH_NPCX_FIU_QSPI + select FLASH_HAS_DRIVER_ENABLED + select FLASH_HAS_PAGE_LAYOUT + select FLASH_JESD216 + select FLASH_HAS_EX_OP + help + This option enables the QSPI NOR Flash driver for NPCX family of + processors. + +config FLASH_NPCX_FIU_NOR_INIT + bool "QSPI NOR flash feature during driver initialization" + default y + depends on FLASH_NPCX_FIU_NOR + help + This option enables the QSPI NOR Flash features such as Quad-Enable, + 4-byte address support and so on during driver initialization. Disable + it if QSPI NOR devices are not ready during driver initialization. diff --git a/drivers/flash/flash_npcx_fiu_nor.c b/drivers/flash/flash_npcx_fiu_nor.c new file mode 100644 index 00000000000..85bde757ce5 --- /dev/null +++ b/drivers/flash/flash_npcx_fiu_nor.c @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nuvoton_npcx_fiu_nor + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USERSPACE +#include +#include +#endif + +#include "flash_npcx_fiu_qspi.h" +#include "spi_nor.h" + +#include +LOG_MODULE_REGISTER(flash_npcx_fiu_nor, CONFIG_FLASH_LOG_LEVEL); + +/* 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; + /* Minimum size for flash erase */ + uint32_t min_erase_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; + uint8_t opcode; + + /* 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 % config->min_erase_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); + } else if (config->min_erase_size == KB(4)) { + opcode = SPI_NOR_CMD_SE; + } else if (config->min_erase_size == KB(64)) { + opcode = SPI_NOR_CMD_BE; + } else { + return -EINVAL; + } + + while (size > 0) { + flash_npcx_uma_cmd_only(dev, SPI_NOR_CMD_WREN); + /* Send page/block erase command with addr */ + flash_npcx_uma_cmd_by_addr(dev, opcode, addr); + addr += config->min_erase_size; + size -= config->min_erase_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, \ + .min_erase_size = DT_INST_PROP(n, min_erase_size), \ + .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) diff --git a/drivers/flash/flash_npcx_fiu_qspi.c b/drivers/flash/flash_npcx_fiu_qspi.c new file mode 100644 index 00000000000..8b53be28c86 --- /dev/null +++ b/drivers/flash/flash_npcx_fiu_qspi.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nuvoton_npcx_fiu_qspi + +#include +#include +#include +#include +#include +#include + +#include "flash_npcx_fiu_qspi.h" + +#include +LOG_MODULE_REGISTER(npcx_fiu_qspi, LOG_LEVEL_ERR); + +/* Driver convenience defines */ +#define HAL_INSTANCE(dev) \ + ((struct fiu_reg *)((const struct npcx_qspi_fiu_config *)(dev)->config)->base) + +/* Device config */ +struct npcx_qspi_fiu_config { + /* Flash interface unit base address */ + uintptr_t base; + /* Clock configuration */ + struct npcx_clk_cfg clk_cfg; + /* Enable 2 external SPI devices for direct read on QSPI bus */ + bool en_direct_access_2dev; +}; + +/* Device data */ +struct npcx_qspi_fiu_data { + /* mutex of qspi bus controller */ + struct k_sem lock_sem; + /* Current device configuration on QSPI bus */ + const struct npcx_qspi_cfg *cur_cfg; + /* Current Software controlled Chip-Select number */ + int sw_cs; + /* Current QSPI bus operation */ + uint32_t operation; +}; + +/* NPCX SPI User Mode Access (UMA) functions */ +static inline void qspi_npcx_uma_cs_level(const struct device *dev, uint8_t sw_cs, bool level) +{ + struct fiu_reg *const inst = HAL_INSTANCE(dev); + + /* Set chip select to high/low level */ + if (level) { + inst->UMA_ECTS |= BIT(sw_cs); + } else { + inst->UMA_ECTS &= ~BIT(sw_cs); + } +} + +static inline void qspi_npcx_uma_write_byte(const struct device *dev, uint8_t data) +{ + struct fiu_reg *const inst = HAL_INSTANCE(dev); + + /* Set data to UMA_CODE and trigger UMA */ + inst->UMA_CODE = data; + inst->UMA_CTS = UMA_CODE_CMD_WR_ONLY; + /* EXEC_DONE will be zero automatically if a UMA transaction is completed. */ + while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) { + continue; + } +} + +static inline void qspi_npcx_uma_read_byte(const struct device *dev, uint8_t *data) +{ + struct fiu_reg *const inst = HAL_INSTANCE(dev); + + /* Trigger UMA and Get data from DB0 later */ + inst->UMA_CTS = UMA_CODE_RD_BYTE(1); + while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) { + continue; + } + + *data = inst->UMA_DB0; +} + +/* NPCX SPI Direct Read Access (DRA)/User Mode Access (UMA) configuration functions */ +static inline void qspi_npcx_config_uma_mode(const struct device *dev, + const struct npcx_qspi_cfg *qspi_cfg) +{ + struct fiu_reg *const inst = HAL_INSTANCE(dev); + + if ((qspi_cfg->flags & NPCX_QSPI_SEC_FLASH_SL) != 0) { + inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_SEC_CS); + } else { + inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_SEC_CS); + } +} + +static inline void qspi_npcx_config_dra_mode(const struct device *dev, + const struct npcx_qspi_cfg *qspi_cfg) +{ + struct fiu_reg *const inst = HAL_INSTANCE(dev); + + /* Enable quad mode of Direct Read Mode if needed */ + if (qspi_cfg->qer_type != JESD216_DW15_QER_NONE) { + inst->RESP_CFG |= BIT(NPCX_RESP_CFG_QUAD_EN); + } else { + inst->RESP_CFG &= ~BIT(NPCX_RESP_CFG_QUAD_EN); + } + + /* Selects the SPI read access type of Direct Read Access mode */ + SET_FIELD(inst->SPI_FL_CFG, NPCX_SPI_FL_CFG_RD_MODE, qspi_cfg->rd_mode); + + /* Enable/Disable 4 byte address mode for Direct Read Access (DRA) */ +#if !defined(CONFIG_SOC_SERIES_NPCX7) /* NPCX7 doesn't support this feature */ + if (qspi_cfg->enter_4ba != 0) { + if ((qspi_cfg->flags & NPCX_QSPI_SEC_FLASH_SL) != 0) { + inst->SPI1_DEV |= BIT(NPCX_SPI1_DEV_FOUR_BADDR_CS11); + } else { + inst->SPI1_DEV |= BIT(NPCX_SPI1_DEV_FOUR_BADDR_CS10); + } + } else { + inst->SPI1_DEV &= ~(BIT(NPCX_SPI1_DEV_FOUR_BADDR_CS11) | + BIT(NPCX_SPI1_DEV_FOUR_BADDR_CS10)); + } +#endif /* CONFIG_SOC_SERIES_NPCX7 */ +} + +static inline void qspi_npcx_fiu_set_operation(const struct device *dev, uint32_t operation) +{ + if ((operation & NPCX_EX_OP_INT_FLASH_WP) != 0) { + npcx_pinctrl_flash_write_protect_set(); + } +} + +/* NPCX specific QSPI-FIU controller functions */ +int qspi_npcx_fiu_uma_transceive(const struct device *dev, struct npcx_uma_cfg *cfg, + uint32_t flags) +{ + struct npcx_qspi_fiu_data *const data = dev->data; + + /* UMA transaction is permitted? */ + if ((data->operation & NPCX_EX_OP_LOCK_UMA) != 0) { + return -EPERM; + } + + /* Assert chip select */ + qspi_npcx_uma_cs_level(dev, data->sw_cs, false); + + /* Transmit op-code first */ + qspi_npcx_uma_write_byte(dev, cfg->opcode); + + if ((flags & NPCX_UMA_ACCESS_ADDR) != 0) { + /* 3-byte or 4-byte address? */ + const int addr_start = (data->cur_cfg->enter_4ba != 0) ? 0 : 1; + + for (size_t i = addr_start; i < 4; i++) { + LOG_DBG("addr %d, %02x", i, cfg->addr.u8[i]); + qspi_npcx_uma_write_byte(dev, cfg->addr.u8[i]); + } + } + + if ((flags & NPCX_UMA_ACCESS_WRITE) != 0) { + if (cfg->tx_buf == NULL) { + return -EINVAL; + } + for (size_t i = 0; i < cfg->tx_count; i++) { + qspi_npcx_uma_write_byte(dev, cfg->tx_buf[i]); + } + } + + if ((flags & NPCX_UMA_ACCESS_READ) != 0) { + if (cfg->rx_buf == NULL) { + return -EINVAL; + } + for (size_t i = 0; i < cfg->rx_count; i++) { + qspi_npcx_uma_read_byte(dev, cfg->rx_buf + i); + } + } + + /* De-assert chip select */ + qspi_npcx_uma_cs_level(dev, data->sw_cs, true); + + return 0; +} + +void qspi_npcx_fiu_mutex_lock_configure(const struct device *dev, + const struct npcx_qspi_cfg *cfg, + const uint32_t operation) +{ + struct npcx_qspi_fiu_data *const data = dev->data; + + k_sem_take(&data->lock_sem, K_FOREVER); + + /* If the current device is different from previous one, configure it */ + if (data->cur_cfg != cfg) { + data->cur_cfg = cfg; + + /* Apply pin-muxing and tri-state */ + pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + + /* Configure User Mode Access (UMA) settings */ + qspi_npcx_config_uma_mode(dev, cfg); + + /* Configure for Direct Read Access (DRA) settings */ + qspi_npcx_config_dra_mode(dev, cfg); + + /* Save SW CS bit used in UMA mode */ + data->sw_cs = find_lsb_set(cfg->flags & NPCX_QSPI_SW_CS_MASK) - 1; + } + + /* Set QSPI bus operation */ + if (data->operation != operation) { + qspi_npcx_fiu_set_operation(dev, operation); + data->operation = operation; + } +} + +void qspi_npcx_fiu_mutex_unlock(const struct device *dev) +{ + struct npcx_qspi_fiu_data *const data = dev->data; + + k_sem_give(&data->lock_sem); +} + +static int qspi_npcx_fiu_init(const struct device *dev) +{ + const struct npcx_qspi_fiu_config *const config = dev->config; + struct fiu_reg *const inst = HAL_INSTANCE(dev); + struct npcx_qspi_fiu_data *const data = dev->data; + const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE); + int ret; + + if (!device_is_ready(clk_dev)) { + LOG_ERR("%s device not ready", clk_dev->name); + return -ENODEV; + } + + /* Turn on device clock first and get source clock freq. */ + ret = clock_control_on(clk_dev, + (clock_control_subsys_t)&config->clk_cfg); + if (ret < 0) { + LOG_ERR("Turn on FIU clock fail %d", ret); + return ret; + } + + /* initialize mutex for qspi controller */ + k_sem_init(&data->lock_sem, 1, 1); + + /* Enable direct access for 2 external SPI devices */ + if (config->en_direct_access_2dev) { + if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX9)) { + inst->FIU_EXT_CFG |= BIT(NPCX_FIU_EXT_CFG_SPI1_2DEV); + } + } + + return 0; +} + +#define NPCX_SPI_FIU_INIT(n) \ +static const struct npcx_qspi_fiu_config npcx_qspi_fiu_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .clk_cfg = NPCX_DT_CLK_CFG_ITEM(n), \ + .en_direct_access_2dev = DT_INST_PROP(n, en_direct_access_2dev), \ +}; \ +static struct npcx_qspi_fiu_data npcx_qspi_fiu_data_##n; \ +DEVICE_DT_INST_DEFINE(n, qspi_npcx_fiu_init, NULL, \ + &npcx_qspi_fiu_data_##n, &npcx_qspi_fiu_config_##n, \ + PRE_KERNEL_1, CONFIG_FLASH_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(NPCX_SPI_FIU_INIT) diff --git a/drivers/flash/flash_npcx_fiu_qspi.h b/drivers/flash/flash_npcx_fiu_qspi.h new file mode 100644 index 00000000000..092c4371e65 --- /dev/null +++ b/drivers/flash/flash_npcx_fiu_qspi.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FLASH_NPCX_FIU_QSPI_H_ +#define ZEPHYR_DRIVERS_FLASH_NPCX_FIU_QSPI_H_ + +#include +#include "jesd216.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* UMA operation flags */ +#define NPCX_UMA_ACCESS_WRITE BIT(0) +#define NPCX_UMA_ACCESS_READ BIT(1) +#define NPCX_UMA_ACCESS_ADDR BIT(2) + +/* UMA operation configuration for a SPI device */ +struct npcx_uma_cfg { + uint8_t opcode; + uint8_t *tx_buf; + size_t tx_count; + uint8_t *rx_buf; + size_t rx_count; + union { + uint32_t u32; + uint8_t u8[4]; + } addr; +}; + +/* QSPI bus configuration for a SPI device */ +struct npcx_qspi_cfg { + /* Type of Quad Enable bit in status register */ + enum jesd216_dw15_qer_type qer_type; + /* Pinctrl for QSPI bus */ + const struct pinctrl_dev_config *pcfg; + /* Enter four bytes address mode value */ + uint8_t enter_4ba; + /* SPI read access type of Direct Read Access mode */ + uint8_t rd_mode; + /* Configurations for the Quad-SPI peripherals */ + int flags; +}; + +/** + * @brief Execute UMA transactions on qspi bus + * + * @param dev Pointer to the device structure for qspi bus controller instance. + * @param cfg Pointer to the configuration of UMA transactions. + * @param flags Flags to be used during transactions. + * @retval 0 on success, -EPERM if an UMA transaction is not permitted. + */ +int qspi_npcx_fiu_uma_transceive(const struct device *dev, struct npcx_uma_cfg *cfg, + uint32_t flags); + +/** + * @brief Lock the mutex of npcx qspi bus controller and apply its configuration + * + * @param dev Pointer to the device structure for qspi bus controller instance. + * @param cfg Pointer to the configuration for the device on qspi bus. + * @param operation Qspi bus operation for the device. + */ +void qspi_npcx_fiu_mutex_lock_configure(const struct device *dev, + const struct npcx_qspi_cfg *cfg, + const uint32_t operation); + +/** + * @brief Unlock the mutex of npcx qspi bus controller. + * + * @param dev Pointer to the device structure for qspi bus controller instance. + */ +void qspi_npcx_fiu_mutex_unlock(const struct device *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_FLASH_NPCX_FIU_QSPI_H_ */ diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index a544b4e7b68..91d70e8cbef 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -27,7 +27,6 @@ zephyr_library_sources_ifdef(CONFIG_SPI_XLNX_AXI_QUADSPI spi_xlnx_axi_quadspi.c) zephyr_library_sources_ifdef(CONFIG_ESP32_SPIM spi_esp32_spim.c) zephyr_library_sources_ifdef(CONFIG_SPI_TEST spi_test.c) zephyr_library_sources_ifdef(CONFIG_SPI_PSOC6 spi_psoc6.c) -zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_FIU spi_npcx_fiu.c) zephyr_library_sources_ifdef(CONFIG_SPI_BITBANG spi_bitbang.c) zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI_LDMA spi_xec_qmspi_ldma.c) zephyr_library_sources_ifdef(CONFIG_SPI_GD32 spi_gd32.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0dea58b241a..03d9afba1fe 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -107,8 +107,6 @@ source "drivers/spi/Kconfig.test" source "drivers/spi/Kconfig.psoc6" -source "drivers/spi/Kconfig.npcx_fiu" - source "drivers/spi/Kconfig.bitbang" source "drivers/spi/Kconfig.gd32" diff --git a/drivers/spi/Kconfig.npcx_fiu b/drivers/spi/Kconfig.npcx_fiu deleted file mode 100644 index a37b8841e18..00000000000 --- a/drivers/spi/Kconfig.npcx_fiu +++ /dev/null @@ -1,12 +0,0 @@ -# NPCX SPI Driver configuration options - -# Copyright (c) 2021 Nuvoton Technology Corporation. -# SPDX-License-Identifier: Apache-2.0 - -config SPI_NPCX_FIU - bool "Nuvoton NPCX embedded controller (EC) SPI driver for NOR flash" - default y - depends on DT_HAS_NUVOTON_NPCX_SPI_FIU_ENABLED - help - Enable the SPI driver for NPCX family of processors. This driver is - for the dedicated SPI controller (FIU) to access the NOR flash. diff --git a/drivers/spi/spi_npcx_fiu.c b/drivers/spi/spi_npcx_fiu.c deleted file mode 100644 index 08938cc4561..00000000000 --- a/drivers/spi/spi_npcx_fiu.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2021 Nuvoton Technology Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT nuvoton_npcx_spi_fiu - -#include -#include -#include -#include - -LOG_MODULE_REGISTER(spi_npcx_fiu, LOG_LEVEL_ERR); - -#include "spi_context.h" - -/* Device config */ -struct npcx_spi_fiu_config { - /* flash interface unit base address */ - uintptr_t base; - /* clock configuration */ - struct npcx_clk_cfg clk_cfg; -}; - -/* Device data */ -struct npcx_spi_fiu_data { - struct spi_context ctx; -}; - -/* Driver convenience defines */ -#define HAL_INSTANCE(dev) \ - ((struct fiu_reg *)((const struct npcx_spi_fiu_config *)(dev)->config)->base) - -static inline void spi_npcx_fiu_cs_level(const struct device *dev, int level) -{ - struct fiu_reg *const inst = HAL_INSTANCE(dev); - - /* Set chip select to high/low level */ - if (level == 0) - inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_SW_CS1); - else - inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_SW_CS1); -} - -static inline void spi_npcx_fiu_exec_cmd(const struct device *dev, uint8_t code, - uint8_t cts) -{ - struct fiu_reg *const inst = HAL_INSTANCE(dev); - -#ifdef CONFIG_ASSERT - struct npcx_spi_fiu_data *data = dev->data; - struct spi_context *ctx = &data->ctx; - - /* Flash mutex must be held while executing UMA commands */ - __ASSERT((k_sem_count_get(&ctx->lock) == 0), "UMA is not locked"); -#endif - - /* set UMA_CODE */ - inst->UMA_CODE = code; - /* execute UMA flash transaction */ - inst->UMA_CTS = cts; - while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) - continue; -} - -static int spi_npcx_fiu_transceive(const struct device *dev, - const struct spi_config *spi_cfg, - const struct spi_buf_set *tx_bufs, - const struct spi_buf_set *rx_bufs) -{ - struct npcx_spi_fiu_data *data = dev->data; - struct fiu_reg *const inst = HAL_INSTANCE(dev); - struct spi_context *ctx = &data->ctx; - size_t cur_xfer_len; - int error = 0; - - spi_context_lock(ctx, false, NULL, NULL, spi_cfg); - ctx->config = spi_cfg; - - /* - * Configure UMA lock/unlock only if tx buffer set and rx buffer set - * are both empty. - */ - if (tx_bufs == NULL && rx_bufs == NULL) { - if (spi_cfg->operation & SPI_LOCK_ON) - inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_UMA_LOCK); - else - inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_UMA_LOCK); - spi_context_unlock_unconditionally(ctx); - return 0; - } - - /* Assert chip assert */ - spi_npcx_fiu_cs_level(dev, 0); - spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1); - if (rx_bufs == NULL) { - while (spi_context_tx_buf_on(ctx)) { - spi_npcx_fiu_exec_cmd(dev, *ctx->tx_buf, - UMA_CODE_CMD_WR_ONLY); - spi_context_update_tx(ctx, 1, 1); - } - } else { - cur_xfer_len = spi_context_longest_current_buf(ctx); - for (size_t i = 0; i < cur_xfer_len; i++) { - spi_npcx_fiu_exec_cmd(dev, *ctx->tx_buf, - UMA_CODE_CMD_WR_ONLY); - spi_context_update_tx(ctx, 1, 1); - spi_context_update_rx(ctx, 1, 1); - } - while (spi_context_rx_buf_on(ctx)) { - inst->UMA_CTS = UMA_CODE_RD_BYTE(1); - while (IS_BIT_SET(inst->UMA_CTS, - NPCX_UMA_CTS_EXEC_DONE)) - continue; - /* Get read transaction results */ - *ctx->rx_buf = inst->UMA_DB0; - spi_context_update_tx(ctx, 1, 1); - spi_context_update_rx(ctx, 1, 1); - } - } - spi_npcx_fiu_cs_level(dev, 1); - spi_context_release(ctx, error); - - return error; -} - -int spi_npcx_fiu_release(const struct device *dev, - const struct spi_config *config) -{ - struct npcx_spi_fiu_data *data = dev->data; - struct spi_context *ctx = &data->ctx; - - if (!spi_context_configured(ctx, config)) { - return -EINVAL; - } - - spi_context_unlock_unconditionally(ctx); - return 0; -} - -static int spi_npcx_fiu_init(const struct device *dev) -{ - const struct npcx_spi_fiu_config *const config = dev->config; - const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE); - int ret; - - if (!device_is_ready(clk_dev)) { - LOG_ERR("%s device not ready", clk_dev->name); - return -ENODEV; - } - - /* Turn on device clock first and get source clock freq. */ - ret = clock_control_on(clk_dev, - (clock_control_subsys_t)&config->clk_cfg); - if (ret < 0) { - LOG_ERR("Turn on FIU clock fail %d", ret); - return ret; - } - - /* Make sure the context is unlocked */ - spi_context_unlock_unconditionally(&((struct npcx_spi_fiu_data *)dev->data)->ctx); - - return 0; -} - -static struct spi_driver_api spi_npcx_fiu_api = { - .transceive = spi_npcx_fiu_transceive, - .release = spi_npcx_fiu_release, -}; - -static const struct npcx_spi_fiu_config npcx_spi_fiu_config = { - .base = DT_INST_REG_ADDR(0), - .clk_cfg = NPCX_DT_CLK_CFG_ITEM(0), -}; - -static struct npcx_spi_fiu_data npcx_spi_fiu_data = { - SPI_CONTEXT_INIT_LOCK(npcx_spi_fiu_data, ctx), -}; - -DEVICE_DT_INST_DEFINE(0, &spi_npcx_fiu_init, NULL, &npcx_spi_fiu_data, - &npcx_spi_fiu_config, POST_KERNEL, - CONFIG_SPI_INIT_PRIORITY, &spi_npcx_fiu_api); diff --git a/dts/arm/nuvoton/npcx/npcx.dtsi b/dts/arm/nuvoton/npcx/npcx.dtsi index 78f0b84ba5b..3994920af39 100644 --- a/dts/arm/nuvoton/npcx/npcx.dtsi +++ b/dts/arm/nuvoton/npcx/npcx.dtsi @@ -9,6 +9,7 @@ /* Macros for device tree declarations of npcx soc family */ #include #include +#include #include #include #include @@ -510,9 +511,9 @@ }; }; - /* Dedicated SPI interface to access SPI flashes */ - spi_fiu0: spi@40020000 { - compatible = "nuvoton,npcx-spi-fiu"; + /* Dedicated Quad-SPI interface to access SPI flashes */ + qspi_fiu0: quadspi@40020000 { + compatible = "nuvoton,npcx-fiu-qspi"; #address-cells = <1>; #size-cells = <0>; reg = <0x40020000 0x2000>; diff --git a/dts/arm/nuvoton/npcx7m6fb.dtsi b/dts/arm/nuvoton/npcx7m6fb.dtsi index c4bd84bf531..171da95e374 100644 --- a/dts/arm/nuvoton/npcx7m6fb.dtsi +++ b/dts/arm/nuvoton/npcx7m6fb.dtsi @@ -32,14 +32,19 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { + status = "okay"; + int_flash: w25q80@0 { - compatible ="jedec,spi-nor"; - /* 8388608 bits = 1 Mbytes */ - size = <0x800000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 14]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/arm/nuvoton/npcx7m6fc.dtsi b/dts/arm/nuvoton/npcx7m6fc.dtsi index 531b6da6329..b5b6dfa7c3e 100644 --- a/dts/arm/nuvoton/npcx7m6fc.dtsi +++ b/dts/arm/nuvoton/npcx7m6fc.dtsi @@ -32,14 +32,19 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { + status = "okay"; + int_flash: w25q40@0 { - compatible ="jedec,spi-nor"; - /* 4194304 bits = 512K Bytes */ - size = <0x400000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 13]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/arm/nuvoton/npcx7m7fc.dtsi b/dts/arm/nuvoton/npcx7m7fc.dtsi index 31670039d69..240fe7ae728 100644 --- a/dts/arm/nuvoton/npcx7m7fc.dtsi +++ b/dts/arm/nuvoton/npcx7m7fc.dtsi @@ -36,14 +36,19 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { + status = "okay"; + int_flash: w25q40@0 { - compatible ="jedec,spi-nor"; - /* 4194304 bits = 512K Bytes */ - size = <0x400000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 13]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/arm/nuvoton/npcx9m3f.dtsi b/dts/arm/nuvoton/npcx9m3f.dtsi index 4f32f2f0632..4feb0568fa0 100644 --- a/dts/arm/nuvoton/npcx9m3f.dtsi +++ b/dts/arm/nuvoton/npcx9m3f.dtsi @@ -26,14 +26,19 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { + status = "okay"; + int_flash: w25q40@0 { - compatible ="jedec,spi-nor"; - /* 4194304 bits = 512K Bytes */ - size = <0x400000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 13]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/arm/nuvoton/npcx9m6f.dtsi b/dts/arm/nuvoton/npcx9m6f.dtsi index a918784f46a..76cf19c2ca5 100644 --- a/dts/arm/nuvoton/npcx9m6f.dtsi +++ b/dts/arm/nuvoton/npcx9m6f.dtsi @@ -26,14 +26,19 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { + status = "okay"; + int_flash: w25q40@0 { - compatible ="jedec,spi-nor"; - /* 4194304 bits = 512K Bytes */ - size = <0x400000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 13]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/arm/nuvoton/npcx9m7f.dtsi b/dts/arm/nuvoton/npcx9m7f.dtsi index 646225431ce..572daeca9ba 100644 --- a/dts/arm/nuvoton/npcx9m7f.dtsi +++ b/dts/arm/nuvoton/npcx9m7f.dtsi @@ -26,14 +26,17 @@ }; }; -&spi_fiu0 { +&qspi_fiu0 { int_flash: w25q80@0 { - compatible ="jedec,spi-nor"; - /* 8388608 bits = 1 Mbytes */ - size = <0x800000>; + compatible ="nuvoton,npcx-fiu-nor"; + size = ; reg = <0>; - spi-max-frequency = <50000000>; status = "okay"; - jedec-id = [ef 40 14]; + + /* quad spi bus configuration of nor flash device */ + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; }; }; diff --git a/dts/bindings/flash_controller/nuvoton,npcx-fiu-nor.yaml b/dts/bindings/flash_controller/nuvoton,npcx-fiu-nor.yaml new file mode 100644 index 00000000000..10955f8709c --- /dev/null +++ b/dts/bindings/flash_controller/nuvoton,npcx-fiu-nor.yaml @@ -0,0 +1,56 @@ +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +description: | + The SPI NOR flash devices accessed by Nuvoton Flash Interface Unit (FIU). + + Representation of a SPI NOR flash on a qspi bus looks like: + + int_flash: w25q40@0 { + compatible ="nuvoton,npcx-fiu-nor"; + size = ; + reg = <0>; + + qspi-flags = ; + mapped-addr = <0x64000000>; + pinctrl-0 = <&int_flash_sl>; + pinctrl-names = "default"; + }; + +compatible: "nuvoton,npcx-fiu-nor" + +include: [flash-controller.yaml, pinctrl-device.yaml, "jedec,spi-nor-common.yaml"] + +on-bus: qspi + +properties: + mapped-addr: + type: int + required: true + description: Mapped memory address of direct read access for spi nor flash. + min-erase-size: + type: int + default: 0x10000 + description: Minimum erase size of spi nor flash. + enum: + - 0x1000 # 4KB (Sector Erase) + - 0x10000 # 64KB (Block Erase) + max-timeout: + type: int + default: 10000 + description: Typically, it equals to max timeout of chip erase in ms. + qspi-flags: + type: int + required: true + description: The definitions for configuring the Quad-SPI peripherals. + rd-mode: + type: string + default: "NPCX_RD_MODE_FAST_DUAL" + description: | + Selects the SPI read access type of Direct Read Access. Usually, we choose + Fast Read Dual I/O mode for better performance. If the nor spi flash does + not support this mode, please set this property explicitly. + enum: + - "NPCX_RD_MODE_NORMAL" # Direct read access by command code 03h + - "NPCX_RD_MODE_FAST" # Direct read access by command code 0bh + - "NPCX_RD_MODE_FAST_DUAL" # Direct read access by command code bbh diff --git a/dts/bindings/flash_controller/nuvoton,npcx-fiu-qspi.yaml b/dts/bindings/flash_controller/nuvoton,npcx-fiu-qspi.yaml new file mode 100644 index 00000000000..3e8627faf23 --- /dev/null +++ b/dts/bindings/flash_controller/nuvoton,npcx-fiu-qspi.yaml @@ -0,0 +1,40 @@ +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +description: | + Properties defining the NPCX Quad-SPI peripheral of Flash Interface Unit (FIU). + A npcx quad-spi dt node would typically looks like: + + &qspi_fiu0 { + status = "okay"; + + int_flash: w25q400@0 { + status = "okay"; + reg = <0>; + ... + }; + + ext_flash: w25q256@1 { + status = "okay"; + reg = <1>; + ... + }; + }; + + `int_flash` and `ext_flash` are the devices accessed by this peripheral. + +compatible: "nuvoton,npcx-fiu-qspi" + +include: [base.yaml, pinctrl-device.yaml] + +bus: qspi + +properties: + reg: + required: true + clocks: + required: true + en-direct-access-2dev: + type: boolean + description: | + Two external SPI devices are supported for Direct Read Access (DRA) on QSPI bus. diff --git a/dts/bindings/spi/nuvoton,npcx-spi-fiu.yaml b/dts/bindings/spi/nuvoton,npcx-spi-fiu.yaml deleted file mode 100644 index 6f3b900e435..00000000000 --- a/dts/bindings/spi/nuvoton,npcx-spi-fiu.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2021 Nuvoton Technology Corporation. -# SPDX-License-Identifier: Apache-2.0 - -description: Nuvoton, NPCX-SPI-FIU controller node - -compatible: "nuvoton,npcx-spi-fiu" - -include: [spi-controller.yaml] - -properties: - reg: - required: true - clocks: - required: true diff --git a/include/zephyr/drivers/flash/npcx_flash_api_ex.h b/include/zephyr/drivers/flash/npcx_flash_api_ex.h new file mode 100644 index 00000000000..c640a28648b --- /dev/null +++ b/include/zephyr/drivers/flash/npcx_flash_api_ex.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ZEPHYR_INCLUDE_DRIVERS_NPCX_FLASH_API_EX_H__ +#define __ZEPHYR_INCLUDE_DRIVERS_NPCX_FLASH_API_EX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum flash_npcx_ex_ops { + /* + * NPCX User Mode Access (UMA) mode execution. + * + * Execute a SPI transaction via User Mode Access (UMA) mode. Users can + * perform a customized SPI transaction to gread or write the device's + * configuration such as status registers of nor flash, power on/off, + * and so on. + */ + FLASH_NPCX_EX_OP_EXEC_UMA = FLASH_EX_OP_VENDOR_BASE, + /* + * NPCX Configure specific operation for Quad-SPI nor flash. + * + * It configures specific operation for Quad-SPI nor flash such as lock + * or unlock UMA mode, set write protection pin of internal flash, and + * so on. + */ + FLASH_NPCX_EX_OP_SET_QSPI_OPER, + /* + * NPCX Get specific operation for Quad-SPI nor flash. + * + * It returns current specific operation for Quad-SPI nor flash. + */ + FLASH_NPCX_EX_OP_GET_QSPI_OPER, +}; + +/* Structures used by FLASH_NPCX_EX_OP_EXEC_UMA */ +struct npcx_ex_ops_uma_in { + uint8_t opcode; + uint8_t *tx_buf; + size_t tx_count; + uint32_t addr; + size_t addr_count; + size_t rx_count; +}; + +struct npcx_ex_ops_uma_out { + uint8_t *rx_buf; +}; + +/* Structures used by FLASH_NPCX_EX_OP_SET_QSPI_OPER */ +struct npcx_ex_ops_qspi_oper_in { + bool enable; + uint32_t mask; +}; + +/* Structures used by FLASH_NPCX_EX_OP_GET_QSPI_OPER */ +struct npcx_ex_ops_qspi_oper_out { + uint32_t oper; +}; + +/* Specific NPCX QSPI devices control bits */ +#define NPCX_EX_OP_LOCK_UMA BIT(0) /* Lock/Unlock UMA mode */ +#define NPCX_EX_OP_INT_FLASH_WP BIT(1) /* Issue write protection of internal flash */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ZEPHYR_INCLUDE_DRIVERS_NPCX_FLASH_API_EX_H__ */ diff --git a/include/zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h b/include/zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h new file mode 100644 index 00000000000..c214218f523 --- /dev/null +++ b/include/zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NPCK_FIU_QSPI_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NPCK_FIU_QSPI_H_ + +#include + +/* Software controlled Chip-Select number for UMA transactions */ +#define NPCX_QSPI_SW_CS0 BIT(0) +#define NPCX_QSPI_SW_CS1 BIT(1) +#define NPCX_QSPI_SW_CS2 BIT(2) +#define NPCX_QSPI_SW_CS_MASK (NPCX_QSPI_SW_CS0 | NPCX_QSPI_SW_CS1 | NPCX_QSPI_SW_CS2) + +/* Supported flash interfaces for UMA transactions */ +#define NPCX_QSPI_SEC_FLASH_SL BIT(4) + +/* Supported read mode for Direct Read Access */ +#define NPCX_RD_MODE_NORMAL 0 +#define NPCX_RD_MODE_FAST 1 +#define NPCX_RD_MODE_FAST_DUAL 3 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NPCK_FIU_QSPI_H_ */ diff --git a/soc/arm/nuvoton_npcx/common/reg/reg_def.h b/soc/arm/nuvoton_npcx/common/reg/reg_def.h index 4bed431a457..283f9bd200a 100644 --- a/soc/arm/nuvoton_npcx/common/reg/reg_def.h +++ b/soc/arm/nuvoton_npcx/common/reg/reg_def.h @@ -1516,11 +1516,23 @@ struct fiu_reg { volatile uint8_t FIU_DMM_CYC; /* 0x033: FIU Extended Configuration */ volatile uint8_t FIU_EXT_CFG; +#if defined(CONFIG_SOC_SERIES_NPCX9) + /* 0x034: UMA address byte 0-3 */ + volatile uint32_t UMA_AB0_3; + /* 0x038-0x3C */ + volatile uint8_t reserved8[5]; + /* 0x03D: SPI Device */ + volatile uint8_t SPI1_DEV; + /* 0x03E-0x3F */ + volatile uint8_t reserved9[2]; +#endif }; /* FIU register fields */ #define NPCX_RESP_CFG_IAD_EN 0 #define NPCX_RESP_CFG_DEV_SIZE_EX 2 +#define NPCX_RESP_CFG_QUAD_EN 3 +#define NPCX_SPI_FL_CFG_RD_MODE FIELD(6, 2) #define NPCX_UMA_CTS_A_SIZE 3 #define NPCX_UMA_CTS_C_SIZE 4 #define NPCX_UMA_CTS_RD_WR 5 @@ -1530,6 +1542,12 @@ struct fiu_reg { #define NPCX_UMA_ECTS_SW_CS1 1 #define NPCX_UMA_ECTS_SEC_CS 2 #define NPCX_UMA_ECTS_UMA_LOCK 3 +#define NPCX_SPI1_DEV_FOUR_BADDR_CS10 6 +#define NPCX_SPI1_DEV_FOUR_BADDR_CS11 7 +#define NPCX_SPI1_DEV_SPI1_LO_DEV_SIZE FIELD(0, 4) +#define NPCX_FIU_EXT_CFG_SPI1_2DEV 7 +#define NPCX_FIU_EXT_CFG_SET_DMM_EN 2 +#define NPCX_FIU_EXT_CFG_SET_CMD_EN 1 /* UMA fields selections */ #define UMA_FLD_ADDR BIT(NPCX_UMA_CTS_A_SIZE) /* 3-bytes ADR field */ diff --git a/soc/arm/nuvoton_npcx/common/registers.c b/soc/arm/nuvoton_npcx/common/registers.c index f1e112190ae..453fffbe24a 100644 --- a/soc/arm/nuvoton_npcx/common/registers.c +++ b/soc/arm/nuvoton_npcx/common/registers.c @@ -162,7 +162,11 @@ NPCX_REG_OFFSET_CHECK(ps2_reg, PSISIG, 0x008); NPCX_REG_OFFSET_CHECK(ps2_reg, PSIEN, 0x00a); /* FIU register structure check */ +#if defined(CONFIG_SOC_SERIES_NPCX9) +NPCX_REG_SIZE_CHECK(fiu_reg, 0x040); +#else NPCX_REG_SIZE_CHECK(fiu_reg, 0x034); +#endif NPCX_REG_OFFSET_CHECK(fiu_reg, BURST_CFG, 0x001); NPCX_REG_OFFSET_CHECK(fiu_reg, SPI_FL_CFG, 0x014); NPCX_REG_OFFSET_CHECK(fiu_reg, UMA_CTS, 0x01e);