Skip to content

Commit 7a1741d

Browse files
drivers: adc: add DMA support for Silabs IADC
This commit introduces DMA support for the Silabs IADC driver. A new Kconfig option is added to enable DMA support, ensuring compatibility with the existing ADC configuration. DMA can be used for synch/asynch operation. Signed-off-by: Martin Hoff <[email protected]>
1 parent 34cfb4c commit 7a1741d

File tree

2 files changed

+183
-2
lines changed

2 files changed

+183
-2
lines changed

drivers/adc/Kconfig.silabs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ config ADC_SILABS_IADC
99
select ADC_CONFIGURABLE_INPUTS
1010
help
1111
Enable the driver implementation for Silabs Incremental ADC
12+
13+
config ADC_SILABS_IADC_DMA
14+
bool "Silabs IADC async DMA support"
15+
depends on ADC_SILABS_IADC
16+
depends on DMA_SILABS_LDMA
17+
help
18+
Enable DMA support with the Silabs IADC driver.
19+
This allows ADC conversions to be performed (asynchronously or not)
20+
using DMA for improved performance.

drivers/adc/adc_silabs_iadc.c

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <zephyr/drivers/adc.h>
1010
#include <zephyr/drivers/clock_control.h>
1111
#include <zephyr/drivers/clock_control/clock_control_silabs.h>
12+
#include <zephyr/drivers/dma.h>
13+
#include <zephyr/drivers/dma/dma_silabs_ldma.h>
1214
#include <zephyr/drivers/pinctrl.h>
1315
#include <zephyr/logging/log.h>
1416
#include <zephyr/pm/device.h>
@@ -24,6 +26,15 @@ LOG_MODULE_REGISTER(iadc, CONFIG_ADC_LOG_LEVEL);
2426
#define IADC_PORT_MASK 0xF0
2527
#define IADC_PIN_MASK 0x0F
2628

29+
struct iadc_dma_channel {
30+
const struct device *dma_dev;
31+
int dma_channel;
32+
struct dma_block_config blk_cfg;
33+
struct dma_config dma_cfg;
34+
uint32_t offset;
35+
bool enabled;
36+
};
37+
2738
struct iadc_chan_conf {
2839
sl_hal_iadc_analog_gain_t gain;
2940
sl_hal_iadc_voltage_reference_t reference;
@@ -44,6 +55,7 @@ struct iadc_data {
4455
uint32_t channels;
4556
uint8_t adc_config_count; /* Number of ADC configs created (max 2) */
4657
struct iadc_chan_conf chan_conf[SL_HAL_IADC_CHANNEL_ID_MAX - 1];
58+
struct iadc_dma_channel dma;
4759
};
4860

4961
struct iadc_config {
@@ -97,6 +109,115 @@ static void iadc_configure_scan_table_entry(sl_hal_iadc_scan_table_entry_t *entr
97109
};
98110
}
99111

112+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
113+
static int iadc_dma_init(const struct device *dev)
114+
{
115+
const struct iadc_config *config = dev->config;
116+
struct iadc_data *data = dev->data;
117+
struct iadc_dma_channel *dma = &data->dma;
118+
119+
if (dma->dma_dev) {
120+
if (!device_is_ready(dma->dma_dev)) {
121+
LOG_ERR("DMA device not ready");
122+
return -ENODEV;
123+
}
124+
125+
dma->dma_channel = dma_request_channel(dma->dma_dev, NULL);
126+
if (dma->dma_channel < 0) {
127+
LOG_ERR("Failed to request DMA channel");
128+
return -ENODEV;
129+
}
130+
}
131+
132+
/* Configure DMA block */
133+
memset(&dma->blk_cfg, 0, sizeof(dma->blk_cfg));
134+
dma->blk_cfg.source_address = (uintptr_t)&((IADC_TypeDef *)config->base)->SCANFIFODATA;
135+
dma->blk_cfg.dest_address = 0;
136+
dma->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
137+
dma->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
138+
dma->blk_cfg.block_size = 0;
139+
dma->dma_cfg.complete_callback_en = 1;
140+
dma->dma_cfg.channel_priority = 3;
141+
dma->dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
142+
dma->dma_cfg.head_block = &dma->blk_cfg;
143+
dma->dma_cfg.user_data = data;
144+
145+
return 0;
146+
}
147+
148+
static int iadc_dma_start(const struct device *dev)
149+
{
150+
struct iadc_data *data = dev->data;
151+
struct iadc_dma_channel *dma = &data->dma;
152+
int ret;
153+
154+
if (!dma->dma_dev) {
155+
return -ENODEV;
156+
}
157+
158+
if (dma->enabled) {
159+
LOG_WRN("DMA was already enabled for IADC");
160+
return -EBUSY;
161+
}
162+
163+
/* Configure DMA channel */
164+
ret = dma_config(dma->dma_dev, dma->dma_channel, &dma->dma_cfg);
165+
if (ret) {
166+
LOG_ERR("DMA config error: %d", ret);
167+
return ret;
168+
}
169+
170+
dma->enabled = true;
171+
172+
/* Start DMA transfer */
173+
ret = dma_start(dma->dma_dev, dma->dma_channel);
174+
if (ret) {
175+
LOG_ERR("DMA start error: %d", ret);
176+
dma->enabled = false;
177+
return ret;
178+
}
179+
180+
LOG_DBG("DMA transfer started");
181+
182+
return 0;
183+
}
184+
185+
static void iadc_dma_stop(const struct device *dev)
186+
{
187+
struct iadc_data *data = dev->data;
188+
struct iadc_dma_channel *dma = &data->dma;
189+
190+
if (!dma->enabled) {
191+
return;
192+
}
193+
194+
/* Stop DMA transfer */
195+
dma_stop(dma->dma_dev, dma->dma_channel);
196+
197+
dma->enabled = false;
198+
199+
LOG_DBG("DMA transfer stopped");
200+
}
201+
202+
static void iadc_dma_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status)
203+
{
204+
struct iadc_data *data = user_data;
205+
const struct device *dev = data->dev;
206+
207+
if (status < 0) {
208+
LOG_ERR("DMA transfer error: %d", status);
209+
adc_context_complete(&data->ctx, status);
210+
return;
211+
}
212+
213+
iadc_dma_stop(dev);
214+
215+
adc_context_on_sampling_done(&data->ctx, dev);
216+
217+
LOG_DBG("DMA transfer completed");
218+
}
219+
#endif
220+
100221
static void iadc_set_config(const struct device *dev)
101222
{
102223
sl_hal_iadc_scan_table_t scanTable = SL_HAL_IADC_SCANTABLE_DEFAULT;
@@ -107,6 +228,12 @@ static void iadc_set_config(const struct device *dev)
107228
struct iadc_data *data = dev->data;
108229
struct iadc_chan_conf *chan_conf;
109230

231+
if (data->dma.dma_dev) {
232+
scanInit.data_valid_level = SL_HAL_IADC_DATA_VALID_1;
233+
/* Only needed to wake up DMA if EM is 2/3 */
234+
scanInit.fifo_dma_wakeup = true;
235+
}
236+
110237
data->adc_config_count = 0;
111238

112239
/*
@@ -221,6 +348,14 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
221348

222349
data->buffer = sequence->buffer;
223350

351+
if (data->dma.dma_dev) {
352+
/* Need to take care that it will copy 16 bit data and not only 12 bit of the result
353+
*/
354+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
355+
data->dma.blk_cfg.block_size = channel_count * sizeof(uint16_t);
356+
data->dma.offset = 0;
357+
}
358+
224359
adc_context_start_read(&data->ctx, sequence);
225360

