zephyr/drivers/flash/flash_npcx_fiu_qspi.c
Mulin Chao f942b44c56 soc: arm: npcx: move workaround methods for npcx series to its soc.c
Move workaround methods for npcx series to soc init functions. If
there's no workaround for this series, drop its soc.c file directly.

Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
2023-08-28 08:22:10 +01:00

293 lines
8.3 KiB
C

/*
* 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_4byte_mode(const struct device *dev,
const struct npcx_qspi_cfg *qspi_cfg)
{
#if !defined(CONFIG_SOC_SERIES_NPCX7) /* NPCX7 doesn't support this feature */
struct fiu_reg *const inst = HAL_INSTANCE(dev);
#if defined(CONFIG_SOC_SERIES_NPCX9)
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));
}
#elif defined(CONFIG_SOC_SERIES_NPCX4)
if (qspi_cfg->enter_4ba != 0) {
SET_FIELD(inst->SPI_DEV, NPCX_SPI_DEV_NADDRB, NPCX_DEV_NUM_ADDR_4BYTE);
}
#endif
#endif /* CONFIG_SOC_SERIES_NPCX7 */
}
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);
/* Select SPI device number for DRA mode in npcx4 series */
if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX4)) {
int spi_dev_num = (qspi_cfg->flags & NPCX_QSPI_SEC_FLASH_SL) != 0 ? 1 : 0;
SET_FIELD(inst->BURST_CFG, NPCX_BURST_CFG_SPI_DEV_SEL, spi_dev_num);
}
/* 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) */
qspi_npcx_config_dra_4byte_mode(dev, qspi_cfg);
}
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) || IS_ENABLED(CONFIG_SOC_SERIES_NPCX4)) {
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)