driver: flash: npcx: introduce npcx flash driver

This CL attempts to implement npcx's flash driver instead of the
original one (npcx spi driver plus spi_nor flash driver).

Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
This commit is contained in:
Mulin Chao 2023-06-19 18:44:57 -07:00 committed by Carles Cufí
commit f34fff91bc
24 changed files with 1302 additions and 251 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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.

View file

@ -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 <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);
/* 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, &reg, 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)

View file

@ -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 <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/flash/npcx_flash_api_ex.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h>
#include <soc.h>
#include "flash_npcx_fiu_qspi.h"
#include <zephyr/logging/log.h>
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)

View file

@ -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 <zephyr/device.h>
#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_ */

View file

@ -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)

View file

@ -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"

View file

@ -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.

View file

@ -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 <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>
#include <soc.h>
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);

View file

@ -9,6 +9,7 @@
/* Macros for device tree declarations of npcx soc family */
#include <zephyr/dt-bindings/adc/adc.h>
#include <zephyr/dt-bindings/clock/npcx_clock.h>
#include <zephyr/dt-bindings/flash_controller/npcx_fiu_qspi.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/pinctrl/npcx-pinctrl.h>
@ -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>;

View file

@ -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 = <DT_SIZE_K(1024 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 14];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(512 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(512 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(512 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(512 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(1024 * 8)>;
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 14];
/* quad spi bus configuration of nor flash device */
qspi-flags = <NPCX_QSPI_SW_CS1>;
mapped-addr = <0x64000000>;
pinctrl-0 = <&int_flash_sl>;
pinctrl-names = "default";
};
};

View file

@ -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 = <DT_SIZE_K(512 * 8)>;
reg = <0>;
qspi-flags = <NPCX_QSPI_SW_CS1>;
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

View file

@ -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.

View file

@ -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

View file

@ -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 <zephyr/drivers/flash.h>
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__ */

View file

@ -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 <zephyr/dt-bindings/dt-util.h>
/* 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_ */

View file

@ -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 */

View file

@ -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);