diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 6b855957db0..15ac978ec2f 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -41,6 +41,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_PW spi_pw.c) zephyr_library_sources_ifdef(CONFIG_SPI_SMARTBOND spi_smartbond.c) zephyr_library_sources_ifdef(CONFIG_SPI_OPENTITAN spi_opentitan.c) zephyr_library_sources_ifdef(CONFIG_SPI_NUMAKER spi_numaker.c) +zephyr_library_sources_ifdef(CONFIG_SPI_AMBIQ spi_ambiq.c) zephyr_library_sources_ifdef(CONFIG_SPI_RTIO spi_rtio.c) zephyr_library_sources_ifdef(CONFIG_SPI_ASYNC spi_signal.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 71a658c3e88..92bb211b4cd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -129,4 +129,6 @@ source "drivers/spi/Kconfig.opentitan" source "drivers/spi/Kconfig.numaker" +source "drivers/spi/Kconfig.ambiq" + endif # SPI diff --git a/drivers/spi/Kconfig.ambiq b/drivers/spi/Kconfig.ambiq new file mode 100644 index 00000000000..130b6575136 --- /dev/null +++ b/drivers/spi/Kconfig.ambiq @@ -0,0 +1,15 @@ +# Ambiq SDK SPI +# +# Copyright (c) 2023 Antmicro +# +# SPDX-License-Identifier: Apache-2.0 +# + +config SPI_AMBIQ + bool "AMBIQ SPI driver" + default y + depends on DT_HAS_AMBIQ_SPI_ENABLED + select AMBIQ_HAL + select AMBIQ_HAL_USE_SPI + help + Enable driver for Ambiq SPI. diff --git a/drivers/spi/spi_ambiq.c b/drivers/spi/spi_ambiq.c new file mode 100644 index 00000000000..facfc09b035 --- /dev/null +++ b/drivers/spi/spi_ambiq.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2023 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ambiq_spi + +#include +LOG_MODULE_REGISTER(spi_ambiq); + +#include +#include +#include +#include +#include +#include +#include "spi_context.h" +#include + +#define PWRCTRL_MAX_WAIT_US 5 + +typedef int (*ambiq_spi_pwr_func_t)(void); + +struct spi_ambiq_config { + uint32_t base; + int size; + uint32_t clock_freq; + const struct pinctrl_dev_config *pcfg; + ambiq_spi_pwr_func_t pwr_func; +}; + +struct spi_ambiq_data { + struct spi_context ctx; + am_hal_iom_config_t iom_cfg; + void *IOMHandle; +}; + +#define SPI_BASE (((const struct spi_ambiq_config *)(dev)->config)->base) +#define REG_STAT 0x248 +#define IDLE_STAT 0x4 +#define SPI_STAT(dev) (SPI_BASE + REG_STAT) +#define SPI_WORD_SIZE 8 + +static int spi_config(const struct device *dev, const struct spi_config *config) +{ + struct spi_ambiq_data *data = dev->data; + const struct spi_ambiq_config *cfg = dev->config; + + data->iom_cfg.eInterfaceMode = AM_HAL_IOM_SPI_MODE; + + int ret = 0; + + if (config->operation & SPI_HALF_DUPLEX) { + 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); + return -ENOTSUP; + } + + if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { + LOG_ERR("Only supports single mode"); + return -ENOTSUP; + } + + if (config->operation & SPI_LOCK_ON) { + LOG_ERR("Lock On not supported"); + 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)) { + if (config->operation & (SPI_MODE_CPOL && SPI_MODE_CPHA)) { + data->iom_cfg.eSpiMode = AM_HAL_IOM_SPI_MODE_3; + } else if (config->operation & SPI_MODE_CPOL) { + data->iom_cfg.eSpiMode = AM_HAL_IOM_SPI_MODE_2; + } else if (config->operation & SPI_MODE_CPHA) { + data->iom_cfg.eSpiMode = AM_HAL_IOM_SPI_MODE_1; + } else { + data->iom_cfg.eSpiMode = AM_HAL_IOM_SPI_MODE_0; + } + } + + if (config->operation & SPI_OP_MODE_SLAVE) { + LOG_ERR("Slave mode not supported"); + return -ENOTSUP; + } + if (config->operation & SPI_MODE_LOOP) { + LOG_ERR("Loopback mode not supported"); + return -ENOTSUP; + } + + if (cfg->clock_freq > AM_HAL_IOM_MAX_FREQ) { + LOG_ERR("Clock frequency too high"); + return -ENOTSUP; + } + + data->iom_cfg.ui32ClockFreq = cfg->clock_freq; + + /* Disable IOM instance as it cannot be configured when enabled*/ + ret = am_hal_iom_disable(data->IOMHandle); + + ret = am_hal_iom_configure(data->IOMHandle, &data->iom_cfg); + + ret = am_hal_iom_enable(data->IOMHandle); + + return ret; +} + +static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config) +{ + struct spi_ambiq_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + int ret = 0; + + am_hal_iom_transfer_t trans = {0}; + uint8_t *buf3; + + trans.eDirection = AM_HAL_IOM_FULLDUPLEX; + + uint16_t cmd = *ctx->tx_buf; + + trans.ui32InstrLen = ctx->tx_len; + spi_context_update_tx(ctx, 1, 1); + cmd = __bswap_16(cmd | *ctx->tx_buf << 8); + trans.ui64Instr = cmd; + + if (ctx->rx_buf != NULL) { + trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf; + spi_context_update_rx(ctx, 1, ctx->rx_len); + buf3 = (uint8_t *)malloc(sizeof(uint8_t) * ctx->rx_len); + trans.ui32NumBytes = ctx->rx_len; + trans.pui32RxBuffer = (uint32_t *)&buf3; + ret = am_hal_iom_spi_blocking_fullduplex(data->IOMHandle, &trans); + + memcpy(ctx->rx_buf, &buf3, (ctx->rx_len * sizeof(uint8_t))); + + } else if (ctx->tx_buf != NULL) { + spi_context_update_tx(ctx, 1, 1); + trans.ui32NumBytes = ctx->tx_len; + trans.pui32TxBuffer = (uint32_t *)ctx->tx_buf; + trans.pui32RxBuffer = (uint32_t *)ctx->tx_buf; + ret = am_hal_iom_spi_blocking_fullduplex(data->IOMHandle, &trans); + } + + spi_context_complete(ctx, dev, 0); + + return ret; +} + +static int spi_ambiq_transceive(const struct device *dev, const struct spi_config *config, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + struct spi_ambiq_data *data = dev->data; + int ret; + + ret = spi_config(dev, config); + + if (ret) { + return ret; + } + + if (!tx_bufs && !rx_bufs) { + return 0; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); + + ret = spi_ambiq_xfer(dev, config); + + return ret; +} + +static int spi_ambiq_release(const struct device *dev, const struct spi_config *config) +{ + struct spi_ambiq_data *data = dev->data; + + if (!sys_read32(SPI_STAT(dev))) { + return -EBUSY; + } + + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static struct spi_driver_api spi_ambiq_driver_api = { + .transceive = spi_ambiq_transceive, + .release = spi_ambiq_release, +}; + +static int spi_ambiq_init(const struct device *dev) +{ + struct spi_ambiq_data *data = dev->data; + const struct spi_ambiq_config *cfg = dev->config; + int ret; + + ret = am_hal_iom_initialize((cfg->base - REG_IOM_BASEADDR) / cfg->size, &data->IOMHandle); + + ret = cfg->pwr_func(); + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + + return ret; +} + +#define AMBIQ_SPI_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static int pwr_on_ambiq_spi_##n(void) \ + { \ + uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \ + DT_INST_PHA(n, ambiq_pwrcfg, offset); \ + sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr); \ + k_busy_wait(PWRCTRL_MAX_WAIT_US); \ + return 0; \ + } \ + static struct spi_ambiq_data spi_ambiq_data##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx)}; \ + static const struct spi_ambiq_config spi_ambiq_config##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .size = DT_INST_REG_SIZE(n), \ + .clock_freq = DT_INST_PROP(n, clock_frequency), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(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, \ + POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, &spi_ambiq_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(AMBIQ_SPI_INIT) diff --git a/dts/bindings/spi/ambiq,spi.yaml b/dts/bindings/spi/ambiq,spi.yaml new file mode 100644 index 00000000000..425c7fc44ee --- /dev/null +++ b/dts/bindings/spi/ambiq,spi.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +description: Ambiq SPI + +compatible: "ambiq,spi" + +include: [spi-controller.yaml, pinctrl-device.yaml, ambiq-pwrcfg.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + clock-frequency: + required: true + + ambiq,pwrcfg: + required: true diff --git a/modules/hal_ambiq/Kconfig b/modules/hal_ambiq/Kconfig index 791472dedf9..b6029f0592c 100644 --- a/modules/hal_ambiq/Kconfig +++ b/modules/hal_ambiq/Kconfig @@ -35,4 +35,9 @@ config AMBIQ_HAL_USE_I2C help Use the I2C driver from Ambiq HAL +config AMBIQ_HAL_USE_SPI + bool + help + Use the SPI driver from Ambiq HAL + endif # AMBIQ_HAL diff --git a/west.yml b/west.yml index 3d5fa9be2c7..3159278cfb4 100644 --- a/west.yml +++ b/west.yml @@ -142,7 +142,7 @@ manifest: groups: - hal - name: hal_ambiq - revision: 446736a1c36199d1c8b9b2c4b3d14bf27006d440 + revision: fbb1618df8b0946cc2abea817309dc85fe051c21 path: modules/hal/ambiq groups: - hal