Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions drivers/spi/Kconfig.gd32
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ config SPI_GD32
depends on DT_HAS_GD_GD32_SPI_ENABLED
help
Enables Gigadevice GD32 SPI driver.

if SPI_GD32

config SPI_GD32_INTERRUPT
bool "GD32 MCU SPI Interrupt Support"
default y if SPI_ASYNC
help
Enable the interrupt driven mode for SPI instances

endif # SPI_GD32
133 changes: 113 additions & 20 deletions drivers/spi/spi_gd32.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ struct spi_gd32_config {
uint32_t reg;
uint32_t rcu_periph_clock;
const struct pinctrl_dev_config *pcfg;
#ifdef CONFIG_SPI_GD32_INTERRUPT
void (*irq_configure)();
#endif
};

struct spi_gd32_data {
Expand Down Expand Up @@ -158,6 +161,10 @@ static int spi_gd32_frame_exchange(const struct device *dev)
struct spi_context *ctx = &data->ctx;
uint16_t tx_frame = 0U, rx_frame = 0U;

while ((SPI_STAT(cfg->reg) & SPI_STAT_TBE) == 0) {
/* NOP */
}

if (SPI_WORD_SIZE_GET(ctx->config->operation) == 8) {
if (spi_context_tx_buf_on(ctx)) {
tx_frame = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf));
Expand Down Expand Up @@ -199,16 +206,17 @@ static int spi_gd32_frame_exchange(const struct device *dev)
return spi_gd32_get_err(cfg);
}

