From 792578bc479b13f552f14df1b768eb7a11edf03e Mon Sep 17 00:00:00 2001 From: Hao Luo Date: Fri, 2 Aug 2024 17:01:41 +0800 Subject: [PATCH] drivers: spi: Add SPI device support for Apollo3 SoCs This commit adds spi device support for Apollo3 SoCs. Signed-off-by: Hao Luo --- .../apollo3_evb/apollo3_evb-pinctrl.dtsi | 6 +- .../apollo3p_evb/apollo3p_evb-pinctrl.dtsi | 6 +- drivers/spi/CMakeLists.txt | 3 +- drivers/spi/Kconfig.ambiq | 24 +- drivers/spi/{spi_ambiq.c => spi_ambiq_spim.c} | 2 - drivers/spi/spi_ambiq_spis.c | 381 ++++++++++++++++++ dts/arm/ambiq/ambiq_apollo3_blue.dtsi | 11 + dts/arm/ambiq/ambiq_apollo3p_blue.dtsi | 11 + dts/bindings/spi/ambiq,spis.yaml | 15 + modules/hal_ambiq/Kconfig | 9 +- west.yml | 2 +- 11 files changed, 455 insertions(+), 15 deletions(-) rename drivers/spi/{spi_ambiq.c => spi_ambiq_spim.c} (99%) create mode 100644 drivers/spi/spi_ambiq_spis.c create mode 100644 dts/bindings/spi/ambiq,spis.yaml diff --git a/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi b/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi index fcde064a92c0f99..331a60bf7de9c2e 100644 --- a/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi +++ b/boards/ambiq/apollo3_evb/apollo3_evb-pinctrl.dtsi @@ -64,7 +64,11 @@ bias-pull-up; }; }; - + spis0_default: spis0_default { + group1 { + pinmux = , , , ; + }; + }; spi0_default: spi0_default { group1 { pinmux = , , ; diff --git a/boards/ambiq/apollo3p_evb/apollo3p_evb-pinctrl.dtsi b/boards/ambiq/apollo3p_evb/apollo3p_evb-pinctrl.dtsi index 67241cce53359a1..2bf67298629d866 100644 --- a/boards/ambiq/apollo3p_evb/apollo3p_evb-pinctrl.dtsi +++ b/boards/ambiq/apollo3p_evb/apollo3p_evb-pinctrl.dtsi @@ -64,7 +64,11 @@ bias-pull-up; }; }; - + spis0_default: spis0_default { + group1 { + pinmux = , , , ; + }; + }; spi0_default: spi0_default { group1 { pinmux = , , ; diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index a4eb01ef8e706fc..3cb486dee40fea8 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -45,7 +45,8 @@ 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_AMBIQ_SPIM spi_ambiq_spim.c) +zephyr_library_sources_ifdef(CONFIG_SPI_AMBIQ_SPIS spi_ambiq_spis.c) zephyr_library_sources_ifdef(CONFIG_SPI_AMBIQ_BLEIF spi_ambiq_bleif.c) zephyr_library_sources_ifdef(CONFIG_SPI_RPI_PICO_PIO spi_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_SPI_MCHP_MSS spi_mchp_mss.c) diff --git a/drivers/spi/Kconfig.ambiq b/drivers/spi/Kconfig.ambiq index 17fb6cbd6792bf5..20fb6dc13ecdaf6 100644 --- a/drivers/spi/Kconfig.ambiq +++ b/drivers/spi/Kconfig.ambiq @@ -6,17 +6,17 @@ # SPDX-License-Identifier: Apache-2.0 # -menuconfig SPI_AMBIQ - bool "AMBIQ SPI driver" +menuconfig SPI_AMBIQ_SPIM + bool "AMBIQ SPI Master driver" default y - depends on DT_HAS_AMBIQ_SPI_ENABLED + depends on DT_HAS_AMBIQ_SPIM_ENABLED select GPIO select AMBIQ_HAL - select AMBIQ_HAL_USE_SPI + select AMBIQ_HAL_USE_SPIM help - Enable driver for Ambiq SPI. + Enable driver for Ambiq SPI Master. -if SPI_AMBIQ +if SPI_AMBIQ_SPIM config SPI_AMBIQ_DMA bool "AMBIQ APOLLO SPI DMA Support" @@ -31,7 +31,17 @@ config SPI_DMA_TCB_BUFFER_SIZE help DMA Transfer Control Buffer size in words -endif # SPI_AMBIQ +endif # SPI_AMBIQ_SPIM + +config SPI_AMBIQ_SPIS + bool "AMBIQ SPI Slave driver" + default y + depends on DT_HAS_AMBIQ_SPIS_ENABLED + select GPIO + select AMBIQ_HAL + select AMBIQ_HAL_USE_SPIS + help + Enable driver for Ambiq SPI Slave. config SPI_AMBIQ_BLEIF bool "AMBIQ SPI-BLEIF driver" diff --git a/drivers/spi/spi_ambiq.c b/drivers/spi/spi_ambiq_spim.c similarity index 99% rename from drivers/spi/spi_ambiq.c rename to drivers/spi/spi_ambiq_spim.c index 384366c60be1434..cfaf7d18f97d544 100644 --- a/drivers/spi/spi_ambiq.c +++ b/drivers/spi/spi_ambiq_spim.c @@ -47,8 +47,6 @@ typedef void (*spi_context_update_trx)(struct spi_context *ctx, uint8_t dfs, uin #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]; diff --git a/drivers/spi/spi_ambiq_spis.c b/drivers/spi/spi_ambiq_spis.c new file mode 100644 index 000000000000000..0efbc508daed405 --- /dev/null +++ b/drivers/spi/spi_ambiq_spis.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2024 Ambiq + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ambiq_spis + +#include +LOG_MODULE_REGISTER(spi_ambiq_spis); + +#include +#include +#include +#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 { + const struct gpio_dt_spec int_gpios; + uint32_t base; + int size; + const struct pinctrl_dev_config *pcfg; + ambiq_spi_pwr_func_t pwr_func; + void (*irq_config_func)(void); +}; + +struct spi_ambiq_data { + struct spi_context ctx; + am_hal_ios_config_t ios_cfg; + void *ios_handler; + int inst_idx; + struct k_sem spim_wrcmp_sem; +}; + +#define AMBIQ_SPIS_TX_BUFSIZE_MAX 1023 +uint8_t g_pui8TxFifoBuffer[AMBIQ_SPIS_TX_BUFSIZE_MAX]; + +#define SPI_WORD_SIZE 8 + +#define FIFO_BASE 0x78 +#define FIFO_END 0x100 +#define FIFO_LENGTH (FIFO_END - FIFO_BASE) + +#define AMBIQ_SPIS_INT_ERR (AM_HAL_IOS_INT_FOVFL | AM_HAL_IOS_INT_FUNDFL | AM_HAL_IOS_INT_FRDERR) + +#define AMBIQ_SPIS_XCMP_INT \ + (AM_HAL_IOS_INT_XCMPWR | AM_HAL_IOS_INT_XCMPWF | AM_HAL_IOS_INT_XCMPRR | \ + AM_HAL_IOS_INT_XCMPRF) + +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_ios_disable(data->ios_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); +} + +static void spi_ambiq_isr(const struct device *dev) +{ + uint32_t ui32Status; + struct spi_ambiq_data *data = dev->data; + + am_hal_ios_interrupt_status_get(data->ios_handler, false, &ui32Status); + am_hal_ios_interrupt_clear(data->ios_handler, ui32Status); + if (ui32Status & AM_HAL_IOS_INT_XCMPWR) { + k_sem_give(&data->spim_wrcmp_sem); + } +} + +static int spi_config(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; + + data->ios_cfg.ui32InterfaceSelect = AM_HAL_IOS_USE_SPI; + + if (spi_context_configured(ctx, config)) { + /* Already configured. No need to do it again. */ + return 0; + } + + if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) { + 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) { + if (config->operation & SPI_MODE_CPHA) { + data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_3; + } else { + data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_2; + } + } else { + if (config->operation & SPI_MODE_CPHA) { + data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_1; + } else { + data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_0; + } + } + + if (config->operation & SPI_OP_MODE_MASTER) { + LOG_ERR("Master mode not supported"); + return -ENOTSUP; + } + if (config->operation & SPI_MODE_LOOP) { + LOG_ERR("Loopback mode not supported"); + return -ENOTSUP; + } + + if (spi_cs_is_gpio(config)) { + LOG_ERR("CS control via GPIO is not supported"); + return -EINVAL; + } + + /* Eliminate the "read-only" section, so an external host can use the + * entire "direct write" section. + */ + data->ios_cfg.ui32ROBase = FIFO_BASE; + /* Making the "FIFO" section as big as possible. */ + data->ios_cfg.ui32FIFOBase = FIFO_BASE; + /* We don't need any RAM space, so extend the FIFO all the way to the end + * of the LRAM. + */ + data->ios_cfg.ui32RAMBase = FIFO_END; + /* FIFO Threshold - set to half the size */ + data->ios_cfg.ui32FIFOThreshold = FIFO_LENGTH >> 1; + + data->ios_cfg.pui8SRAMBuffer = g_pui8TxFifoBuffer, + data->ios_cfg.ui32SRAMBufferCap = AMBIQ_SPIS_TX_BUFSIZE_MAX, + + ctx->config = config; + + ret = am_hal_ios_configure(data->ios_handler, &data->ios_cfg); + + return ret; +} + +static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config) +{ + const struct spi_ambiq_config *cfg = dev->config; + struct spi_ambiq_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + int ret = 0; + uint32_t chunk, num_written, num_read = 0; + + while (1) { + if (spi_context_tx_buf_on(ctx)) { + /* Inform the host */ + gpio_pin_set_dt(&cfg->int_gpios, 1); + k_sleep(K_USEC(100)); + gpio_pin_set_dt(&cfg->int_gpios, 0); + /* Copy data into FIFO */ + chunk = (ctx->tx_len > AMBIQ_SPIS_TX_BUFSIZE_MAX) + ? AMBIQ_SPIS_TX_BUFSIZE_MAX + : ctx->tx_len; + ret = am_hal_ios_fifo_write(data->ios_handler, (uint8_t*)ctx->tx_buf, chunk, + &num_written); + if (ret != 0) { + spi_ambiq_reset(dev); + LOG_ERR("SPIS write error: %d", ret); + return ret; + } + spi_context_update_tx(ctx, 1, num_written); + if (!spi_context_tx_buf_on(ctx)) { + break; + } + } else if (spi_context_rx_buf_on(ctx)) { + /* Wait for host write complete interrupt */ + (void)k_sem_take(&data->spim_wrcmp_sem, K_FOREVER); + /* Read out the first byte as packet length */ + num_read = am_hal_ios_pui8LRAM[0]; + if (ctx->rx_len < num_read) { + LOG_ERR("SPIS read buffer inadequate!"); + return -ENOMEM; + } + /* Read data from LRAM */ + memcpy(ctx->rx_buf, (uint8_t*)&am_hal_ios_pui8LRAM[1], num_read); + spi_context_update_rx(ctx, 1, num_read); + break; + } + } + + spi_context_complete(ctx, dev, ret); + 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; + + if (!tx_bufs && !rx_bufs) { + return 0; + } + + ret = pm_device_runtime_get(dev); + + if (ret < 0) { + LOG_ERR("pm_device_runtime_get failed: %d", ret); + } + + /* context setup */ + spi_context_lock(&data->ctx, false, NULL, NULL, config); + + ret = spi_config(dev, config); + + if (ret) { + spi_context_release(&data->ctx, ret); + return ret; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); + + ret = spi_ambiq_xfer(dev, config); + + spi_context_release(&data->ctx, ret); + + /* Use async put to avoid useless device suspension/resumption + * when doing consecutive transmission. + */ + ret = pm_device_runtime_put_async(dev, K_MSEC(2)); + + if (ret < 0) { + LOG_ERR("pm_device_runtime_put failed: %d", ret); + } + + return ret; +} + +static int spi_ambiq_release(const struct device *dev, const struct spi_config *config) +{ + struct spi_ambiq_data *data = dev->data; + + if (!spi_context_configured(&data->ctx, config)) { + return -EINVAL; + } + + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static const 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 = 0; + + if (AM_HAL_STATUS_SUCCESS != + am_hal_ios_initialize((cfg->base - REG_IOSLAVE_BASEADDR) / cfg->size, + &data->ios_handler)) { + LOG_ERR("Fail to initialize SPIS\n"); + return -ENXIO; + } + + ret = cfg->pwr_func(); + + ret |= pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Fail to config SPIS pins\n"); + goto end; + } + + am_hal_ios_interrupt_clear(data->ios_handler, AM_HAL_IOS_INT_ALL); + am_hal_ios_interrupt_enable(data->ios_handler, AMBIQ_SPIS_INT_ERR | AM_HAL_IOS_INT_IOINTW | + AMBIQ_SPIS_XCMP_INT); + cfg->irq_config_func(); + +end: + if (ret < 0) { + am_hal_ios_uninitialize(data->ios_handler); + } else { + spi_context_unlock_unconditionally(&data->ctx); + } + return ret; +} + +#ifdef CONFIG_PM_DEVICE +static int spi_ambiq_pm_action(const struct device *dev, enum pm_device_action action) +{ + struct spi_ambiq_data *data = dev->data; + uint32_t ret; + am_hal_sysctrl_power_state_e status; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + status = AM_HAL_SYSCTRL_WAKE; + break; + case PM_DEVICE_ACTION_SUSPEND: + status = AM_HAL_SYSCTRL_DEEPSLEEP; + break; + default: + return -ENOTSUP; + } + + ret = am_hal_ios_power_ctrl(data->ios_handler, status, true); + + if (ret != AM_HAL_STATUS_SUCCESS) { + LOG_ERR("am_hal_ios_power_ctrl failed: %d", ret); + return -EPERM; + } else { + return 0; + } +} +#endif /* CONFIG_PM_DEVICE */ + +#define AMBIQ_SPIS_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 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 = { \ + SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx), \ + .spim_wrcmp_sem = Z_SEM_INITIALIZER(spi_ambiq_data##n.spim_wrcmp_sem, 0, 1), \ + .inst_idx = n}; \ + static const struct spi_ambiq_config spi_ambiq_config##n = { \ + .int_gpios = GPIO_DT_SPEC_INST_GET(n, int_gpios), \ + .base = DT_INST_REG_ADDR(n), \ + .size = DT_INST_REG_SIZE(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .irq_config_func = spi_irq_config_func_##n, \ + .pwr_func = pwr_on_ambiq_spi_##n}; \ + PM_DEVICE_DT_INST_DEFINE(n, spi_ambiq_pm_action); \ + DEVICE_DT_INST_DEFINE(n, spi_ambiq_init, PM_DEVICE_DT_INST_GET(n), &spi_ambiq_data##n, \ + &spi_ambiq_config##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &spi_ambiq_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(AMBIQ_SPIS_INIT) diff --git a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi index de76a84fc40051c..821b9e75c6f5388 100644 --- a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi +++ b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi @@ -189,6 +189,17 @@ zephyr,pm-device-runtime-auto; }; + spis0: spi@50000100 { + compatible = "ambiq,spis"; + reg = <0x50000100 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <4 0>; + status = "disabled"; + ambiq,pwrcfg = <&pwrcfg 0x8 0>; + zephyr,pm-device-runtime-auto; + }; + spi0: spi@50004000 { reg = <0x50004000 0x1000>; #address-cells = <1>; diff --git a/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi b/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi index e41e17f61df59f3..a43356460ca5d7c 100644 --- a/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi +++ b/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi @@ -207,6 +207,17 @@ zephyr,pm-device-runtime-auto; }; + spis0: spi@50000100 { + compatible = "ambiq,spis"; + reg = <0x50000100 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <4 0>; + status = "disabled"; + ambiq,pwrcfg = <&pwrcfg 0x8 0>; + zephyr,pm-device-runtime-auto; + }; + spi0: spi@50004000 { reg = <0x50004000 0x1000>; #address-cells = <1>; diff --git a/dts/bindings/spi/ambiq,spis.yaml b/dts/bindings/spi/ambiq,spis.yaml new file mode 100644 index 000000000000000..dd0c1952f9c3e3c --- /dev/null +++ b/dts/bindings/spi/ambiq,spis.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Ambiq +# SPDX-License-Identifier: Apache-2.0 + +description: Ambiq SPI Slave + +compatible: "ambiq,spis" + +include: [spi-controller.yaml, pinctrl-device.yaml, ambiq-pwrcfg.yaml] + +properties: + int-gpios: + type: phandle-array + required: true + description: | + GPIO that interrupts the SPI master to require operation diff --git a/modules/hal_ambiq/Kconfig b/modules/hal_ambiq/Kconfig index 326d6bac2d45159..7b72b6201b0f7bb 100644 --- a/modules/hal_ambiq/Kconfig +++ b/modules/hal_ambiq/Kconfig @@ -35,10 +35,15 @@ config AMBIQ_HAL_USE_I2C help Use the I2C driver from Ambiq HAL -config AMBIQ_HAL_USE_SPI +config AMBIQ_HAL_USE_SPIM bool help - Use the SPI driver from Ambiq HAL + Use the SPI Master driver from Ambiq HAL + +config AMBIQ_HAL_USE_SPIS + bool + help + Use the SPI Slave driver from Ambiq HAL config AMBIQ_HAL_USE_MSPI bool diff --git a/west.yml b/west.yml index 17f37a95b50feb9..7398c37edaf342b 100644 --- a/west.yml +++ b/west.yml @@ -147,7 +147,7 @@ manifest: groups: - hal - name: hal_ambiq - revision: df4a9863f87cf75dc0b4a8e47483c87363d0e8f0 + revision: 3b4f15f785923b7226c57bb8a96c546c15ec9895 path: modules/hal/ambiq groups: - hal