Skip to content

Commit

Permalink
drivers: dma: siwx917: Enable scatter-gather transfer support
Browse files Browse the repository at this point in the history
Implement support for scatter-gather DMA transfers in the siwx917 driver.
This enhancement allows the driver to handle multiple non-contiguous memory
buffers in a single DMA transaction

Signed-off-by: Sai Santhosh Malae <[email protected]>
  • Loading branch information
smalae committed Dec 23, 2024
1 parent 1e3784e commit c6f932d
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 21 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/upstream-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions drivers/dma/Kconfig.siwx917
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ config DMA_SILABS_SIWX917_COMMON_INIT_PRIORITY
int "Common initialization priority"
depends on DMA_SILABS_SIWX917
default 42

Check failure on line 15 in drivers/dma/Kconfig.siwx917

View workflow job for this annotation

GitHub Actions / compliance

TRAILING_WHITESPACE

drivers/dma/Kconfig.siwx917:15 trailing whitespace
config SG_BUFFER_COUNT
int "The maximum allowable number of buffers for scatter-gather transfers"
depends on DMA_SILABS_SIWX917
default 10
217 changes: 196 additions & 21 deletions drivers/dma/dma_silabs_siwx917.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@
#include <zephyr/logging/log.h>
#include <zephyr/types.h>
#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 */

Check warning on line 31 in drivers/dma/dma_silabs_siwx917.c

View workflow job for this annotation

GitHub Actions / compliance

BLOCK_COMMENT_STYLE

drivers/dma/dma_silabs_siwx917.c:31 Block comments use * on subsequent lines

Check warning on line 31 in drivers/dma/dma_silabs_siwx917.c

View workflow job for this annotation

GitHub Actions / compliance

BLOCK_COMMENT_STYLE

drivers/dma/dma_silabs_siwx917.c:31 Block comments use a trailing */ on a separate line
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 */
Expand All @@ -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 */
};
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand All @@ -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) \
{ \
Expand Down
2 changes: 2 additions & 0 deletions tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_DMA_SG_CHANNEL_NR=2
CONFIG_DMA_SG_XFER_SIZE=1024
15 changes: 15 additions & 0 deletions tests/drivers/dma/scatter_gather/boards/siwx917_rb4338a.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2024 Silicon Laboratories Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

/ {
aliases {
dma0 = &udma0;
};
};

&udma0 {
status = "okay";
};

0 comments on commit c6f932d

Please sign in to comment.