226361
res = adc_context_wait_for_completion(&data->ctx);
@@ -238,7 +373,16 @@ static void iadc_start_channel(const struct device *dev)
238373

239374
iadc_set_config(data->dev);
240375

376+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
377+
if (data->dma.dma_dev) {
378+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer + data->dma.offset;
379+
iadc_dma_start(dev);
380+
} else {
381+
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
382+
}
383+
#else
241384
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
385+
#endif
242386

243387
sl_hal_iadc_start_scan(iadc);
244388
}
@@ -259,6 +403,9 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repe
259403

260404
if (repeat_sampling) {
261405
data->buffer = data->repeat_buffer;
406+
data->dma.offset = 0;
407+
} else {
408+
data->dma.offset = data->dma.blk_cfg.block_size * (data->ctx.sampling_index);
262409
}
263410
}
264411

@@ -445,11 +592,18 @@ static int iadc_init(const struct device *dev)
445592
}
446593

447594
ret = clock_control_get_rate(config->clock_dev, (clock_control_subsys_t)&config->clock_cfg,
448-
&data->clock_rate);
595+
&data->clock_rate);
449596
if (ret < 0) {
450597
return ret;
451598
}
452599

600+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
601+
ret = iadc_dma_init(dev);
602+
if (ret < 0) {
603+
data->dma.dma_dev = NULL;
604+
}
605+
#endif
606+
453607
config->irq_cfg_func();
454608

455609
adc_context_unlock_unconditionally(&data->ctx);
@@ -466,6 +620,24 @@ static DEVICE_API(adc, iadc_api) = {
466620
.ref_internal = SL_HAL_IADC_DEFAULT_VREF,
467621
};
468622

623+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
624+
#define IADC_DMA_CHANNEL_INIT(n) \
625+
.dma = {.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR(n)), \
626+
.dma_cfg = { \
627+
.dma_slot = \
628+
SILABS_LDMA_REQSEL_TO_SLOT(DT_INST_DMAS_CELL_BY_IDX(n, 0, slot)), \
629+
.source_data_size = 2, \
630+
.dest_data_size = 2, \
631+
.source_burst_length = 2, \
632+
.dest_burst_length = 2, \
633+
.dma_callback = iadc_dma_cb, \
634+
}},
635+
#define IADC_DMA_CHANNEL(n) COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), \
636+
(IADC_DMA_CHANNEL_INIT(n)), ())
637+
#else
638+
#define IADC_DMA_CHANNEL(n)
639+
#endif
640+
469641
#define IADC_INIT(n) \
470642
PINCTRL_DT_INST_DEFINE(n); \
471643
PM_DEVICE_DT_INST_DEFINE(n, iadc_pm_action); \
@@ -483,7 +655,7 @@ static DEVICE_API(adc, iadc_api) = {
483655
static struct iadc_data iadc_data_##n = {ADC_CONTEXT_INIT_TIMER(iadc_data_##n, ctx), \
484656
ADC_CONTEXT_INIT_LOCK(iadc_data_##n, ctx), \
485657
ADC_CONTEXT_INIT_SYNC(iadc_data_##n, ctx), \
486-
}; \
658+
IADC_DMA_CHANNEL(n)}; \
487659
\
488660
static void iadc_config_func_##n(void) \
489661
{ \

0 commit comments

Comments
 (0)