drivers: spi: npcx: add SPI support to access the SPI flash

The FIU/UMA module in the NPCX chip provides an dedicated SPI interface
to access the SPI flash. This commit adds the driver support for it.
With this commit, the application can call the flash APIs
(via spi_nor.c) to access the internal flash of NPCX EC chips.

Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
Change-Id: I32bbf09f6e014b728ff8e4692e48151ae759e188
This commit is contained in:
Jun Lin 2021-10-13 14:17:02 +08:00 committed by Anas Nashif
parent c4842c1879
commit 5d72417df4
15 changed files with 402 additions and 0 deletions

View file

@ -25,5 +25,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_USERSPACE spi_handlers.c)

View file

@ -84,4 +84,6 @@ source "drivers/spi/Kconfig.test"
source "drivers/spi/Kconfig.psoc6"
source "drivers/spi/Kconfig.npcx_fiu"
endif # SPI

View file

@ -0,0 +1,11 @@
# 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"
depends on SOC_FAMILY_NPCX
help
Enable the SPI driver for NPCX family of processors. This driver is
for the dedicated SPI controller (FIU) to access the NOR flash.

183
drivers/spi/spi_npcx_fiu.c Normal file
View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2021 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_npcx_spi_fiu
#include <drivers/clock_control.h>
#include <drivers/spi.h>
#include <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 DRV_CONFIG(dev) ((const struct npcx_spi_fiu_config *)(dev)->config)
#define DRV_DATA(dev) ((struct npcx_spi_fiu_data *)(dev)->data)
#define HAL_INSTANCE(dev) (struct fiu_reg *)(DRV_CONFIG(dev)->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 = DRV_DATA(dev);
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 = DRV_DATA(dev);
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, 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 = DRV_DATA(dev);
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 = DRV_CONFIG(dev);
const struct device *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(&DRV_DATA(dev)->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

@ -559,6 +559,16 @@
status = "disabled";
};
};
/* Dedicated SPI interface to access SPI flashes */
spi_fiu0: spi@40020000 {
compatible = "nuvoton,npcx-spi-fiu";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40020000 0x2000>;
clocks = <&pcc NPCX_CLOCK_BUS_APB3 NPCX_PWDWN_CTL1 2>;
label = "SPI_FIU";
};
};
soc-if {

View file

@ -25,3 +25,16 @@
device-id = <0x21>;
};
};
&spi_fiu0 {
int_flash: w25q80@0 {
compatible ="jedec,spi-nor";
/* 8388608 bits = 1 Mbytes */
size = <0x800000>;
label = "W25Q80";
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 14];
};
};

View file

@ -25,3 +25,16 @@
device-id = <0x29>;
};
};
&spi_fiu0 {
int_flash: w25q40@0 {
compatible ="jedec,spi-nor";
/* 4194304 bits = 512K Bytes */
size = <0x400000>;
label = "W25Q40";
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
};
};

View file

@ -25,3 +25,16 @@
device-id = <0x20>;
};
};
&spi_fiu0 {
int_flash: w25q40@0 {
compatible ="jedec,spi-nor";
/* 4194304 bits = 512K Bytes */
size = <0x400000>;
label = "W25Q40";
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
};
};

View file

@ -25,3 +25,16 @@
device-id = <0x25>;
};
};
&spi_fiu0 {
int_flash: w25q40@0 {
compatible ="jedec,spi-nor";
/* 4194304 bits = 512K Bytes */
size = <0x400000>;
label = "W25Q40";
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
};
};

View file

@ -25,3 +25,16 @@
device-id = <0x21>;
};
};
&spi_fiu0 {
int_flash: w25q40@0 {
compatible ="jedec,spi-nor";
/* 4194304 bits = 512K Bytes */
size = <0x400000>;
label = "W25Q40";
reg = <0>;
spi-max-frequency = <50000000>;
status = "okay";
jedec-id = [ef 40 13];
};
};

View file

@ -0,0 +1,16 @@
# 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
label:
required: true

View file

