From 7c09a0ad026c19256d7da630dce6159e0a953224 Mon Sep 17 00:00:00 2001 From: Nazar Palamar Date: Wed, 8 Jan 2025 17:23:22 +0200 Subject: [PATCH] drivers: spi: CAT1 SPI driver: add DMA support Add DMA support for CAT1 SPI driver Signed-off-by: Nazar Palamar --- drivers/spi/Kconfig.ifx_cat1 | 19 ++ drivers/spi/spi_ifx_cat1.c | 222 +++++++++++++++++- .../boards/cy8cproto_063_ble.overlay | 10 + 3 files changed, 243 insertions(+), 8 deletions(-) diff --git a/drivers/spi/Kconfig.ifx_cat1 b/drivers/spi/Kconfig.ifx_cat1 index e08dc5e13ccc..89cddbef9932 100644 --- a/drivers/spi/Kconfig.ifx_cat1 +++ b/drivers/spi/Kconfig.ifx_cat1 @@ -12,3 +12,22 @@ config SPI_INFINEON_CAT1 select GPIO help This option enables the SPI driver for Infineon CAT1 family. + + +if SPI_INFINEON_CAT1 + +config IFX_CAT1_SPI_DMA + bool "Infineon CAT1 SPI DMA Support" + default n + select DMA + help + Enable DMA during usage of SPI driver. + +config IFX_CAT1_SPI_DMA_TX_AUTO_TRIGGER + bool "Infineon CAT1 SPI Tx DMA channel trigger mechanism" + default y + select DMA + help + Automatically trigger SPI Tx DMA after config + +endif # SPI_INFINEON_CAT1 diff --git a/drivers/spi/spi_ifx_cat1.c b/drivers/spi/spi_ifx_cat1.c index bec34768556e..c767e52d196b 100644 --- a/drivers/spi/spi_ifx_cat1.c +++ b/drivers/spi/spi_ifx_cat1.c @@ -16,6 +16,11 @@ LOG_MODULE_REGISTER(cat1_spi); #include #include #include +#ifdef CONFIG_IFX_CAT1_SPI_DMA +#include + +#include +#endif #include #include @@ -26,6 +31,18 @@ LOG_MODULE_REGISTER(cat1_spi); #define IFX_CAT1_SPI_MIN_DATA_WIDTH (8) #define IFX_CAT1_SPI_MAX_DATA_WIDTH (32) +#ifdef CONFIG_IFX_CAT1_SPI_DMA +extern DW_Type * ifx_cat1_dma_get_regs(const struct device *dev); +extern int ifx_cat1_dma_ex_connect_digital(const struct device *dev, uint32_t channel, + cyhal_source_t source, cyhal_dma_input_t input); + +/* dummy buffers to be used by driver for DMA operations when app gives a NULL buffer + * during an asymmetric transfer + */ +static uint32_t tx_dummy_data; +static uint32_t rx_dummy_data; +#endif + /* Device config structure */ struct ifx_cat1_spi_config { CySCB_Type *reg_addr; @@ -34,6 +51,15 @@ struct ifx_cat1_spi_config { uint8_t irq_priority; }; +#ifdef CONFIG_IFX_CAT1_SPI_DMA +struct ifx_cat1_dma_stream { + const struct device *dev_dma; + uint32_t dma_channel; + struct dma_config dma_cfg; + struct dma_block_config blk_cfg; +}; +#endif + /* Data structure */ struct ifx_cat1_spi_data { struct spi_context ctx; @@ -41,15 +67,24 @@ struct ifx_cat1_spi_data { cyhal_resource_inst_t hw_resource; uint8_t dfs_value; size_t chunk_len; + bool dma_configured; + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + struct ifx_cat1_dma_stream dma_rx; + struct ifx_cat1_dma_stream dma_tx; +#endif }; static int32_t get_hw_block_num(CySCB_Type *reg_addr) { + extern const uint8_t _CYHAL_SCB_BASE_ADDRESS_INDEX[_SCB_ARRAY_SIZE]; + extern CySCB_Type *const _CYHAL_SCB_BASE_ADDRESSES[_SCB_ARRAY_SIZE]; + uint32_t i; for (i = 0u; i < _SCB_ARRAY_SIZE; i++) { if (_CYHAL_SCB_BASE_ADDRESSES[i] == reg_addr) { - return i; + return _CYHAL_SCB_BASE_ADDRESS_INDEX[i]; } } @@ -74,8 +109,8 @@ static void transfer_chunk(const struct device *dev) { struct ifx_cat1_spi_data *const data = dev->data; struct spi_context *ctx = &data->ctx; - int ret = 0; size_t chunk_len = spi_context_max_continuous_chunk(ctx); + int ret = 0; if (chunk_len == 0) { goto exit; @@ -83,12 +118,68 @@ static void transfer_chunk(const struct device *dev) data->chunk_len = chunk_len; +#ifdef CONFIG_IFX_CAT1_SPI_DMA + register struct ifx_cat1_dma_stream *dma_tx = &data->dma_tx; + register struct ifx_cat1_dma_stream *dma_rx = &data->dma_rx; + + if (spi_context_rx_buf_on(ctx)) { + dma_rx->blk_cfg.dest_address = (uint32_t)ctx->rx_buf; + dma_rx->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + } else { + dma_rx->blk_cfg.dest_address = (uint32_t)&rx_dummy_data; + dma_rx->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + } + + if (spi_context_tx_buf_on(ctx)) { + dma_tx->blk_cfg.source_address = (uint32_t)ctx->tx_buf; + dma_tx->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + + } else { + tx_dummy_data = 0; + dma_tx->blk_cfg.source_address = (uint32_t)&tx_dummy_data; + dma_tx->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + } + + dma_rx->blk_cfg.block_size = dma_tx->blk_cfg.block_size = chunk_len; + + ret = dma_config(dma_rx->dev_dma, dma_rx->dma_channel, &dma_rx->dma_cfg); + if (ret < 0) { + goto exit; + } + + /* The dma_start function for RX (PERIPHERAL_TO_MEMORY) enables + * the RX channel but does not trigger a DMA transfer. The RX DMA + * operation is started by the FIFO level trigger. + */ + ret = dma_start(dma_rx->dev_dma, dma_rx->dma_channel); + if (ret < 0) { + goto exit; + } + + ret = dma_config(dma_tx->dev_dma, dma_tx->dma_channel, &dma_tx->dma_cfg); + if (ret < 0) { + goto exit; + } + +#ifdef CONFIG_IFX_CAT1_SPI_DMA_TX_AUTO_TRIGGER + ret = dma_start(dma_tx->dev_dma, dma_tx->dma_channel); + if (ret == 0) { + return; + } +#else + if (ret == 0) { + data->dma_configured = 1; + return; + } +#endif +#else cy_rslt_t result = cyhal_spi_transfer_async( &data->obj, ctx->tx_buf, spi_context_tx_buf_on(ctx) ? chunk_len : 0, ctx->rx_buf, spi_context_rx_buf_on(ctx) ? chunk_len : 0); if (result == CY_RSLT_SUCCESS) { return; } +#endif ret = -EIO; @@ -97,6 +188,7 @@ static void transfer_chunk(const struct device *dev) spi_context_complete(ctx, dev, ret); } +#ifndef CONFIG_IFX_CAT1_SPI_DMA static void spi_interrupt_callback(void *arg, cyhal_spi_event_t event) { const struct device *dev = (const struct device *)arg; @@ -107,8 +199,6 @@ static void spi_interrupt_callback(void *arg, cyhal_spi_event_t event) #if defined(CONFIG_SPI_ASYNC) cyhal_spi_abort_async(&data->obj); #endif - spi_context_cs_control(ctx, false); - spi_context_complete(ctx, dev, -EIO); } if (event & CYHAL_SPI_IRQ_DONE) { @@ -118,6 +208,27 @@ static void spi_interrupt_callback(void *arg, cyhal_spi_event_t event) transfer_chunk(dev); } } +#endif + +#ifdef CONFIG_IFX_CAT1_SPI_DMA +static void dma_callback(const struct device *dma_dev, void *arg, uint32_t channel, int status) +{ + struct device *dev = arg; + struct ifx_cat1_spi_data *const data = dev->data; + struct spi_context *ctx = &data->ctx; + + if (channel == data->dma_rx.dma_channel) { + spi_context_update_tx(ctx, get_dfs_value(ctx), data->chunk_len); + spi_context_update_rx(ctx, get_dfs_value(ctx), data->chunk_len); + + transfer_chunk(dev); + } else if (channel == data->dma_tx.dma_channel) { + + } else { + printk("Unknown\n"); + } +} +#endif int spi_config(const struct device *dev, const struct spi_config *spi_cfg) { @@ -151,7 +262,10 @@ int spi_config(const struct device *dev, const struct spi_config *spi_cfg) return -EINVAL; } - if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE) { + /* Store spi config in context */ + ctx->config = spi_cfg; + + if (spi_context_is_slave(ctx)) { scb_spi_config.spiMode = CY_SCB_SPI_SLAVE; scb_spi_config.oversample = 0; scb_spi_config.enableMisoLateSample = false; @@ -206,7 +320,7 @@ int spi_config(const struct device *dev, const struct spi_config *spi_cfg) } /* Configure Slave select polarity */ - if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE) { + if (spi_context_is_slave(ctx)) { Cy_SCB_SPI_SetActiveSlaveSelectPolarity(data->obj.base, CY_SCB_SPI_SLAVE_SELECT0, scb_spi_config.ssPolarity); } @@ -220,9 +334,11 @@ int spi_config(const struct device *dev, const struct spi_config *spi_cfg) /* Write 0 when NULL buffer is provided for Tx/Rx */ data->obj.write_fill = 0; +#ifndef CONFIG_IFX_CAT1_SPI_DMA /* Register common SPI callback */ cyhal_spi_register_callback(&data->obj, spi_interrupt_callback, (void *)dev); cyhal_spi_enable_event(&data->obj, CYHAL_SPI_IRQ_DONE, config->irq_priority, true); +#endif /* Store spi config in context */ ctx->config = spi_cfg; @@ -283,6 +399,9 @@ static int ifx_cat1_spi_release(const struct device *dev, const struct spi_confi struct ifx_cat1_spi_data *const data = dev->data; cyhal_spi_free(&data->obj); +#ifdef CONFIG_IFX_CAT1_SPI_DMA + dma_stop(data->dma_tx.dev_dma, data->dma_tx.dma_channel); +#endif return 0; } @@ -298,7 +417,7 @@ static DEVICE_API(spi, ifx_cat1_spi_api) = { .release = ifx_cat1_spi_release, }; -static int ifx_cat1_spi_init(const struct device *dev) +int ifx_cat1_spi_init(const struct device *dev) { struct ifx_cat1_spi_data *const data = dev->data; const struct ifx_cat1_spi_config *const config = dev->config; @@ -308,6 +427,63 @@ static int ifx_cat1_spi_init(const struct device *dev) data->hw_resource.type = CYHAL_RSC_SCB; data->hw_resource.block_num = get_hw_block_num(config->reg_addr); + data->obj.resource = data->hw_resource; + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + if (data->dma_rx.dev_dma != NULL) { + cyhal_source_t spi_source; + + if (!device_is_ready(data->dma_rx.dev_dma)) { + return -ENODEV; + } + data->dma_rx.blk_cfg.source_address = (uint32_t)(&(config->reg_addr->RX_FIFO_RD)); + data->dma_rx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg; + data->dma_rx.dma_cfg.user_data = (void *)dev; + data->dma_rx.dma_cfg.dma_callback = dma_callback; + + if (cyhal_spi_enable_output(&data->obj, + CYHAL_SPI_OUTPUT_TRIGGER_RX_FIFO_LEVEL_REACHED, + &spi_source)) { + return -ENOTSUP; + } + + if (ifx_cat1_dma_ex_connect_digital(data->dma_rx.dev_dma, data->dma_rx.dma_channel, + spi_source, + CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)) { + return -ENOTSUP; + } + } + + if (data->dma_tx.dev_dma != NULL) { + cyhal_source_t spi_source; + + if (!device_is_ready(data->dma_tx.dev_dma)) { + return -ENODEV; + } + data->dma_tx.blk_cfg.dest_address = (uint32_t)(&(config->reg_addr->TX_FIFO_WR)); + data->dma_tx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg; + data->dma_tx.dma_cfg.user_data = (void *)dev; + data->dma_tx.dma_cfg.dma_callback = dma_callback; + + if (cyhal_spi_enable_output(&data->obj, + CYHAL_SPI_OUTPUT_TRIGGER_TX_FIFO_LEVEL_REACHED, + &spi_source)) { + return -ENOTSUP; + } + + if (ifx_cat1_dma_ex_connect_digital(data->dma_tx.dev_dma, data->dma_tx.dma_channel, + spi_source, + CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)) { + return -ENOTSUP; + } + } + +#endif + /* Configure dt provided device signals when available */ ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { @@ -322,12 +498,40 @@ static int ifx_cat1_spi_init(const struct device *dev) return 0; } +#if defined(CONFIG_IFX_CAT1_SPI_DMA) +#define SPI_DMA_CHANNEL_INIT(index, dir, ch_dir, src_data_size, dst_data_size) \ + .dev_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(index, dir)), \ + .dma_channel = DT_INST_DMAS_CELL_BY_NAME(index, dir, channel), \ + .dma_cfg = { \ + .channel_direction = ch_dir, \ + .source_data_size = src_data_size, \ + .dest_data_size = dst_data_size, \ + .source_burst_length = 0, \ + .dest_burst_length = 0, \ + .block_count = 1, \ + .complete_callback_en = 1, \ + }, + +#define SPI_DMA_CHANNEL(index, dir, ch_dir, src_data_size, dst_data_size) \ + .dma_##dir = {COND_CODE_1( \ + DT_INST_DMAS_HAS_NAME(index, dir), \ + (SPI_DMA_CHANNEL_INIT(index, dir, ch_dir, src_data_size, dst_data_size)), \ + (NULL))}, + +#else +#define SPI_DMA_CHANNEL(index, dir, ch_dir, src_data_size, dst_data_size) +#endif + #define IFX_CAT1_SPI_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ + \ static struct ifx_cat1_spi_data spi_cat1_data_##n = { \ SPI_CONTEXT_INIT_LOCK(spi_cat1_data_##n, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_cat1_data_##n, ctx), \ - SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx)}; \ + SPI_DMA_CHANNEL(n, tx, MEMORY_TO_PERIPHERAL, 1, 1) \ + SPI_DMA_CHANNEL(n, rx, PERIPHERAL_TO_MEMORY, 1, 1) \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx)}; \ + \ static struct ifx_cat1_spi_config spi_cat1_config_##n = { \ .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ @@ -342,6 +546,8 @@ static int ifx_cat1_spi_init(const struct device *dev) .oversample = IFX_CAT1_SPI_DEFAULT_OVERSAMPLE, \ .enableMisoLateSample = true, \ .ssPolarity = CY_SCB_SPI_ACTIVE_LOW, \ + .rxFifoTriggerLevel = 0, \ + .txFifoTriggerLevel = 1, \ }, \ .irq_priority = DT_INST_IRQ(n, priority), \ }; \ diff --git a/tests/drivers/spi/spi_loopback/boards/cy8cproto_063_ble.overlay b/tests/drivers/spi/spi_loopback/boards/cy8cproto_063_ble.overlay index e6ddcc3ef2ee..76ecb6e4fff9 100644 --- a/tests/drivers/spi/spi_loopback/boards/cy8cproto_063_ble.overlay +++ b/tests/drivers/spi/spi_loopback/boards/cy8cproto_063_ble.overlay @@ -1,3 +1,10 @@ +&dma0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; +}; + &pinctrl { /* Configure pin control bias mode for SPI pins (MASTER) */ p10_0_scb1_spi_m_mosi: p10_0_scb1_spi_m_mosi { @@ -35,6 +42,9 @@ spi: &scb1 { reg = <0>; spi-max-frequency = <1500000>; }; + + dmas = <&dma0 0>, <&dma0 1>; + dma-names = "tx", "rx"; }; &gpio_prt10 {