/* * 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);