zephyr/drivers/spi/spi_smartbond.c
Ben Lauret 9cdc5d38b2 drivers: spi: Add driver for smartbond
This adds the SPI driver for the Renesas SmartBond(tm) DA1469x MCU family.
The driver only supports controller mode. All four SPI modes are supported.
Note that the lowest supported speed is 2285714Hz.
Requesting speeds higher than 16MHz, will result in a 16MHz SCLK.

Co-authored-by: Stan Geitel <stan@geitel.nl>

Signed-off-by: Ben Lauret <ben.lauret.wm@renesas.com>
2023-04-20 10:32:40 +02:00

292 lines
8.6 KiB
C

/*
* Copyright (c) 2022 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_smartbond_spi
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(spi_smartbond);
#include "spi_context.h"
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/spi.h>
#include <DA1469xAB.h>
#define DIVN_CLK 32000000 /* divN_clk 32MHz */
#define SCLK_FREQ_2MHZ (DIVN_CLK / 14) /* 2.285714MHz*/
#define SCLK_FREQ_4MHZ (DIVN_CLK / 8) /* 4MHz */
#define SCLK_FREQ_8MHZ (DIVN_CLK / 4) /* 8MHz */
#define SCLK_FREQ_16MHZ (DIVN_CLK / 2) /* 16MHz */
struct spi_smartbond_cfg {
SPI_Type *regs;
int periph_clock_config;
const struct pinctrl_dev_config *pcfg;
};
struct spi_smartbond_data {
struct spi_context ctx;
uint8_t dfs;
};
static inline void spi_smartbond_enable(const struct spi_smartbond_cfg *cfg, bool enable)
{
if (enable) {
cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_ON_Msk;
cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_RST_Msk;
} else {
cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_ON_Msk;
cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_RST_Msk;
}
}
static inline bool spi_smartbond_isenabled(const struct spi_smartbond_cfg *cfg)
{
return (!!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_ON_Msk)) &&
(!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_RST_Msk));
}
static inline int spi_smartbond_set_speed(const struct spi_smartbond_cfg *cfg,
const uint32_t frequency)
{
if (frequency < SCLK_FREQ_2MHZ) {
LOG_ERR("Frequency is lower than minimal SCLK %d", SCLK_FREQ_2MHZ);
return -ENOTSUP;
} else if (frequency < SCLK_FREQ_4MHZ) {
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) |
3UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos;
} else if (frequency < SCLK_FREQ_8MHZ) {
cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk);
} else if (frequency < SCLK_FREQ_16MHZ) {
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) |
1UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos;
} else {
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) |
2UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos;
}
return 0;
}
static inline int spi_smartbond_set_word_size(const struct spi_smartbond_cfg *cfg,
struct spi_smartbond_data *data,
const uint32_t operation)
{
switch (SPI_WORD_SIZE_GET(operation)) {
case 8:
data->dfs = 1;
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk);
break;
case 16:
data->dfs = 2;
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) |
(1UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos);
break;
case 32:
data->dfs = 4;
cfg->regs->SPI_CTRL_REG =
(cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) |
(2UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos);
break;
default:
LOG_ERR("Word size not supported");
return -ENOTSUP;
}
return 0;
}
static int spi_smartbond_configure(const struct spi_smartbond_cfg *cfg,
struct spi_smartbond_data *data,
const struct spi_config *spi_cfg)
{
int rc;
if (spi_context_configured(&data->ctx, spi_cfg)) {
return 0;
}
if (spi_cfg->operation & SPI_OP_MODE_SLAVE) {
LOG_ERR("Slave mode not yet supported");
return -ENOTSUP;
}
if (spi_cfg->operation & SPI_HALF_DUPLEX) {
LOG_ERR("Half-duplex not supported");
return -ENOTSUP;
}
if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
(spi_cfg->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
LOG_ERR("Only single line mode is supported");
return -ENOTSUP;
}
if (spi_cfg->operation & SPI_MODE_LOOP) {
LOG_ERR("Loopback mode is not supported");
return -ENOTSUP;
}
if (spi_smartbond_isenabled(cfg)) {
spi_smartbond_enable(cfg, false);
}
rc = spi_smartbond_set_speed(cfg, spi_cfg->frequency);
if (rc) {
return rc;
}
cfg->regs->SPI_CTRL_REG =
(spi_cfg->operation & SPI_MODE_CPOL)
? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_POL_Msk)
: (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_POL_Msk);
cfg->regs->SPI_CTRL_REG =
(spi_cfg->operation & SPI_MODE_CPHA)
? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_PHA_Msk)
: (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_PHA_Msk);
rc = spi_smartbond_set_word_size(cfg, data, spi_cfg->operation);
if (rc) {
return rc;
}
cfg->regs->SPI_CTRL_REG &= ~(SPI_SPI_CTRL_REG_SPI_FIFO_MODE_Msk);
spi_smartbond_enable(cfg, true);
cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_MINT_Msk;
data->ctx.config = spi_cfg;
return 0;
}
static int spi_smartbond_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)
{
const struct spi_smartbond_cfg *cfg = dev->config;
struct spi_smartbond_data *data = dev->data;
struct spi_context *ctx = &data->ctx;
uint32_t bitmask;
int rc;
spi_context_lock(&data->ctx, false, NULL, NULL, spi_cfg);
rc = spi_smartbond_configure(cfg, data, spi_cfg);
if (rc == 0) {
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs);
spi_context_cs_control(ctx, true);
bitmask = ~((~0UL) << SPI_WORD_SIZE_GET(data->ctx.config->operation));
while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
if (spi_context_tx_buf_on(ctx)) {
cfg->regs->SPI_RX_TX_REG = (*(uint32_t *)ctx->tx_buf) & bitmask;
spi_context_update_tx(ctx, data->dfs, 1);
} else {
cfg->regs->SPI_RX_TX_REG = 0UL;
}
while (!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_INT_BIT_Msk)) {
};
if (spi_context_rx_buf_on(ctx)) {
(*(uint32_t *)ctx->rx_buf) = cfg->regs->SPI_RX_TX_REG & bitmask;
spi_context_update_rx(ctx, data->dfs, 1);
} else {
(void)cfg->regs->SPI_RX_TX_REG;
}
cfg->regs->SPI_CLEAR_INT_REG = 1UL;
}
}
spi_context_cs_control(ctx, false);
spi_context_release(&data->ctx, rc);
return rc;
}
#ifdef CONFIG_SPI_ASYNC
static int spi_smartbond_transceive_async(const struct device *dev,
const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs, spi_callback_t cb,
void *userdata)
{
return -ENOTSUP;
}
#endif
static int spi_smartbond_release(const struct device *dev, const struct spi_config *spi_cfg)
{
struct spi_smartbond_data *data = dev->data;
struct spi_context *ctx = &data->ctx;
if (!spi_context_configured(ctx, spi_cfg)) {
LOG_ERR("SPI configuration was not the last one to be used");
return -EINVAL;
}
spi_context_unlock_unconditionally(ctx);
return 0;
}
static const struct spi_driver_api spi_smartbond_driver_api = {
.transceive = spi_smartbond_transceive,
#ifdef CONFIG_SPI_ASYNC
.transceive_async = spi_smartbond_transceive_async,
#endif
.release = spi_smartbond_release,
};
static int spi_smartbond_init(const struct device *dev)
{
const struct spi_smartbond_cfg *cfg = dev->config;
struct spi_smartbond_data *data = dev->data;
int rc;
CRG_COM->RESET_CLK_COM_REG = cfg->periph_clock_config << 1;
CRG_COM->SET_CLK_COM_REG = cfg->periph_clock_config;
rc = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (rc < 0) {
LOG_ERR("Failed to configure SPI pins");
return rc;
}
rc = spi_context_cs_configure_all(&data->ctx);
if (rc < 0) {
LOG_ERR("Failed to configure CS pins: %d", rc);
return rc;
}
spi_context_unlock_unconditionally(&data->ctx);
return 0;
}
#define SPI_SMARTBOND_DEVICE(id) \
PINCTRL_DT_INST_DEFINE(id); \
static const struct spi_smartbond_cfg spi_smartbond_##id##_cfg = { \
.regs = (SPI_Type *)DT_INST_REG_ADDR(id), \
.periph_clock_config = DT_INST_PROP(id, periph_clock_config), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
}; \
static struct spi_smartbond_data spi_smartbond_##id##_data = { \
SPI_CONTEXT_INIT_LOCK(spi_smartbond_##id##_data, ctx), \
SPI_CONTEXT_INIT_SYNC(spi_smartbond_##id##_data, ctx), \
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx)}; \
DEVICE_DT_INST_DEFINE(id, spi_smartbond_init, NULL, &spi_smartbond_##id##_data, \
&spi_smartbond_##id##_cfg, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
&spi_smartbond_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SPI_SMARTBOND_DEVICE)