Skip to content

Commit 5d72417

Browse files
ChiHuaLnashif
authored andcommitted
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 <[email protected]> Change-Id: I32bbf09f6e014b728ff8e4692e48151ae759e188
1 parent c4842c1 commit 5d72417

File tree

15 files changed

+402
-0
lines changed

15 files changed

+402
-0
lines changed

drivers/spi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ zephyr_library_sources_ifdef(CONFIG_SPI_XLNX_AXI_QUADSPI spi_xlnx_axi_quadspi.c)
2525
zephyr_library_sources_ifdef(CONFIG_ESP32_SPIM spi_esp32_spim.c)
2626
zephyr_library_sources_ifdef(CONFIG_SPI_TEST spi_test.c)
2727
zephyr_library_sources_ifdef(CONFIG_SPI_PSOC6 spi_psoc6.c)
28+
zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_FIU spi_npcx_fiu.c)
2829

2930
zephyr_library_sources_ifdef(CONFIG_USERSPACE spi_handlers.c)

drivers/spi/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,6 @@ source "drivers/spi/Kconfig.test"
8484

8585
source "drivers/spi/Kconfig.psoc6"
8686

87+
source "drivers/spi/Kconfig.npcx_fiu"
88+
8789
endif # SPI

drivers/spi/Kconfig.npcx_fiu

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# NPCX SPI Driver configuration options
2+
3+
# Copyright (c) 2021 Nuvoton Technology Corporation.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config SPI_NPCX_FIU
7+
bool "Nuvoton NPCX embedded controller (EC) SPI driver for NOR flash"
8+
depends on SOC_FAMILY_NPCX
9+
help
10+
Enable the SPI driver for NPCX family of processors. This driver is
11+
for the dedicated SPI controller (FIU) to access the NOR flash.

