Skip to content

Commit 6f1debe

Browse files
Martinhoff-makerkartben
authored andcommitted
driver: dma: silabs: Add silabs_ldma_block_append function
This function allows to not restart DMA engine for driver that gives new buffer to the DMA engine while a transfer is ongoing. Signed-off-by: Martin Hoff <[email protected]>
1 parent c901551 commit 6f1debe

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

drivers/dma/dma_silabs_ldma.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,16 @@ static void dma_silabs_irq_handler(const struct device *dev, uint32_t id)
303303
atomic_clear(&chan->busy);
304304
}
305305

306+
/*
307+
* In the case that the transfer is done but we have append a new
308+
* descriptor, we need to manually load the next descriptor
309+
*/
310+
if (LDMA_TransferDone(chnum) &&
311+
LDMA->CH[chnum].LINK & _LDMA_CH_LINK_LINK_MASK) {
312+
sys_clear_bit((mem_addr_t)&LDMA->CHDONE, chnum);
313+
LDMA->LINKLOAD = BIT(chnum);
314+
}
315+
306316
if (chan->cb) {
307317
chan->cb(dev, chan->user_data, chnum, status);
308318
}
@@ -495,6 +505,64 @@ static DEVICE_API(dma, dma_funcs) = {
495505
.get_status = dma_silabs_get_status
496506
};
497507

508+
int silabs_ldma_append_block(const struct device *dev, uint32_t channel, struct dma_config *config)
509+
{
510+
const struct dma_silabs_data *data = dev->data;
511+
struct dma_silabs_channel *chan_conf = &data->dma_chan_table[channel];
512+
struct dma_block_config *block_config = config->head_block;
513+
LDMA_Descriptor_t *desc = data->dma_chan_table[channel].desc;
514+
unsigned int key;
515+
int ret;
516+
517+
__ASSERT(!((uintptr_t)desc & ~_LDMA_CH_LINK_LINKADDR_MASK),
518+
"DMA Descriptor is not 32 bits aligned");
519+
520+
if (channel > data->dma_ctx.dma_channels) {
521+
return -EINVAL;
522+
}
523+
524+
if (!atomic_test_bit(data->dma_ctx.atomic, channel)) {
525+
return -EINVAL;
526+
}
527+
528+
/* DMA Channel already have loaded a descriptor with a linkaddr
529+
* so we can't append a new block just after the current transfer.
530+
* You can't also append a descriptor list.
531+
* This check is here to not use the function in a wrong way
532+
*/
533+
if (desc->xfer.linkAddr || config->head_block->next_block) {
534+
return -EINVAL;
535+
}
536+
537+
/* A link is already set by a previous call to the function */
538+
if (sys_test_bit((mem_addr_t)&LDMA->CH[channel].LINK, _LDMA_CH_LINK_LINK_SHIFT)) {
539+
return -EINVAL;
540+
}
541+
542+
ret = dma_silabs_block_to_descriptor(config, chan_conf, block_config, desc);
543+
if (ret) {
544+
return ret;
545+
}
546+
547+
key = irq_lock();
548+
if (!LDMA_TransferDone(channel)) {
549+
/*
550+
* It is voluntary to split this 2 lines in order to separate the write of the link
551+
* addr and the write of the link bit. In this way, there is always a linkAddr when
552+
* the link bit is set.
553+
*/
554+
sys_write32((uintptr_t)desc, (mem_addr_t)&LDMA->CH[channel].LINK);
555+
sys_set_bit((mem_addr_t)&LDMA->CH[channel].LINK, _LDMA_CH_LINK_LINK_SHIFT);
556+
irq_unlock(key);
557+
558+
} else {
559+
irq_unlock(key);
560+
LDMA_StartTransfer(channel, &chan_conf->xfer_config, desc);
561+
}
562+
563+
return 0;
564+
}
565+
498566
#define SILABS_DMA_IRQ_CONNECT(n, inst) \
499567
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, n, irq), DT_INST_IRQ_BY_IDX(inst, n, priority), \
500568
dma_silabs_irq_handler, DEVICE_DT_INST_GET(inst), 0); \

include/zephyr/drivers/dma/dma_silabs_ldma.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,24 @@
2323
FIELD_PREP(SILABS_LDMA_SOURCE_MASK, FIELD_GET(SILABS_DMA_SLOT_SOURCE_MASK, slot)) | \
2424
FIELD_PREP(SILABS_LDMA_SIG_MASK, FIELD_GET(SILABS_DMA_SLOT_SIG_MASK, slot))
2525

26+
/**
27+
* @brief Append a new block to the current channel
28+
*
29+
* This function allows to append a block to the current DMA transfer. It allows a user/driver
30+
* to register the next DMA transfer while a transfer in being held without stopping or restarting
31+
* DMA engine. It is very suitable for Zephyr Uart API where user gives buffers while the DMA engine
32+
* is running. Because this function changes dynamically the link to the block that DMA engine would
33+
* load as the next transfer, it is only working with channel that didn't have linked block list.
34+
*
35+
* In the case that the DMA engine naturally stopped because the previous transfer is finished, this
36+
* function simply restart the DMA engine with the given block. If the DMA engine stopped while
37+
* reconfiguring the next transfer, the DMA engine will restart too.
38+
*
39+
* @param dev: dma device
40+
* @param channel: channel
41+
* @param config: configuration of the channel with the block to append as the head_block.
42+
*/
43+
int silabs_ldma_append_block(const struct device *dev, uint32_t channel,
44+
struct dma_config *config);
45+
2646
#endif /* ZEPHYR_INCLUDE_DRIVERS_DMA_SILABS_LDMA_H_*/

0 commit comments

Comments
 (0)