diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7ce8da0..1f79f11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,15 @@ jobs: -DEXTRA_CONF_FILE=$(pwd)/tests/drivers/dma/chan_blen_transfer/boards/siwx917_rb4338a.conf \ -v --inline-logs + - name: Build Scatter-Gather DMA test + working-directory: zephyr-silabs + shell: bash + run: | + west twister -s drivers.dma.scatter_gather -p siwx917_rb4338a -- \ + -DTC_OVERLAY_FILE=$(pwd)/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay \ + -DEXTRA_CONF_FILE=$(pwd)/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf \ + -v --inline-logs + - name: Build Bluetooth samples working-directory: zephyr-silabs shell: bash diff --git a/.github/workflows/upstream-build.yml b/.github/workflows/upstream-build.yml index 1f357bf..38658da 100644 --- a/.github/workflows/upstream-build.yml +++ b/.github/workflows/upstream-build.yml @@ -61,6 +61,15 @@ jobs: -DEXTRA_CONF_FILE=$(pwd)/tests/drivers/dma/chan_blen_transfer/boards/siwx917_rb4338a.conf \ -v --inline-logs + - name: Build Scatter-Gather DMA test + working-directory: zephyr-silabs + shell: bash + run: | + west twister -s drivers.dma.scatter_gather -p siwx917_rb4338a -- \ + -DTC_OVERLAY_FILE=$(pwd)/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay \ + -DEXTRA_CONF_FILE=$(pwd)/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf \ + -v --inline-logs + - name: Build Bluetooth samples working-directory: zephyr-silabs shell: bash diff --git a/drivers/dma/Kconfig.siwx917 b/drivers/dma/Kconfig.siwx917 index 112787d..51b4d94 100644 --- a/drivers/dma/Kconfig.siwx917 +++ b/drivers/dma/Kconfig.siwx917 @@ -12,3 +12,8 @@ config DMA_SILABS_SIWX917_COMMON_INIT_PRIORITY int "Common initialization priority" depends on DMA_SILABS_SIWX917 default 42 + +config SG_BUFFER_COUNT + int "The maximum allowable number of buffers for scatter-gather transfers" + depends on DMA_SILABS_SIWX917 + default 10 diff --git a/drivers/dma/dma_silabs_siwx917.c b/drivers/dma/dma_silabs_siwx917.c index c404fe3..206cf05 100644 --- a/drivers/dma/dma_silabs_siwx917.c +++ b/drivers/dma/dma_silabs_siwx917.c @@ -12,18 +12,26 @@ #include #include #include "rsi_rom_udma_wrapper.h" +#include "rsi_rom_udma.h" #include "rsi_udma.h" #include "sl_status.h" -#define DT_DRV_COMPAT silabs_siwx917_dma -#define DMA_MAX_TRANSFER_COUNT 1024 -#define DMA_CH_PRIORITY_HIGH 1 -#define DMA_CH_PRIORITY_LOW 0 -#define VALID_BURST_LENGTH 0 -#define UDMA_ADDR_INC_NONE 0X03 +#define DT_DRV_COMPAT silabs_siwx917_dma +#define DMA_MAX_TRANSFER_COUNT 1024 +#define DMA_CH_PRIORITY_HIGH 1 +#define DMA_CH_PRIORITY_LOW 0 +#define VALID_BURST_LENGTH 0 +#define UDMA_ADDR_INC_NONE 0X03 +#define UDMA_MODE_PER_ALT_SCATTER_GATHER 0x07 LOG_MODULE_REGISTER(si91x_dma, CONFIG_DMA_LOG_LEVEL); +struct dma_sg_descriptor_allocator { + RSI_UDMA_DESC_T sg_transfer_desc_table[CONFIG_SG_BUFFER_COUNT]; /* DMA descriptors in + contiguous memory */ + uint8_t free_desc[CONFIG_SG_BUFFER_COUNT]; /* Allocation status of descriptors */ +}; + struct dma_siwx917_config { UDMA0_Type *reg; /* UDMA register base address */ uint8_t channels; /* UDMA channel count */ @@ -34,8 +42,10 @@ struct dma_siwx917_config { struct dma_siwx917_data { UDMA_Channel_Info *chan_info; - dma_callback_t dma_callback; /* User callback */ - void *cb_data; /* User callback data */ + dma_callback_t dma_callback; /* User callback */ + void *cb_data; /* User callback data */ + struct dma_sg_descriptor_allocator + *sg_transfer_desc_block; /* Pointer to scatter-gather descriptors block */ RSI_UDMA_DATACONTEXT_T dma_rom_buff; /* Buffer to store UDMA handle */ /* related information */ }; @@ -87,6 +97,157 @@ static inline int siwx917_dma_addr_adjustment(uint32_t adjustment) } } +/* Releases a range of scatter-gather descriptors */ +static inline void +release_sg_desc_blocks(struct dma_sg_descriptor_allocator *sg_transfer_desc_block, + uint32_t start_index, uint32_t count) +{ + /* Loop through the range of descriptors to be released */ + for (int index = start_index; index < (start_index + count); index++) { + /* Mark each descriptor as free */ + sg_transfer_desc_block->free_desc[index] = 0; + } +} + +/* Requests a base address of contiguous memory for scatter-gather descriptor table */ +static int request_sg_desc_base_addr(struct dma_sg_descriptor_allocator *sg_transfer_desc_block, + RSI_UDMA_DESC_T **desc_base_addr, uint32_t block_count) +{ + uint32_t contiguous_blocks = 0; + uint32_t index1; + + /* Find contiguous free blocks */ + for (index1 = 0; index1 < CONFIG_SG_BUFFER_COUNT; index1++) { + if (sg_transfer_desc_block->free_desc[index1] != 0) { + contiguous_blocks = 0; + continue; + } + contiguous_blocks++; + /* Check if the required number of contiguous blocks is found */ + if (contiguous_blocks == block_count) { + *desc_base_addr = + &sg_transfer_desc_block + ->sg_transfer_desc_table[index1 - block_count + 1]; + goto out; + } + } + /* Return an error if not enough contiguous blocks are found */ + return -EIO; +out: + for (int index2 = (index1 - block_count + 1); index2 < index1; index2++) { + sg_transfer_desc_block->free_desc[index2] = 1; + } + /* Return the starting index of the allocated blocks */ + return (index1 - block_count + 1); +} + +/* Sets up the scatter-gather descriptor table for a DMA transfer */ +static int set_scatter_gather_desc(RSI_UDMA_DESC_T *sg_desc_base_addr, struct dma_config *config, + uint8_t *transfer_type) +{ + int peripheral_request = siwx917_dma_is_peripheral_request(config->channel_direction); + struct dma_block_config *block_addr = config->head_block; + + if (peripheral_request < 0) { + return -EINVAL; + } else if (peripheral_request) { + *transfer_type = UDMA_MODE_PER_SCATTER_GATHER; + } + for (int index = 0; index < config->block_count; index++) { + /* Set the source and destination end addresses */ + sg_desc_base_addr[index].pSrcEndAddr = + (uint32_t *)(block_addr->source_address + + (block_addr->block_size - config->source_data_size)); + sg_desc_base_addr[index].pDstEndAddr = + (uint32_t *)(block_addr->dest_address + + (block_addr->block_size - config->dest_data_size)); + /* Set the source and destination data sizes */ + sg_desc_base_addr[index].vsUDMAChaConfigData1.srcSize = + siwx917_dma_data_width(config->source_data_size); + sg_desc_base_addr[index].vsUDMAChaConfigData1.dstSize = + siwx917_dma_data_width(config->dest_data_size); + /* Calculate the number of DMA transfers required */ + block_addr->block_size /= config->source_data_size; + if (block_addr->block_size > DMA_MAX_TRANSFER_COUNT) { + return -EINVAL; + } + /* Set the total number of DMA transfers */ + sg_desc_base_addr[index].vsUDMAChaConfigData1.totalNumOfDMATrans = + block_addr->block_size - 1; + /* Set the transfer type based on whether it is a peripheral request */ + sg_desc_base_addr[index].vsUDMAChaConfigData1.transferType = + peripheral_request ? UDMA_MODE_PER_ALT_SCATTER_GATHER + : UDMA_MODE_MEM_ALT_SCATTER_GATHER; + /* Set the arbitration size */ + sg_desc_base_addr[index].vsUDMAChaConfigData1.rPower = ARBSIZE_1; + if (siwx917_dma_addr_adjustment(block_addr->source_addr_adj) < 0 || + siwx917_dma_addr_adjustment(block_addr->dest_addr_adj) < 0) { + return -EINVAL; + } + /* Set source and destination address increments */ + sg_desc_base_addr[index].vsUDMAChaConfigData1.srcInc = + siwx917_dma_addr_adjustment(block_addr->source_addr_adj) + ? UDMA_SRC_INC_NONE + : siwx917_dma_data_width(config->source_data_size); + sg_desc_base_addr[index].vsUDMAChaConfigData1.dstInc = + siwx917_dma_addr_adjustment(block_addr->dest_addr_adj) + ? UDMA_DST_INC_NONE + : siwx917_dma_data_width(config->dest_data_size); + /* Move to the next block */ + block_addr = block_addr->next_block; + } + /* Set the transfer type for the last descriptor */ + sg_desc_base_addr[config->block_count - 1].vsUDMAChaConfigData1.transferType = + peripheral_request ? UDMA_MODE_BASIC : UDMA_MODE_AUTO; + return 0; +} + +/* Configure DMA for scatter-gather transfer */ +static int dma_scatter_gather_config(const struct device *dev, RSI_UDMA_HANDLE_T udma_handle, + uint32_t channel, struct dma_config *config) +{ + uint8_t transfer_type = UDMA_MODE_MEM_SCATTER_GATHER; + const struct dma_siwx917_config *cfg = dev->config; + struct dma_siwx917_data *data = dev->data; + RSI_UDMA_DESC_T *sg_desc_base_addr = NULL; + int block_alloc_start_index; + + if (siwx917_dma_data_width(config->source_data_size) < 0 || + siwx917_dma_data_width(config->dest_data_size) < 0) { + return -EINVAL; + } + if (config->block_count > CONFIG_SG_BUFFER_COUNT) { + return -EINVAL; + } + /* Request base address for scatter-gather descriptor table */ + block_alloc_start_index = request_sg_desc_base_addr( + data->sg_transfer_desc_block, &sg_desc_base_addr, config->block_count); + if (block_alloc_start_index < 0) { + return -EIO; + } + if (set_scatter_gather_desc(sg_desc_base_addr, config, &transfer_type)) { + return -EINVAL; + } + /* This channel information is used to distinguish scatter-gather transfers and */ + /* free the allocated descriptors in sg_transfer_desc_block */ + data->chan_info[channel].SrcAddr = 0; + data->chan_info[channel].DestAddr = 0; + data->chan_info[channel].Cnt = config->block_count; + data->chan_info[channel].Size = block_alloc_start_index; + RSI_UDMA_InterruptClear(udma_handle, channel); + RSI_UDMA_ErrorStatusClear(udma_handle); + if (cfg->reg == UDMA0) { + M4SS_UDMA_INTR_SEL |= BIT(channel); + } else { + cfg->reg->UDMA_INTR_MASK_REG |= BIT(channel); + } + cfg->reg->CHNL_PRI_ALT_SET = BIT(channel); + cfg->reg->CHNL_REQ_MASK_CLR = BIT(channel); + RSI_UDMA_SetChannelScatterGatherTransfer(udma_handle, channel, config->block_count, + sg_desc_base_addr, transfer_type); + return 0; +} + static int dma_channel_config(const struct device *dev, RSI_UDMA_HANDLE_T udma_handle, uint32_t channel, struct dma_config *config, UDMA_Channel_Info *channel_info) @@ -149,11 +310,10 @@ static int dma_channel_config(const struct device *dev, RSI_UDMA_HANDLE_T udma_h } else { channel_control.dstInc = UDMA_DST_INC_NONE; } - status = UDMAx_ChannelConfigure(&udma_resources, (uint8_t)channel, - config->head_block->source_address, - config->head_block->dest_address, - config->head_block->block_size, channel_control, - &channel_config, NULL, channel_info, udma_handle); + status = UDMAx_ChannelConfigure( + &udma_resources, (uint8_t)channel, config->head_block->source_address, + config->head_block->dest_address, config->head_block->block_size, channel_control, + &channel_config, NULL, channel_info, udma_handle); if (status) { return -EIO; } @@ -184,8 +344,15 @@ static int dma_siwx917_configure(const struct device *dev, uint32_t channel, return -EINVAL; } - /* Configure dma channel for transfer */ - status = dma_channel_config(dev, udma_handle, channel, config, data->chan_info); + if (config->head_block->source_gather_en || config->head_block->dest_scatter_en) { + /* Configure DMA for a Scatter-Gather transfer */ + status = dma_scatter_gather_config(dev, udma_handle, channel, config); + } else { + /* Configure dma channel for transfer */ + status = dma_channel_config(dev, udma_handle, channel, config, data->chan_info); + } + data->dma_callback = config->dma_callback; + data->cb_data = config->user_data; if (status) { return status; } @@ -259,7 +426,6 @@ static int dma_siwx917_start(const struct device *dev, uint32_t channel) if (RSI_UDMA_ChannelEnable(udma_handle, channel) != 0) { return -EINVAL; } - /* Check if the transfer type is memory-memory */ if (udma_table[channel].vsUDMAChaConfigData1.srcInc != UDMA_SRC_INC_NONE && udma_table[channel].vsUDMAChaConfigData1.dstInc != UDMA_DST_INC_NONE) { @@ -363,12 +529,14 @@ static void dma_siwx917_isr(const struct device *dev) } /* find_lsb_set() returns 1 indexed value */ channel -= 1; + if (data->chan_info[channel].SrcAddr == 0 && data->chan_info[channel].DestAddr == 0) { + /* A Scatter-Gather transfer is completed, free the allocated descriptors */ + release_sg_desc_blocks(data->sg_transfer_desc_block, data->chan_info[channel].Size, + data->chan_info[channel].Cnt); + goto out; + } if (data->chan_info[channel].Cnt == data->chan_info[channel].Size) { - if (data->dma_callback) { - /* Transfer complete, call user callback */ - data->dma_callback(dev, data->cb_data, channel, 0); - } - cfg->reg->UDMA_DONE_STATUS_REG = BIT(channel); + goto out; } else { /* Call UDMA ROM IRQ handler. */ ROMAPI_UDMA_WRAPPER_API->uDMAx_IRQHandler(&udma_resources, udma_resources.desc, @@ -381,6 +549,11 @@ static void dma_siwx917_isr(const struct device *dev) } } out: + if (data->dma_callback) { + /* Transfer complete, call user callback */ + data->dma_callback(dev, data->cb_data, channel, 0); + } + cfg->reg->UDMA_DONE_STATUS_REG = BIT(channel); /* Enable the IRQ to restore interrupt functionality for other DMA channels */ irq_enable(cfg->irq_number); } @@ -396,8 +569,10 @@ static const struct dma_driver_api siwx917_dma_driver_api = { #define SIWX917_DMA_INIT(inst) \ static UDMA_Channel_Info dma##inst##_channel_info[DT_INST_PROP(inst, dma_channels)]; \ + static struct dma_sg_descriptor_allocator dma##inst##_desc_allocator; \ static struct dma_siwx917_data dma##inst##_data = { \ .chan_info = dma##inst##_channel_info, \ + .sg_transfer_desc_block = &dma##inst##_desc_allocator, \ }; \ static void siwx917_dma##inst##_irq_configure(void) \ { \ diff --git a/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf b/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf new file mode 100644 index 0000000..d9eba66 --- /dev/null +++ b/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf @@ -0,0 +1,2 @@ +CONFIG_DMA_SG_CHANNEL_NR=2 +CONFIG_DMA_SG_XFER_SIZE=1024 diff --git a/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay b/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay new file mode 100644 index 0000000..19a843d --- /dev/null +++ b/tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + dma0 = &udma0; + }; +}; + +&udma0 { + status = "okay"; +};