@ -1410,4 +1410,97 @@ struct ps2_reg {
#define NPCX_PSIEN_PS2_WUE 4
#define NPCX_PSIEN_PS2_CLK_SEL 7
/* Flash Interface Unit (FIU) device registers */
struct fiu_reg {
volatile uint8_t reserved1;
/* 0x001: Burst Configuration */
volatile uint8_t BURST_CFG;
/* 0x002: FIU Response Configuration */
volatile uint8_t RESP_CFG;
volatile uint8_t reserved2[17];
/* 0x014: SPI Flash Configuration */
volatile uint8_t SPI_FL_CFG;
volatile uint8_t reserved3;
/* 0x016: UMA Code Byte */
volatile uint8_t UMA_CODE;
/* 0x017: UMA Address Byte 0 */
volatile uint8_t UMA_AB0;
/* 0x018: UMA Address Byte 1 */
volatile uint8_t UMA_AB1;
/* 0x019: UMA Address Byte 2 */
volatile uint8_t UMA_AB2;
/* 0x01A: UMA Data Byte 0 */
volatile uint8_t UMA_DB0;
/* 0x01B: UMA Data Byte 1 */
volatile uint8_t UMA_DB1;
/* 0x01C: UMA Data Byte 2 */
volatile uint8_t UMA_DB2;
/* 0x01D: UMA Data Byte 3 */
volatile uint8_t UMA_DB3;
/* 0x01E: UMA Control and Status */
volatile uint8_t UMA_CTS;
/* 0x01F: UMA Extended Control and Status */
volatile uint8_t UMA_ECTS;
/* 0x020: UMA Data Bytes 0-3 */
volatile uint32_t UMA_DB0_3;
volatile uint8_t reserved4[2];
/* 0x026: CRC Control Register */
volatile uint8_t CRCCON;
/* 0x027: CRC Entry Register */
volatile uint8_t CRCENT;
/* 0x028: CRC Initialization and Result Register */
volatile uint32_t CRCRSLT;
volatile uint8_t reserved5[4];
/* 0x030: FIU Read Command */
volatile uint8_t FIU_RD_CMD;
volatile uint8_t reserved6;
/* 0x032: FIU Dummy Cycles */
volatile uint8_t FIU_DMM_CYC;
/* 0x033: FIU Extended Configuration */
volatile uint8_t FIU_EXT_CFG;
};
/* FIU register fields */
#define NPCX_RESP_CFG_IAD_EN 0
#define NPCX_RESP_CFG_DEV_SIZE_EX 2
#define NPCX_UMA_CTS_A_SIZE 3
#define NPCX_UMA_CTS_C_SIZE 4
#define NPCX_UMA_CTS_RD_WR 5
#define NPCX_UMA_CTS_DEV_NUM 6
#define NPCX_UMA_CTS_EXEC_DONE 7
#define NPCX_UMA_ECTS_SW_CS0 0
#define NPCX_UMA_ECTS_SW_CS1 1
#define NPCX_UMA_ECTS_SEC_CS 2
#define NPCX_UMA_ECTS_UMA_LOCK 3
/* UMA fields selections */
#define UMA_FLD_ADDR BIT(NPCX_UMA_CTS_A_SIZE) /* 3-bytes ADR field */
#define UMA_FLD_NO_CMD BIT(NPCX_UMA_CTS_C_SIZE) /* No 1-Byte CMD field */
#define UMA_FLD_WRITE BIT(NPCX_UMA_CTS_RD_WR) /* Write transaction */
#define UMA_FLD_SHD_SL BIT(NPCX_UMA_CTS_DEV_NUM) /* Shared flash selected */
#define UMA_FLD_EXEC BIT(NPCX_UMA_CTS_EXEC_DONE)
#define UMA_FIELD_DATA_1 0x01
#define UMA_FIELD_DATA_2 0x02
#define UMA_FIELD_DATA_3 0x03
#define UMA_FIELD_DATA_4 0x04
/* UMA code for transaction */
#define UMA_CODE_CMD_ONLY (UMA_FLD_EXEC | UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_ADR (UMA_FLD_EXEC | UMA_FLD_ADDR | \
UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_RD_BYTE(n) (UMA_FLD_EXEC | UMA_FIELD_DATA_##n | \
UMA_FLD_SHD_SL)
#define UMA_CODE_RD_BYTE(n) (UMA_FLD_EXEC | UMA_FLD_NO_CMD | \
UMA_FIELD_DATA_##n | UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_WR_ONLY (UMA_FLD_EXEC | UMA_FLD_WRITE | \
UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_WR_BYTE(n) (UMA_FLD_EXEC | UMA_FLD_WRITE | \
UMA_FIELD_DATA_##n | UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_WR_ADR (UMA_FLD_EXEC | UMA_FLD_WRITE | UMA_FLD_ADDR | \
UMA_FLD_SHD_SL)
#define UMA_CODE_CMD_ADR_WR_BYTE(n) (UMA_FLD_EXEC | UMA_FLD_WRITE | \
UMA_FLD_ADDR | UMA_FIELD_DATA_##n | \
UMA_FLD_SHD_SL)
#endif /* _NUVOTON_NPCX_REG_DEF_H */

View file

@ -161,3 +161,12 @@ NPCX_REG_OFFSET_CHECK(ps2_reg, PSCON, 0x004);
NPCX_REG_OFFSET_CHECK(ps2_reg, PSOSIG, 0x006);
NPCX_REG_OFFSET_CHECK(ps2_reg, PSISIG, 0x008);
NPCX_REG_OFFSET_CHECK(ps2_reg, PSIEN, 0x00a);
/* FIU register structure check */
NPCX_REG_SIZE_CHECK(fiu_reg, 0x034);
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);
NPCX_REG_OFFSET_CHECK(fiu_reg, CRCCON, 0x026);
NPCX_REG_OFFSET_CHECK(fiu_reg, FIU_RD_CMD, 0x030);
NPCX_REG_OFFSET_CHECK(fiu_reg, FIU_EXT_CFG, 0x033);

View file

@ -106,6 +106,12 @@ config PM_DEVICE
endif # SOC_POWER_MANAGEMENT
config SPI_NPCX_FIU
default y
depends on SPI
help
Enable support fot NPCX SPI (FIU) driver for the NOR SPI flash access.
source "soc/arm/nuvoton_npcx/npcx7/Kconfig.defconfig.npcx7*"
endif # SOC_SERIES_NPCX7

View file

@ -116,6 +116,12 @@ config PM_DEVICE
endif # SOC_POWER_MANAGEMENT
config SPI_NPCX_FIU
default y
depends on SPI
help
Enable support fot NPCX SPI (FIU) driver for the NOR SPI flash access.
source "soc/arm/nuvoton_npcx/npcx9/Kconfig.defconfig.npcx9*"
endif # SOC_SERIES_NPCX9