drivers/spi/spi_npcx_fiu.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright (c) 2021 Nuvoton Technology Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nuvoton_npcx_spi_fiu
8+
9+
#include <drivers/clock_control.h>
10+
#include <drivers/spi.h>
11+
#include <logging/log.h>
12+
#include <soc.h>
13+
14+
LOG_MODULE_REGISTER(spi_npcx_fiu, LOG_LEVEL_ERR);
15+
16+
#include "spi_context.h"
17+
18+
/* Device config */
19+
struct npcx_spi_fiu_config {
20+
/* flash interface unit base address */
21+
uintptr_t base;
22+
/* clock configuration */
23+
struct npcx_clk_cfg clk_cfg;
24+
};
25+
26+
/* Device data */
27+
struct npcx_spi_fiu_data {
28+
struct spi_context ctx;
29+
};
30+
31+
/* Driver convenience defines */
32+
#define DRV_CONFIG(dev) ((const struct npcx_spi_fiu_config *)(dev)->config)
33+
#define DRV_DATA(dev) ((struct npcx_spi_fiu_data *)(dev)->data)
34+
#define HAL_INSTANCE(dev) (struct fiu_reg *)(DRV_CONFIG(dev)->base)
35+
36+
static inline void spi_npcx_fiu_cs_level(const struct device *dev, int level)
37+
{
38+
struct fiu_reg *const inst = HAL_INSTANCE(dev);
39+
40+
/* Set chip select to high/low level */
41+
if (level == 0)
42+
inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_SW_CS1);
43+
else
44+
inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_SW_CS1);
45+
}
46+
47+
static inline void spi_npcx_fiu_exec_cmd(const struct device *dev, uint8_t code,
48+
uint8_t cts)
49+
{
50+
struct fiu_reg *const inst = HAL_INSTANCE(dev);
51+
52+
#ifdef CONFIG_ASSERT
53+
struct npcx_spi_fiu_data *data = DRV_DATA(dev);
54+
struct spi_context *ctx = &data->ctx;
55+
56+
/* Flash mutex must be held while executing UMA commands */
57+
__ASSERT((k_sem_count_get(&ctx->lock) == 0), "UMA is not locked");
58+
#endif
59+
60+
/* set UMA_CODE */
61+
inst->UMA_CODE = code;
62+
/* execute UMA flash transaction */
63+
inst->UMA_CTS = cts;
64+
while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE))
65+
continue;
66+
}
67+
68+
static int spi_npcx_fiu_transceive(const struct device *dev,
69+
const struct spi_config *spi_cfg,
70+
const struct spi_buf_set *tx_bufs,
71+
const struct spi_buf_set *rx_bufs)
72+
{
73+
struct npcx_spi_fiu_data *data = DRV_DATA(dev);
74+
struct fiu_reg *const inst = HAL_INSTANCE(dev);
75+
struct spi_context *ctx = &data->ctx;
76+
size_t cur_xfer_len;
77+
int error = 0;
78+
79+
spi_context_lock(ctx, false, NULL, spi_cfg);
80+
81+
/*
82+
* Configure UMA lock/unlock only if tx buffer set and rx buffer set
83+
* are both empty.
84+
*/
85+
if (tx_bufs == NULL && rx_bufs == NULL) {
86+
if (spi_cfg->operation & SPI_LOCK_ON)
87+
inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_UMA_LOCK);
88+
else
89+
inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_UMA_LOCK);
90+
spi_context_unlock_unconditionally(ctx);
91+
return 0;
92+
}
93+
94+
/* Assert chip assert */
95+
spi_npcx_fiu_cs_level(dev, 0);
96+
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
97+
if (rx_bufs == NULL) {
98+
while (spi_context_tx_buf_on(ctx)) {
99+
spi_npcx_fiu_exec_cmd(dev, *ctx->tx_buf,
100+
UMA_CODE_CMD_WR_ONLY);
101+
spi_context_update_tx(ctx, 1, 1);
102+
}
103+
} else {
104+
cur_xfer_len = spi_context_longest_current_buf(ctx);
105+
for (size_t i = 0; i < cur_xfer_len; i++) {
106+
spi_npcx_fiu_exec_cmd(dev, *ctx->tx_buf,
107+
UMA_CODE_CMD_WR_ONLY);
108+
spi_context_update_tx(ctx, 1, 1);
109+
spi_context_update_rx(ctx, 1, 1);
110+
}
111+
while (spi_context_rx_buf_on(ctx)) {
112+
inst->UMA_CTS = UMA_CODE_RD_BYTE(1);
113+
while (IS_BIT_SET(inst->UMA_CTS,
114+
NPCX_UMA_CTS_EXEC_DONE))
115+
continue;
116+
/* Get read transaction results */
117+
*ctx->rx_buf = inst->UMA_DB0;
118+
spi_context_update_tx(ctx, 1, 1);
119+
spi_context_update_rx(ctx, 1, 1);
120+
}
121+
}
122+
spi_npcx_fiu_cs_level(dev, 1);
123+
spi_context_release(ctx, error);
124+
125+
return error;
126+
}
127+
128+
int spi_npcx_fiu_release(const struct device *dev,
129+
const struct spi_config *config)
130+
{
131+
struct npcx_spi_fiu_data *data = DRV_DATA(dev);
132+
struct spi_context *ctx = &data->ctx;
133+
134+
if (!spi_context_configured(ctx, config)) {
135+
return -EINVAL;
136+
}
137+
138+
spi_context_unlock_unconditionally(ctx);
139+
return 0;
140+
}
141+
142+
static int spi_npcx_fiu_init(const struct device *dev)
143+
{
144+
const struct npcx_spi_fiu_config *const config = DRV_CONFIG(dev);
145+
const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
146+
int ret;
147+
148+
if (!device_is_ready(clk_dev)) {
149+
LOG_ERR("%s device not ready", clk_dev->name);
150+
return -ENODEV;
151+
}
152+
153+
/* Turn on device clock first and get source clock freq. */
154+
ret = clock_control_on(clk_dev,
155+
(clock_control_subsys_t *)&config->clk_cfg);
156+
if (ret < 0) {
157+
LOG_ERR("Turn on FIU clock fail %d", ret);
158+
return ret;
159+
}
160+
161+
/* Make sure the context is unlocked */
162+
spi_context_unlock_unconditionally(&DRV_DATA(dev)->ctx);
163+
164+
return 0;
165+
}
166+
167+
static struct spi_driver_api spi_npcx_fiu_api = {
168+
.transceive = spi_npcx_fiu_transceive,
169+
.release = spi_npcx_fiu_release,
170+
};
171+
172+
static const struct npcx_spi_fiu_config npcx_spi_fiu_config = {
173+
.base = DT_INST_REG_ADDR(0),
174+
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
175+
};
176+
177+
static struct npcx_spi_fiu_data npcx_spi_fiu_data = {
178+
SPI_CONTEXT_INIT_LOCK(npcx_spi_fiu_data, ctx),
179+
};
180+
181+
DEVICE_DT_INST_DEFINE(0, &spi_npcx_fiu_init, NULL, &npcx_spi_fiu_data,
182+
&npcx_spi_fiu_config, POST_KERNEL,
183+
CONFIG_SPI_INIT_PRIORITY, &spi_npcx_fiu_api);

