You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
5.8 KiB
248 lines
5.8 KiB
/* |
|
* Copyright (c) 2023 Frontgrade Gaisler AB |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT gaisler_spimctrl |
|
|
|
#include <zephyr/drivers/spi.h> |
|
#include <zephyr/drivers/spi/rtio.h> |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(spi_spimctrl); |
|
#include "spi_context.h" |
|
|
|
|
|
struct spimctrl_regs { |
|
uint32_t conf; |
|
uint32_t ctrl; |
|
uint32_t stat; |
|
uint32_t rx; |
|
uint32_t tx; |
|
}; |
|
|
|
#define CONF_READCMD 0x0000007f |
|
#define CTRL_RST 0x00000010 |
|
#define CTRL_CSN 0x00000008 |
|
#define CTRL_EAS 0x00000004 |
|
#define CTRL_IEN 0x00000002 |
|
#define CTRL_USRC 0x00000001 |
|
#define STAT_INIT 0x00000004 |
|
#define STAT_BUSY 0x00000002 |
|
#define STAT_DONE 0x00000001 |
|
|
|
#define SPI_DATA(dev) ((struct data *) ((dev)->data)) |
|
|
|
struct cfg { |
|
volatile struct spimctrl_regs *regs; |
|
int interrupt; |
|
}; |
|
|
|
struct data { |
|
struct spi_context ctx; |
|
}; |
|
|
|
static int spi_config(struct spi_context *ctx, const struct spi_config *config) |
|
{ |
|
if (config->slave != 0) { |
|
LOG_ERR("More slaves than supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (SPI_WORD_SIZE_GET(config->operation) != 8) { |
|
LOG_ERR("Word size must be 8"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & SPI_CS_ACTIVE_HIGH) { |
|
LOG_ERR("CS active high not supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & SPI_LOCK_ON) { |
|
LOG_ERR("Lock On not supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { |
|
LOG_ERR("Only supports single mode"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & SPI_TRANSFER_LSB) { |
|
LOG_ERR("LSB first not supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) { |
|
LOG_ERR("Only supports CPOL=CPHA=0"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & SPI_OP_MODE_SLAVE) { |
|
LOG_ERR("Slave mode not supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (config->operation & SPI_MODE_LOOP) { |
|
LOG_ERR("Loopback not supported"); |
|
return -ENOTSUP; |
|
} |
|
|
|
ctx->config = config; |
|
|
|
return 0; |
|
} |
|
|
|
static int transceive(const struct device *dev, |
|
const struct spi_config *config, |
|
const struct spi_buf_set *tx_bufs, |
|
const struct spi_buf_set *rx_bufs) |
|
{ |
|
const struct cfg *const cfg = dev->config; |
|
volatile struct spimctrl_regs *const regs = cfg->regs; |
|
struct spi_context *ctx = &SPI_DATA(dev)->ctx; |
|
uint8_t txval; |
|
int rc; |
|
|
|
spi_context_lock(ctx, false, NULL, NULL, config); |
|
|
|
rc = spi_config(ctx, config); |
|
if (rc) { |
|
LOG_ERR("%s: config", __func__); |
|
spi_context_release(ctx, rc); |
|
return rc; |
|
} |
|
|
|
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1); |
|
|
|
regs->ctrl |= (CTRL_USRC | CTRL_IEN); |
|
regs->ctrl &= ~CTRL_CSN; |
|
|
|
if (spi_context_tx_buf_on(ctx)) { |
|
txval = *ctx->tx_buf; |
|
spi_context_update_tx(ctx, 1, 1); |
|
} else { |
|
txval = 0; |
|
} |
|
/* This will eventually trig the interrupt */ |
|
regs->tx = txval; |
|
|
|
rc = spi_context_wait_for_completion(ctx); |
|
|
|
regs->ctrl |= CTRL_CSN; |
|
regs->ctrl &= ~CTRL_USRC; |
|
spi_context_release(ctx, rc); |
|
|
|
return 0; |
|
} |
|
|
|
#ifdef CONFIG_SPI_ASYNC |
|
static int 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 -ENOTSUP; |
|
} |
|
#endif /* CONFIG_SPI_ASYNC */ |
|
|
|
static int release(const struct device *dev, const struct spi_config *config) |
|
{ |
|
spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx); |
|
return 0; |
|
} |
|
|
|
static void spim_isr(struct device *dev) |
|
{ |
|
const struct cfg *const cfg = dev->config; |
|
volatile struct spimctrl_regs *const regs = cfg->regs; |
|
struct spi_context *ctx = &SPI_DATA(dev)->ctx; |
|
uint8_t rx_byte; |
|
uint8_t val; |
|
|
|
if ((regs->stat & STAT_DONE) == 0) { |
|
return; |
|
} |
|
|
|
regs->stat = STAT_DONE; |
|
|
|
/* Always read register and maybe write mem. */ |
|
rx_byte = regs->rx; |
|
if (spi_context_rx_on(ctx)) { |
|
*ctx->rx_buf = rx_byte; |
|
spi_context_update_rx(ctx, 1, 1); |
|
} |
|
|
|
if (spi_context_tx_buf_on(ctx) == false && spi_context_rx_buf_on(ctx) == false) { |
|
regs->ctrl &= ~CTRL_IEN; |
|
spi_context_complete(ctx, dev, 0); |
|
return; |
|
} |
|
|
|
val = 0; |
|
if (spi_context_tx_buf_on(ctx)) { |
|
val = *ctx->tx_buf; |
|
spi_context_update_tx(ctx, 1, 1); |
|
} |
|
regs->tx = val; |
|
} |
|
|
|
static int init(const struct device *dev) |
|
{ |
|
const struct cfg *const cfg = dev->config; |
|
volatile struct spimctrl_regs *const regs = cfg->regs; |
|
|
|
regs->ctrl = CTRL_CSN; |
|
while (regs->stat & STAT_BUSY) { |
|
; |
|
} |
|
regs->stat = STAT_DONE; |
|
|
|
irq_connect_dynamic( |
|
cfg->interrupt, |
|
0, |
|
(void (*)(const void *)) spim_isr, |
|
dev, |
|
0 |
|
); |
|
irq_enable(cfg->interrupt); |
|
|
|
spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx); |
|
|
|
return 0; |
|
} |
|
|
|
static struct spi_driver_api api = { |
|
.transceive = transceive, |
|
#ifdef CONFIG_SPI_ASYNC |
|
.transceive_async = transceive_async, |
|
#endif /* CONFIG_SPI_ASYNC */ |
|
#ifdef CONFIG_SPI_RTIO |
|
.iodev_submit = spi_rtio_iodev_default_submit, |
|
#endif |
|
.release = release, |
|
}; |
|
|
|
#define SPI_INIT(n) \ |
|
static const struct cfg cfg_##n = { \ |
|
.regs = (struct spimctrl_regs *) \ |
|
DT_INST_REG_ADDR(n), \ |
|
.interrupt = DT_INST_IRQN(n), \ |
|
}; \ |
|
static struct data data_##n = { \ |
|
SPI_CONTEXT_INIT_LOCK(data_##n, ctx), \ |
|
SPI_CONTEXT_INIT_SYNC(data_##n, ctx), \ |
|
}; \ |
|
DEVICE_DT_INST_DEFINE(n, \ |
|
init, \ |
|
NULL, \ |
|
&data_##n, \ |
|
&cfg_##n, \ |
|
POST_KERNEL, \ |
|
CONFIG_SPI_INIT_PRIORITY, \ |
|
&api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
|
|
|