Skip to content

Commit 9b0c9b8

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 2daf066 commit 9b0c9b8

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
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
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: 163 additions & 0 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>
@@ -45,6 +47,14 @@ LOG_MODULE_REGISTER(iadc, CONFIG_ADC_LOG_LEVEL);
4547
#define IADC_PORT_MASK 0xF0
4648
#define IADC_PIN_MASK 0x0F
4749

50+
struct iadc_dma_channel {
51+
const struct device *dma_dev;
52+
struct dma_block_config blk_cfg;
53+
struct dma_config dma_cfg;
54+
int dma_channel;
55+
bool enabled;
56+
};
57+
4858
struct iadc_chan_conf {
4959
sl_hal_iadc_analog_gain_t gain;
5060
sl_hal_iadc_voltage_reference_t reference;
@@ -60,6 +70,7 @@ struct iadc_data {
6070
const struct device *dev;
6171
struct adc_context ctx;
6272
struct iadc_chan_conf chan_conf[SL_HAL_IADC_CHANNEL_ID_MAX];
73+
struct iadc_dma_channel dma;
6374
uint8_t adc_config_count; /* Number of ADC configs created (max 2) */
6475
uint32_t clock_rate;
6576
uint32_t channels;
@@ -120,6 +131,104 @@ static void iadc_configure_scan_table_entry(sl_hal_iadc_scan_table_entry_t *entr
120131
};
121132
}
122133

134+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
135+
static int iadc_dma_init(const struct device *dev)
136+
{
137+
const struct iadc_config *config = dev->config;
138+
struct iadc_data *data = dev->data;
139+
struct iadc_dma_channel *dma = &data->dma;
140+
141+
if (!dma->dma_dev) {
142+
return 0;
143+
}
144+
145+
if (!device_is_ready(dma->dma_dev)) {
146+
LOG_ERR("DMA device not ready");
147+
return -ENODEV;
148+
}
149+
150+
dma->dma_channel = dma_request_channel(dma->dma_dev, NULL);
151+
if (dma->dma_channel < 0) {
152+
LOG_ERR("Failed to request DMA channel");
153+
return -ENODEV;
154+
}
155+
156+
memset(&dma->blk_cfg, 0, sizeof(dma->blk_cfg));
157+
dma->blk_cfg.source_address = (uintptr_t)&(config->base)->SCANFIFODATA;
158+
dma->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
159+
dma->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
160+
dma->dma_cfg.complete_callback_en = 1;
161+
dma->dma_cfg.channel_priority = 3;
162+
dma->dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
163+
dma->dma_cfg.head_block = &dma->blk_cfg;
164+
dma->dma_cfg.user_data = data;
165+
166+
return 0;
167+
}
168+
169+
static int iadc_dma_start(const struct device *dev)
170+
{
171+
struct iadc_data *data = dev->data;
172+
struct iadc_dma_channel *dma = &data->dma;
173+
int ret;
174+
175+
if (!dma->dma_dev) {
176+
return -ENODEV;
177+
}
178+
179+
if (dma->enabled) {
180+
return -EBUSY;
181+
}
182+
183+
ret = dma_config(dma->dma_dev, dma->dma_channel, &dma->dma_cfg);
184+
if (ret) {
185+
LOG_ERR("DMA config error: %d", ret);
186+
return ret;
187+
}
188+
189+
dma->enabled = true;
190+
191+
ret = dma_start(dma->dma_dev, dma->dma_channel);
192+
if (ret) {
193+
LOG_ERR("DMA start error: %d", ret);
194+
dma->enabled = false;
195+
return ret;
196+
}
197+
198+
return 0;
199+
}
200+
201+
static void iadc_dma_stop(const struct device *dev)
202+
{
203+
struct iadc_data *data = dev->data;
204+
struct iadc_dma_channel *dma = &data->dma;
205+
206+
if (!dma->enabled) {
207+
return;
208+
}
209+
210+
dma_stop(dma->dma_dev, dma->dma_channel);
211+
212+
dma->enabled = false;
213+
}
214+
215+
static void iadc_dma_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status)
216+
{
217+
struct iadc_data *data = user_data;
218+
const struct device *dev = data->dev;
219+
220+
if (status < 0) {
221+
LOG_ERR("DMA transfer error: %d", status);
222+
adc_context_complete(&data->ctx, status);
223+
return;
224+
}
225+
226+
iadc_dma_stop(dev);
227+
228+
adc_context_on_sampling_done(&data->ctx, dev);
229+
}
230+
#endif /* CONFIG_ADC_SILABS_IADC_DMA */
231+
123232
/* Oversampling and resolution are common for both ADC configs
124233
* because they are not configurable per channel inside a ADC
125234
* sequence and are common for a sequence.
@@ -150,8 +259,28 @@ static int iadc_set_config(const struct device *dev)
150259
uint32_t channels;
151260
int res;
152261

262+
if (data->dma.dma_dev) {
263+
scan_init.data_valid_level = _IADC_SCANFIFOCFG_DVL_VALID1;
264+
/* Only needed to wake up DMA if EM is 2/3 */
265+
scan_init.fifo_dma_wakeup = true;
266+
}
267+
153268
data->adc_config_count = 0;
154269