dts/arm/nuvoton/npcx.dtsi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,16 @@
559559
status = "disabled";
560560
};
561561
};
562+
563+
/* Dedicated SPI interface to access SPI flashes */
564+
spi_fiu0: spi@40020000 {
565+
compatible = "nuvoton,npcx-spi-fiu";
566+
#address-cells = <1>;
567+
#size-cells = <0>;
568+
reg = <0x40020000 0x2000>;
569+
clocks = <&pcc NPCX_CLOCK_BUS_APB3 NPCX_PWDWN_CTL1 2>;
570+
label = "SPI_FIU";
571+
};
562572
};
563573

564574
soc-if {

dts/arm/nuvoton/npcx7m6fb.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@
2525
device-id = <0x21>;
2626
};
2727
};
28+
29+
&spi_fiu0 {
30+
int_flash: w25q80@0 {
31+
compatible ="jedec,spi-nor";
32+
/* 8388608 bits = 1 Mbytes */
33+
size = <0x800000>;
34+
label = "W25Q80";
35+
reg = <0>;
36+
spi-max-frequency = <50000000>;
37+
status = "okay";
38+
jedec-id = [ef 40 14];
39+
};
40+
};

dts/arm/nuvoton/npcx7m6fc.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@
2525
device-id = <0x29>;
2626
};
2727
};
28+
29+
&spi_fiu0 {
30+
int_flash: w25q40@0 {
31+
compatible ="jedec,spi-nor";
32+
/* 4194304 bits = 512K Bytes */
33+
size = <0x400000>;
34+
label = "W25Q40";
35+
reg = <0>;
36+
spi-max-frequency = <50000000>;
37+
status = "okay";
38+
jedec-id = [ef 40 13];
39+
};
40+
};

dts/arm/nuvoton/npcx7m7fc.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@
2525
device-id = <0x20>;
2626
};
2727
};
28+
29+
&spi_fiu0 {
30+
int_flash: w25q40@0 {
31+
compatible ="jedec,spi-nor";
32+
/* 4194304 bits = 512K Bytes */
33+
size = <0x400000>;
34+
label = "W25Q40";
35+
reg = <0>;
36+
spi-max-frequency = <50000000>;
37+
status = "okay";
38+
jedec-id = [ef 40 13];
39+
};
40+
};

dts/arm/nuvoton/npcx9m3f.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@
2525
device-id = <0x25>;
2626
};
2727
};
28+
29+
&spi_fiu0 {
30+
int_flash: w25q40@0 {
31+
compatible ="jedec,spi-nor";
32+
/* 4194304 bits = 512K Bytes */
33+
size = <0x400000>;
34+
label = "W25Q40";
35+
reg = <0>;
36+
spi-max-frequency = <50000000>;
37+
status = "okay";
38+
jedec-id = [ef 40 13];
39+
};
40+
};

dts/arm/nuvoton/npcx9m6f.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@
2525
device-id = <0x21>;
2626
};
2727
};
28+
29+
&spi_fiu0 {
30+
int_flash: w25q40@0 {
31+
compatible ="jedec,spi-nor";
32+
/* 4194304 bits = 512K Bytes */
33+
size = <0x400000>;
34+
label = "W25Q40";
35+
reg = <0>;
36+
spi-max-frequency = <50000000>;
37+
status = "okay";
38+
jedec-id = [ef 40 13];
39+
};
40+
};

0 commit comments

Comments
 (0)