static int spi_gd32_transceive(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
static int spi_gd32_transceive_impl(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs,
struct k_poll_signal *poll_sig)
{
struct spi_gd32_data *data = dev->data;
const struct spi_gd32_config *cfg = dev->config;
int ret;

spi_context_lock(&data->ctx, false, NULL, config);
spi_context_lock(&data->ctx, !!poll_sig, poll_sig, config);

ret = spi_gd32_configure(dev, config);
if (ret < 0) {
Expand All @@ -221,13 +229,28 @@ static int spi_gd32_transceive(const struct device *dev,

spi_context_cs_control(&data->ctx, true);

#ifdef CONFIG_SPI_GD32_INTERRUPT
SPI_STAT(cfg->reg) &= ~(SPI_STAT_RBNE | SPI_STAT_TBE | SPI_GD32_ERR_MASK);
SPI_CTL1(cfg->reg) |= (SPI_CTL1_RBNEIE | SPI_CTL1_TBEIE | SPI_CTL1_ERRIE);
ret = spi_context_wait_for_completion(&data->ctx);
#else
do {
ret = spi_gd32_frame_exchange(dev);
if (ret < 0) {
break;
}
} while (spi_gd32_transfer_ongoing(data));

#ifdef CONFIG_SPI_ASYNC
spi_context_complete(&data->ctx, ret);
Copy link
Contributor

@cameled cameled Jul 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required for SPI_ASYNC, even in a polling case?
As I know, it not required since without interrupt support, async will not work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my understanding, SPI_ASYNC is just only notify finish of transfer with signal object.
Potentially it should works with polling implementations.
(I think it is enough useful for running with worker thread.)

For example, this PR work with enabling SPI_ASYNC even if not enabling SPI_GD32_INTERRUPT.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation.

#endif
#endif

while (!(SPI_STAT(cfg->reg) & SPI_STAT_TBE) ||
(SPI_STAT(cfg->reg) & SPI_STAT_TRANS)) {
/* Wait until last frame transfer complete. */
}

spi_context_cs_control(&data->ctx, false);

SPI_CTL0(cfg->reg) &= ~SPI_CTL0_SPIEN;
Expand All @@ -238,6 +261,58 @@ static int spi_gd32_transceive(const struct device *dev,
return ret;
}

static int spi_gd32_transceive(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
return spi_gd32_transceive_impl(dev, config, tx_bufs, rx_bufs, NULL);
}

#ifdef CONFIG_SPI_ASYNC
static int spi_gd32_transceive_async(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs,
struct k_poll_signal *async)
{
return spi_gd32_transceive_impl(dev, config, tx_bufs, rx_bufs, async);
}
#endif

#ifdef CONFIG_SPI_GD32_INTERRUPT

static void spi_gd32_complete(const struct device *dev, int status)
{
struct spi_gd32_data *data = dev->data;
const struct spi_gd32_config *cfg = dev->config;

SPI_CTL1(cfg->reg) &= ~(SPI_CTL1_RBNEIE | SPI_CTL1_TBEIE | SPI_CTL1_ERRIE);

spi_context_complete(&data->ctx, status);
}

static void spi_gd32_isr(struct device *dev)
{
const struct spi_gd32_config *cfg = dev->config;
struct spi_gd32_data *data = dev->data;
int err = 0;

if ((SPI_STAT(cfg->reg) & SPI_GD32_ERR_MASK) != 0) {
err = spi_gd32_get_err(cfg);
} else {
err = spi_gd32_frame_exchange(dev);
}

if (err || !spi_gd32_transfer_ongoing(data)) {
spi_gd32_complete(dev, err);
}

SPI_STAT(cfg->reg) = 0;
}

#endif /* INTERRUPT */

static int spi_gd32_release(const struct device *dev,
const struct spi_config *config)
{
Expand All @@ -250,6 +325,9 @@ static int spi_gd32_release(const struct device *dev,

static struct spi_driver_api spi_gd32_driver_api = {
.transceive = spi_gd32_transceive,
#ifdef CONFIG_SPI_ASYNC
.transceive_async = spi_gd32_transceive_async,
#endif
.release = spi_gd32_release
};

Expand All @@ -272,25 +350,40 @@ int spi_gd32_init(const struct device *dev)
return ret;
}

#ifdef CONFIG_SPI_GD32_INTERRUPT
cfg->irq_configure(dev);
#endif

spi_context_unlock_unconditionally(&data->ctx);

return 0;
}

#define GD32_SPI_INIT(idx) \
PINCTRL_DT_INST_DEFINE(idx); \
static struct spi_gd32_data spi_gd32_data_##idx = { \
SPI_CONTEXT_INIT_LOCK(spi_gd32_data_##idx, ctx), \
SPI_CONTEXT_INIT_SYNC(spi_gd32_data_##idx, ctx), \
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(idx), ctx) \
}; \
static struct spi_gd32_config spi_gd32_config_##idx = { \
.reg = DT_INST_REG_ADDR(idx), \
.rcu_periph_clock = DT_INST_PROP(idx, rcu_periph_clock), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
}; \
DEVICE_DT_INST_DEFINE(idx, &spi_gd32_init, NULL, &spi_gd32_data_##idx, \
&spi_gd32_config_##idx, POST_KERNEL, \
CONFIG_SPI_INIT_PRIORITY, &spi_gd32_driver_api);
#define GD32_IRQ_CONFIGURE(idx) \
static void spi_gd32_irq_configure_##idx(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), \
spi_gd32_isr, \
DEVICE_DT_INST_GET(idx), 0); \
irq_enable(DT_INST_IRQN(idx)); \
}

#define GD32_SPI_INIT(idx) \
PINCTRL_DT_INST_DEFINE(idx); \
IF_ENABLED(CONFIG_SPI_GD32_INTERRUPT, (GD32_IRQ_CONFIGURE(idx))); \
static struct spi_gd32_data spi_gd32_data_##idx = { \
SPI_CONTEXT_INIT_LOCK(spi_gd32_data_##idx, ctx), \
SPI_CONTEXT_INIT_SYNC(spi_gd32_data_##idx, ctx), \
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(idx), ctx) }; \
static struct spi_gd32_config spi_gd32_config_##idx = { \
.reg = DT_INST_REG_ADDR(idx), \
.rcu_periph_clock = DT_INST_PROP(idx, rcu_periph_clock), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
IF_ENABLED(CONFIG_SPI_GD32_INTERRUPT, \
(.irq_configure = spi_gd32_irq_configure_##idx)) }; \
DEVICE_DT_INST_DEFINE(idx, &spi_gd32_init, NULL, \
&spi_gd32_data_##idx, &spi_gd32_config_##idx, \
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
&spi_gd32_driver_api);

DT_INST_FOREACH_STATUS_OKAY(GD32_SPI_INIT)