270+
if (data->dma.dma_dev) {
271+
if (data->alignment == _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20) {
272+
data->dma.dma_cfg.source_data_size = 4;
273+
data->dma.dma_cfg.dest_data_size = 4;
274+
data->dma.dma_cfg.source_burst_length = 4;
275+
data->dma.dma_cfg.dest_burst_length = 4;
276+
} else {
277+
data->dma.dma_cfg.source_data_size = 2;
278+
data->dma.dma_cfg.dest_data_size = 2;
279+
data->dma.dma_cfg.source_burst_length = 2;
280+
data->dma.dma_cfg.dest_burst_length = 2;
281+
}
282+
}
283+
155284
channels = data->channels;
156285

157286
/*
@@ -377,6 +506,11 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
377506
data->buffer = sequence->buffer;
378507
data->active_channels = channel_count;
379508

509+
if (data->dma.dma_dev) {
510+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
511+
data->dma.blk_cfg.block_size = channel_count * data->data_size;
512+
}
513+
380514
data->channels = sequence->channels;
381515

382516
res = iadc_set_config(data->dev);
@@ -394,9 +528,19 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
394528
static void iadc_start_scan(const struct device *dev)
395529
{
396530
const struct iadc_config *config = dev->config;
531+
__maybe_unused struct iadc_data *data = dev->data;
397532
IADC_TypeDef *iadc = (IADC_TypeDef *)config->base;
398533

534+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
535+
if (data->dma.dma_dev) {
536+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
537+
iadc_dma_start(dev);
538+
} else {
539+
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
540+
}
541+
#else
399542
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
543+
#endif
400544

401545
sl_hal_iadc_start_scan(iadc);
402546
}
@@ -606,6 +750,13 @@ static int iadc_init(const struct device *dev)
606750
return ret;
607751
}
608752

753+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
754+
ret = iadc_dma_init(dev);
755+
if (ret < 0) {
756+
data->dma.dma_dev = NULL;
757+
}
758+
#endif
759+
609760
config->irq_cfg_func();
610761

611762
adc_context_unlock_unconditionally(&data->ctx);
@@ -622,6 +773,17 @@ static DEVICE_API(adc, iadc_api) = {
622773
.ref_internal = SL_HAL_IADC_DEFAULT_VREF,
623774
};
624775

776+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
777+
#define IADC_DMA_CHANNEL_INIT(n) \
778+
.dma.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR(n)), \
779+
.dma.dma_cfg.dma_slot = SILABS_LDMA_REQSEL_TO_SLOT(DT_INST_DMAS_CELL_BY_IDX(n, 0, slot)), \
780+
.dma.dma_cfg.dma_callback = iadc_dma_cb,
781+
#define IADC_DMA_CHANNEL(n) \
782+
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), (IADC_DMA_CHANNEL_INIT(n)), ())
783+
#else
784+
#define IADC_DMA_CHANNEL(n)
785+
#endif
786+
625787
#define IADC_INIT(n) \
626788
PINCTRL_DT_INST_DEFINE(n); \
627789
PM_DEVICE_DT_INST_DEFINE(n, iadc_pm_action); \
@@ -640,6 +802,7 @@ static DEVICE_API(adc, iadc_api) = {
640802
ADC_CONTEXT_INIT_TIMER(iadc_data_##n, ctx), \
641803
ADC_CONTEXT_INIT_LOCK(iadc_data_##n, ctx), \
642804
ADC_CONTEXT_INIT_SYNC(iadc_data_##n, ctx), \
805+
IADC_DMA_CHANNEL(n) \
643806
}; \
644807
\
645808
static void iadc_config_func_##n(void) \

0 commit comments

Comments
 (0)