drivers: spi: Add support for Apollo3 SoCs SPI
This commit adds support for the SPI which can be found in Apollo3 SoCs, it can work in both DMA and non-DMA modes Signed-off-by: Hao Luo <hluo@ambiq.com>
This commit is contained in:
parent
ccc570f0c1
commit
524ea22952
8 changed files with 429 additions and 67 deletions
|
@ -101,6 +101,14 @@
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&spi0 {
|
||||||
|
compatible = "ambiq,spi";
|
||||||
|
pinctrl-0 = <&spi0_default>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
clock-frequency = <DT_FREQ_M(1)>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
&i2c3 {
|
&i2c3 {
|
||||||
compatible = "ambiq,i2c";
|
compatible = "ambiq,i2c";
|
||||||
pinctrl-0 = <&i2c3_default>;
|
pinctrl-0 = <&i2c3_default>;
|
||||||
|
|
|
@ -12,6 +12,7 @@ supported:
|
||||||
- watchdog
|
- watchdog
|
||||||
- counter
|
- counter
|
||||||
- gpio
|
- gpio
|
||||||
|
- spi
|
||||||
- i2c
|
- i2c
|
||||||
testing:
|
testing:
|
||||||
ignore_tags:
|
ignore_tags:
|
||||||
|
|
|
@ -101,6 +101,14 @@
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&spi0 {
|
||||||
|
compatible = "ambiq,spi";
|
||||||
|
pinctrl-0 = <&spi0_default>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
clock-frequency = <DT_FREQ_M(1)>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
&i2c3 {
|
&i2c3 {
|
||||||
compatible = "ambiq,i2c";
|
compatible = "ambiq,i2c";
|
||||||
pinctrl-0 = <&i2c3_default>;
|
pinctrl-0 = <&i2c3_default>;
|
||||||
|
|
|
@ -12,6 +12,7 @@ supported:
|
||||||
- watchdog
|
- watchdog
|
||||||
- counter
|
- counter
|
||||||
- gpio
|
- gpio
|
||||||
|
- spi
|
||||||
- i2c
|
- i2c
|
||||||
testing:
|
testing:
|
||||||
ignore_tags:
|
ignore_tags:
|
||||||
|
|
|
@ -15,6 +15,19 @@ config SPI_AMBIQ
|
||||||
help
|
help
|
||||||
Enable driver for Ambiq SPI.
|
Enable driver for Ambiq SPI.
|
||||||
|
|
||||||
|
config SPI_AMBIQ_DMA
|
||||||
|
bool "AMBIQ APOLLO SPI DMA Support"
|
||||||
|
depends on SPI_AMBIQ
|
||||||
|
help
|
||||||
|
Enable DMA for Ambiq SPI.
|
||||||
|
|
||||||
|
config SPI_DMA_TCB_BUFFER_SIZE
|
||||||
|
int "DMA Transfer Control Buffer size in words."
|
||||||
|
default 1024
|
||||||
|
depends on SPI_AMBIQ_DMA
|
||||||
|
help
|
||||||
|
DMA Transfer Control Buffer size in words
|
||||||
|
|
||||||
config MSPI_AMBIQ
|
config MSPI_AMBIQ
|
||||||
bool "AMBIQ MSPI driver"
|
bool "AMBIQ MSPI driver"
|
||||||
default y
|
default y
|
||||||
|
|
|
@ -28,20 +28,62 @@ struct spi_ambiq_config {
|
||||||
uint32_t clock_freq;
|
uint32_t clock_freq;
|
||||||
const struct pinctrl_dev_config *pcfg;
|
const struct pinctrl_dev_config *pcfg;
|
||||||
ambiq_spi_pwr_func_t pwr_func;
|
ambiq_spi_pwr_func_t pwr_func;
|
||||||
|
void (*irq_config_func)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct spi_ambiq_data {
|
struct spi_ambiq_data {
|
||||||
struct spi_context ctx;
|
struct spi_context ctx;
|
||||||
am_hal_iom_config_t iom_cfg;
|
am_hal_iom_config_t iom_cfg;
|
||||||
void *IOMHandle;
|
void *iom_handler;
|
||||||
|
int inst_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SPI_BASE (((const struct spi_ambiq_config *)(dev)->config)->base)
|
typedef void (*spi_context_update_trx)(struct spi_context *ctx, uint8_t dfs, uint32_t len);
|
||||||
#define REG_STAT 0x248
|
|
||||||
#define IDLE_STAT 0x4
|
|
||||||
#define SPI_STAT(dev) (SPI_BASE + REG_STAT)
|
|
||||||
#define SPI_WORD_SIZE 8
|
#define SPI_WORD_SIZE 8
|
||||||
|
|
||||||
|
#define SPI_CS_INDEX 3
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
static __aligned(32) struct {
|
||||||
|
__aligned(32) uint32_t buf[CONFIG_SPI_DMA_TCB_BUFFER_SIZE];
|
||||||
|
} spi_dma_tcb_buf[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)] __attribute__((__section__(".nocache")));
|
||||||
|
|
||||||
|
static void spi_ambiq_callback(void *callback_ctxt, uint32_t status)
|
||||||
|
{
|
||||||
|
const struct device *dev = callback_ctxt;
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
struct spi_context *ctx = &data->ctx;
|
||||||
|
|
||||||
|
spi_context_complete(ctx, dev, (status == AM_HAL_STATUS_SUCCESS) ? 0 : -EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_ambiq_reset(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
struct spi_context *ctx = &data->ctx;
|
||||||
|
|
||||||
|
/* cancel timed out transaction */
|
||||||
|
am_hal_iom_disable(data->iom_handler);
|
||||||
|
/* NULL config to trigger reconfigure on next xfer */
|
||||||
|
ctx->config = NULL;
|
||||||
|
/* signal any thread waiting on sync semaphore */
|
||||||
|
spi_context_complete(ctx, dev, -ETIMEDOUT);
|
||||||
|
/* clean up for next xfer */
|
||||||
|
k_sem_reset(&ctx->sync);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void spi_ambiq_isr(const struct device *dev)
|
||||||
|
{
|
||||||
|
uint32_t ui32Status;
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
|
||||||
|
am_hal_iom_interrupt_status_get(data->iom_handler, false, &ui32Status);
|
||||||
|
am_hal_iom_interrupt_clear(data->iom_handler, ui32Status);
|
||||||
|
am_hal_iom_interrupt_service(data->iom_handler, ui32Status);
|
||||||
|
}
|
||||||
|
|
||||||
static int spi_config(const struct device *dev, const struct spi_config *config)
|
static int spi_config(const struct device *dev, const struct spi_config *config)
|
||||||
{
|
{
|
||||||
struct spi_ambiq_data *data = dev->data;
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
@ -57,12 +99,7 @@ static int spi_config(const struct device *dev, const struct spi_config *config)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->operation & SPI_HALF_DUPLEX) {
|
if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) {
|
||||||
LOG_ERR("Half-duplex not supported");
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SPI_WORD_SIZE_GET(config->operation) != 8) {
|
|
||||||
LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
|
LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
@ -110,75 +147,224 @@ static int spi_config(const struct device *dev, const struct spi_config *config)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->iom_cfg.ui32ClockFreq = cfg->clock_freq;
|
/* Select slower of two: SPI bus frequency for SPI device or SPI master clock frequency */
|
||||||
|
data->iom_cfg.ui32ClockFreq =
|
||||||
|
(config->frequency ? MIN(config->frequency, cfg->clock_freq) : cfg->clock_freq);
|
||||||
ctx->config = config;
|
ctx->config = config;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
data->iom_cfg.pNBTxnBuf = spi_dma_tcb_buf[data->inst_idx].buf;
|
||||||
|
data->iom_cfg.ui32NBTxnBufLength = CONFIG_SPI_DMA_TCB_BUFFER_SIZE;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Disable IOM instance as it cannot be configured when enabled*/
|
/* Disable IOM instance as it cannot be configured when enabled*/
|
||||||
ret = am_hal_iom_disable(data->IOMHandle);
|
ret = am_hal_iom_disable(data->iom_handler);
|
||||||
|
|
||||||
ret = am_hal_iom_configure(data->IOMHandle, &data->iom_cfg);
|
ret = am_hal_iom_configure(data->iom_handler, &data->iom_cfg);
|
||||||
|
|
||||||
ret = am_hal_iom_enable(data->IOMHandle);
|
ret = am_hal_iom_enable(data->iom_handler);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spi_ambiq_xfer_half_duplex(const struct device *dev, am_hal_iom_dir_e dir,
|
||||||
|
am_hal_iom_transfer_t trans, bool cont)
|
||||||
|
{
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
struct spi_context *ctx = &data->ctx;
|
||||||
|
bool is_last = false;
|
||||||
|
uint32_t rem_num, cur_num = 0;
|
||||||
|
size_t count = 0;
|
||||||
|
int ret = 0;
|
||||||
|
spi_context_update_trx ctx_update;
|
||||||
|
|
||||||
|
if (dir == AM_HAL_IOM_FULLDUPLEX) {
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (dir == AM_HAL_IOM_RX) {
|
||||||
|
trans.eDirection = AM_HAL_IOM_RX;
|
||||||
|
count = ctx->rx_count;
|
||||||
|
ctx_update = spi_context_update_rx;
|
||||||
|
} else if (dir == AM_HAL_IOM_TX) {
|
||||||
|
trans.eDirection = AM_HAL_IOM_TX;
|
||||||
|
count = ctx->tx_count;
|
||||||
|
ctx_update = spi_context_update_tx;
|
||||||
|
}
|
||||||
|
/* Only instruction */
|
||||||
|
if ((!count) && (trans.ui32InstrLen)) {
|
||||||
|
trans.bContinue = cont;
|
||||||
|
#ifdef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
if (AM_HAL_STATUS_SUCCESS !=
|
||||||
|
am_hal_iom_nonblocking_transfer(data->iom_handler, &trans, spi_ambiq_callback,
|
||||||
|
(void *)dev)) {
|
||||||
|
spi_ambiq_reset(dev);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
ret = spi_context_wait_for_completion(ctx);
|
||||||
|
#else
|
||||||
|
ret = am_hal_iom_blocking_transfer(data->iom_handler, &trans);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (dir == AM_HAL_IOM_RX) {
|
||||||
|
rem_num = ctx->rx_len;
|
||||||
|
} else {
|
||||||
|
rem_num = ctx->tx_len;
|
||||||
|
}
|
||||||
|
while (rem_num) {
|
||||||
|
cur_num = (rem_num > AM_HAL_IOM_MAX_TXNSIZE_SPI)
|
||||||
|
? AM_HAL_IOM_MAX_TXNSIZE_SPI
|
||||||
|
: rem_num;
|
||||||
|
if ((i == (count - 1)) && (cur_num == rem_num)) {
|
||||||
|
is_last = true;
|
||||||
|
}
|
||||||
|
trans.bContinue = (is_last == true) ? cont : true;
|
||||||
|
trans.ui32NumBytes = cur_num;
|
||||||
|
trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf;
|
||||||
|
trans.pui32RxBuffer = (uint32_t *)ctx->rx_buf;
|
||||||
|
#ifdef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
if (AM_HAL_STATUS_SUCCESS !=
|
||||||
|
am_hal_iom_nonblocking_transfer(
|
||||||
|
data->iom_handler, &trans,
|
||||||
|
((is_last == true) ? spi_ambiq_callback : NULL),
|
||||||
|
(void *)dev)) {
|
||||||
|
spi_ambiq_reset(dev);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (is_last) {
|
||||||
|
ret = spi_context_wait_for_completion(ctx);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ret = am_hal_iom_blocking_transfer(data->iom_handler, &trans);
|
||||||
|
#endif
|
||||||
|
rem_num -= cur_num;
|
||||||
|
ctx_update(ctx, 1, cur_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_ambiq_xfer_full_duplex(const struct device *dev, am_hal_iom_dir_e dir,
|
||||||
|
am_hal_iom_transfer_t trans, bool cont)
|
||||||
|
{
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
struct spi_context *ctx = &data->ctx;
|
||||||
|
bool trx_once = (ctx->tx_len == ctx->rx_len);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dir != AM_HAL_IOM_FULLDUPLEX) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* Tx and Rx length must be the same for am_hal_iom_spi_blocking_fullduplex */
|
||||||
|
trans.eDirection = dir;
|
||||||
|
trans.ui32NumBytes = MIN(ctx->rx_len, ctx->tx_len);
|
||||||
|
trans.pui32RxBuffer = (uint32_t *)ctx->rx_buf;
|
||||||
|
trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf;
|
||||||
|
trans.bContinue = (trx_once) ? cont : true;
|
||||||
|
spi_context_update_tx(ctx, 1, trans.ui32NumBytes);
|
||||||
|
spi_context_update_rx(ctx, 1, trans.ui32NumBytes);
|
||||||
|
|
||||||
|
ret = am_hal_iom_spi_blocking_fullduplex(data->iom_handler, &trans);
|
||||||
|
|
||||||
|
/* Transfer the remaining bytes */
|
||||||
|
if (!trx_once) {
|
||||||
|
if (ctx->tx_len) {
|
||||||
|
trans.eDirection = AM_HAL_IOM_TX;
|
||||||
|
trans.ui32NumBytes = ctx->tx_len;
|
||||||
|
trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf;
|
||||||
|
} else if (ctx->rx_len) {
|
||||||
|
trans.eDirection = AM_HAL_IOM_RX;
|
||||||
|
trans.ui32NumBytes = ctx->rx_len;
|
||||||
|
trans.pui32RxBuffer = (uint32_t *)ctx->rx_buf;
|
||||||
|
}
|
||||||
|
trans.bContinue = cont;
|
||||||
|
ret = am_hal_iom_blocking_transfer(data->iom_handler, &trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_ambiq_fill_instruction(const struct device *dev, am_hal_iom_transfer_t *trans,
|
||||||
|
uint32_t len)
|
||||||
|
{
|
||||||
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
struct spi_context *ctx = &data->ctx;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The instruction length can only be:
|
||||||
|
* 0~AM_HAL_IOM_MAX_OFFSETSIZE.
|
||||||
|
* split transaction if oversize
|
||||||
|
*/
|
||||||
|
if (trans->ui32InstrLen + len > AM_HAL_IOM_MAX_OFFSETSIZE) {
|
||||||
|
ret = spi_ambiq_xfer_half_duplex(dev, AM_HAL_IOM_TX, *trans, true);
|
||||||
|
} else {
|
||||||
|
trans->ui32InstrLen += len;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
#if defined(CONFIG_SOC_SERIES_APOLLO3X)
|
||||||
|
trans->ui32Instr = (trans->ui32Instr << 8) | (*ctx->tx_buf);
|
||||||
|
#else
|
||||||
|
trans->ui64Instr = (trans->ui64Instr << 8) | (*ctx->tx_buf);
|
||||||
|
#endif
|
||||||
|
spi_context_update_tx(ctx, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config)
|
static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config)
|
||||||
{
|
{
|
||||||
struct spi_ambiq_data *data = dev->data;
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
const struct spi_ambiq_config *cfg = dev->config;
|
||||||
struct spi_context *ctx = &data->ctx;
|
struct spi_context *ctx = &data->ctx;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
bool cont = (config->operation & SPI_HOLD_ON_CS) ? true : false;
|
||||||
|
|
||||||
am_hal_iom_transfer_t trans = {0};
|
am_hal_iom_transfer_t trans = {0};
|
||||||
|
|
||||||
if (ctx->tx_len) {
|
/* TODO Need to get iom_nce from different nodes of spi */
|
||||||
trans.ui64Instr = *ctx->tx_buf;
|
#if defined(CONFIG_SOC_SERIES_APOLLO3X)
|
||||||
trans.ui32InstrLen = 1;
|
trans.uPeerInfo.ui32SpiChipSelect = cfg->pcfg->states->pins[SPI_CS_INDEX].iom_nce;
|
||||||
spi_context_update_tx(ctx, 1, 1);
|
#else
|
||||||
|
trans.uPeerInfo.ui32SpiChipSelect = cfg->pcfg->states->pins[SPI_CS_INDEX].iom_nce % 4;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ctx->rx_buf != NULL) {
|
/* There's data to send */
|
||||||
if (ctx->tx_len > 0) {
|
if (spi_context_tx_on(ctx)) {
|
||||||
/* The instruction length can only be 0~5. */
|
/* Always put the first byte to instuction */
|
||||||
if (ctx->tx_len > 4) {
|
ret = spi_ambiq_fill_instruction(dev, &trans, 1);
|
||||||
spi_context_complete(ctx, dev, 0);
|
/* There's data to Receive */
|
||||||
return -ENOTSUP;
|
if (spi_context_rx_on(ctx)) {
|
||||||
}
|
/* Regard the first tx_buf as cmd if there are more than one buffer */
|
||||||
|
if (ctx->rx_count > 1) {
|
||||||
/* Put the remaining TX data in instruction. */
|
ret = spi_ambiq_fill_instruction(dev, &trans, ctx->tx_len);
|
||||||
trans.ui32InstrLen += ctx->tx_len;
|
/* Skip the cmd buffer for rx. */
|
||||||
for (int i = 0; i < trans.ui32InstrLen - 1; i++) {
|
spi_context_update_rx(ctx, 1, ctx->rx_len);
|
||||||
trans.ui64Instr = (trans.ui64Instr << 8) | (*ctx->tx_buf);
|
|
||||||
spi_context_update_tx(ctx, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if ((!(config->operation & SPI_HALF_DUPLEX)) && (spi_context_tx_on(ctx))) {
|
||||||
/* Set RX direction and hold CS to continue to receive data. */
|
ret = spi_ambiq_xfer_full_duplex(dev, AM_HAL_IOM_FULLDUPLEX, trans,
|
||||||
trans.eDirection = AM_HAL_IOM_RX;
|
cont);
|
||||||
trans.bContinue = true;
|
} else {
|
||||||
trans.pui32RxBuffer = (uint32_t *)ctx->rx_buf;
|
ret = spi_ambiq_xfer_half_duplex(dev, AM_HAL_IOM_RX, trans,
|
||||||
trans.ui32NumBytes = ctx->rx_len;
|
cont);
|
||||||
ret = am_hal_iom_blocking_transfer(data->IOMHandle, &trans);
|
}
|
||||||
} else if (ctx->tx_buf != NULL) {
|
} else { /* There's no data to Receive */
|
||||||
/* Set TX direction to send data and release CS after transmission. */
|
/* Regard the first tx_buf as cmd if there are more than one buffer */
|
||||||
trans.eDirection = AM_HAL_IOM_TX;
|
if (ctx->tx_count > 1) {
|
||||||
trans.bContinue = false;
|
ret = spi_ambiq_fill_instruction(dev, &trans, ctx->tx_len);
|
||||||
trans.ui32NumBytes = ctx->tx_len;
|
}
|
||||||
trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf;
|
ret = spi_ambiq_xfer_half_duplex(dev, AM_HAL_IOM_TX, trans, cont);
|
||||||
ret = am_hal_iom_blocking_transfer(data->IOMHandle, &trans);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else { /* There's no data to send */
|
||||||
/* Set RX direction to receive data and release CS after transmission. */
|
ret = spi_ambiq_xfer_half_duplex(dev, AM_HAL_IOM_RX, trans, cont);
|
||||||
trans.ui64Instr = 0;
|
|
||||||
trans.ui32InstrLen = 0;
|
|
||||||
trans.eDirection = AM_HAL_IOM_RX;
|
|
||||||
trans.bContinue = false;
|
|
||||||
trans.pui32RxBuffer = (uint32_t *)ctx->rx_buf;
|
|
||||||
trans.ui32NumBytes = ctx->rx_len;
|
|
||||||
ret = am_hal_iom_blocking_transfer(data->IOMHandle, &trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_context_complete(ctx, dev, 0);
|
#ifndef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
if (!cont) {
|
||||||
|
spi_context_complete(ctx, dev, ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,28 +375,39 @@ static int spi_ambiq_transceive(const struct device *dev, const struct spi_confi
|
||||||
struct spi_ambiq_data *data = dev->data;
|
struct spi_ambiq_data *data = dev->data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!tx_bufs && !rx_bufs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* context setup */
|
||||||
|
spi_context_lock(&data->ctx, false, NULL, NULL, config);
|
||||||
|
|
||||||
ret = spi_config(dev, config);
|
ret = spi_config(dev, config);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
spi_context_release(&data->ctx, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tx_bufs && !rx_bufs) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
|
spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
|
||||||
|
|
||||||
ret = spi_ambiq_xfer(dev, config);
|
ret = spi_ambiq_xfer(dev, config);
|
||||||
|
|
||||||
|
spi_context_release(&data->ctx, ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_ambiq_release(const struct device *dev, const struct spi_config *config)
|
static int spi_ambiq_release(const struct device *dev, const struct spi_config *config)
|
||||||
{
|
{
|
||||||
struct spi_ambiq_data *data = dev->data;
|
struct spi_ambiq_data *data = dev->data;
|
||||||
|
am_hal_iom_status_t iom_status;
|
||||||
|
|
||||||
if (!sys_read32(SPI_STAT(dev))) {
|
am_hal_iom_status_get(data->iom_handler, &iom_status);
|
||||||
|
|
||||||
|
if ((iom_status.bStatIdle != IOM0_STATUS_IDLEST_IDLE) ||
|
||||||
|
(iom_status.bStatCmdAct == IOM0_STATUS_CMDACT_ACTIVE) ||
|
||||||
|
(iom_status.ui32NumPendTransactions)) {
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,14 +425,33 @@ static int spi_ambiq_init(const struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_ambiq_data *data = dev->data;
|
struct spi_ambiq_data *data = dev->data;
|
||||||
const struct spi_ambiq_config *cfg = dev->config;
|
const struct spi_ambiq_config *cfg = dev->config;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
ret = am_hal_iom_initialize((cfg->base - REG_IOM_BASEADDR) / cfg->size, &data->IOMHandle);
|
if (AM_HAL_STATUS_SUCCESS !=
|
||||||
|
am_hal_iom_initialize((cfg->base - REG_IOM_BASEADDR) / cfg->size, &data->iom_handler)) {
|
||||||
|
LOG_ERR("Fail to initialize SPI\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
ret = cfg->pwr_func();
|
ret = cfg->pwr_func();
|
||||||
|
|
||||||
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
ret |= pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Fail to config SPI pins\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPI_AMBIQ_DMA
|
||||||
|
am_hal_iom_interrupt_clear(data->iom_handler, AM_HAL_IOM_INT_CQUPD | AM_HAL_IOM_INT_ERR);
|
||||||
|
am_hal_iom_interrupt_enable(data->iom_handler, AM_HAL_IOM_INT_CQUPD | AM_HAL_IOM_INT_ERR);
|
||||||
|
cfg->irq_config_func();
|
||||||
|
#endif
|
||||||
|
end:
|
||||||
|
if (ret < 0) {
|
||||||
|
am_hal_iom_uninitialize(data->iom_handler);
|
||||||
|
} else {
|
||||||
|
spi_context_unlock_unconditionally(&data->ctx);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,14 +465,21 @@ static int spi_ambiq_init(const struct device *dev)
|
||||||
k_busy_wait(PWRCTRL_MAX_WAIT_US); \
|
k_busy_wait(PWRCTRL_MAX_WAIT_US); \
|
||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
|
static void spi_irq_config_func_##n(void) \
|
||||||
|
{ \
|
||||||
|
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), spi_ambiq_isr, \
|
||||||
|
DEVICE_DT_INST_GET(n), 0); \
|
||||||
|
irq_enable(DT_INST_IRQN(n)); \
|
||||||
|
}; \
|
||||||
static struct spi_ambiq_data spi_ambiq_data##n = { \
|
static struct spi_ambiq_data spi_ambiq_data##n = { \
|
||||||
SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx), \
|
SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx), \
|
||||||
SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx)}; \
|
SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx), .inst_idx = n}; \
|
||||||
static const struct spi_ambiq_config spi_ambiq_config##n = { \
|
static const struct spi_ambiq_config spi_ambiq_config##n = { \
|
||||||
.base = DT_INST_REG_ADDR(n), \
|
.base = DT_INST_REG_ADDR(n), \
|
||||||
.size = DT_INST_REG_SIZE(n), \
|
.size = DT_INST_REG_SIZE(n), \
|
||||||
.clock_freq = DT_INST_PROP(n, clock_frequency), \
|
.clock_freq = DT_INST_PROP(n, clock_frequency), \
|
||||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||||
|
.irq_config_func = spi_irq_config_func_##n, \
|
||||||
.pwr_func = pwr_on_ambiq_spi_##n}; \
|
.pwr_func = pwr_on_ambiq_spi_##n}; \
|
||||||
DEVICE_DT_INST_DEFINE(n, spi_ambiq_init, NULL, &spi_ambiq_data##n, &spi_ambiq_config##n, \
|
DEVICE_DT_INST_DEFINE(n, spi_ambiq_init, NULL, &spi_ambiq_data##n, &spi_ambiq_config##n, \
|
||||||
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, &spi_ambiq_driver_api);
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, &spi_ambiq_driver_api);
|
||||||
|
|
|
@ -160,6 +160,60 @@
|
||||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
spi0: spi@50004000 {
|
||||||
|
reg = <0x50004000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <6 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi1: spi@50005000 {
|
||||||
|
reg = <0x50005000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <7 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x4>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi2: spi@50006000 {
|
||||||
|
reg = <0x50006000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <8 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x8>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi3: spi@50007000 {
|
||||||
|
reg = <0x50007000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <9 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x10>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi4: spi@50008000 {
|
||||||
|
reg = <0x50008000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <10 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x20>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi5: spi@50009000 {
|
||||||
|
reg = <0x50009000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <11 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x40>;
|
||||||
|
};
|
||||||
|
|
||||||
i2c0: i2c@50004000 {
|
i2c0: i2c@50004000 {
|
||||||
reg = <0x50004000 0x1000>;
|
reg = <0x50004000 0x1000>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
|
|
@ -160,6 +160,60 @@
|
||||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
spi0: spi@50004000 {
|
||||||
|
reg = <0x50004000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <6 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi1: spi@50005000 {
|
||||||
|
reg = <0x50005000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <7 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x4>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi2: spi@50006000 {
|
||||||
|
reg = <0x50006000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <8 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x8>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi3: spi@50007000 {
|
||||||
|
reg = <0x50007000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <9 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x10>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi4: spi@50008000 {
|
||||||
|
reg = <0x50008000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <10 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x20>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi5: spi@50009000 {
|
||||||
|
reg = <0x50009000 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <11 0>;
|
||||||
|
status = "disabled";
|
||||||
|
ambiq,pwrcfg = <&pwrcfg 0x8 0x40>;
|
||||||
|
};
|
||||||
|
|
||||||
i2c0: i2c@50004000 {
|
i2c0: i2c@50004000 {
|
||||||
reg = <0x50004000 0x1000>;
|
reg = <0x50004000 0x1000>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue