Skip to content

Commit 72d3940

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 2470784 commit 72d3940

File tree

2 files changed

+284
-1
lines changed

2 files changed

+284
-1
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: 275 additions & 1 deletion
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;
@@ -122,6 +133,212 @@ static void iadc_configure_scan_table_entry(sl_hal_iadc_scan_table_entry_t *entr
122133
};
123134
}
124135

136+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
137+
static int iadc_dma_init(const struct device *dev)
138+
{
139+
const struct iadc_config *config = dev->config;
140+
struct iadc_data *data = dev->data;
141+
struct iadc_dma_channel *dma = &data->dma;
142+
143+
if (dma->dma_dev) {
144+
if (!device_is_ready(dma->dma_dev)) {
145+
LOG_ERR("DMA device not ready");
146+
return -ENODEV;
147+
}
148+
149+
dma->dma_channel = dma_request_channel(dma->dma_dev, NULL);
150+
if (dma->dma_channel < 0) {
151+
LOG_ERR("Failed to request DMA channel");
152+
return -ENODEV;
153+
}
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+
uint8_t *sample_ptr;
220+
221+
if (status < 0) {
222+
LOG_ERR("DMA transfer error: %d", status);
223+
adc_context_complete(&data->ctx, status);
224+
return;
225+
}
226+
227+
/* Apply data mask */
228+
for (int i = 0; i < data->active_channels; i++) {
229+
sample_ptr = data->buffer + (i * data->data_size);
230+
231+
if (data->data_size == sizeof(uint32_t)) {
232+
uint32_t *sample_32 = (uint32_t *)sample_ptr;
233+
*sample_32 = *sample_32 & data->data_mask;
234+
} else {
235+
uint16_t *sample_16 = (uint16_t *)sample_ptr;
236+
*sample_16 = *sample_16 & data->data_mask;
237+
}
238+
}
239+
240+
iadc_dma_stop(dev);
241+
242+
adc_context_on_sampling_done(&data->ctx, dev);
243+
}
244+
#endif
245+
246+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
247+
static int iadc_dma_init(const struct device *dev)
248+
{
249+
const struct iadc_config *config = dev->config;
250+
struct iadc_data *data = dev->data;
251+
struct iadc_dma_channel *dma = &data->dma;
252+
253+
if (dma->dma_dev) {
254+
if (!device_is_ready(dma->dma_dev)) {
255+
LOG_ERR("DMA device not ready");
256+
return -ENODEV;
257+
}
258+
259+
dma->dma_channel = dma_request_channel(dma->dma_dev, NULL);
260+
if (dma->dma_channel < 0) {
261+
LOG_ERR("Failed to request DMA channel");
262+
return -ENODEV;
263+
}
264+
}
265+
266+
memset(&dma->blk_cfg, 0, sizeof(dma->blk_cfg));
267+
dma->blk_cfg.source_address = (uintptr_t)&((IADC_TypeDef *)config->base)->SCANFIFODATA;
268+
dma->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
269+
dma->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
270+
dma->dma_cfg.complete_callback_en = 1;
271+
dma->dma_cfg.channel_priority = 3;
272+
dma->dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
273+
dma->dma_cfg.head_block = &dma->blk_cfg;
274+
dma->dma_cfg.user_data = data;
275+
276+
return 0;
277+
}
278+
279+
static int iadc_dma_start(const struct device *dev)
280+
{
281+
struct iadc_data *data = dev->data;
282+
struct iadc_dma_channel *dma = &data->dma;
283+
int ret;
284+
285+
if (!dma->dma_dev) {
286+
return -ENODEV;
287+
}
288+
289+
if (dma->enabled) {
290+
return -EBUSY;
291+
}
292+
293+
ret = dma_config(dma->dma_dev, dma->dma_channel, &dma->dma_cfg);
294+
if (ret) {
295+
LOG_ERR("DMA config error: %d", ret);
296+
return ret;
297+
}
298+
299+
dma->enabled = true;
300+
301+
ret = dma_start(dma->dma_dev, dma->dma_channel);
302+
if (ret) {
303+
LOG_ERR("DMA start error: %d", ret);
304+
dma->enabled = false;
305+
return ret;
306+
}
307+
308+
return 0;
309+
}
310+
311+
static void iadc_dma_stop(const struct device *dev)
312+
{
313+
struct iadc_data *data = dev->data;
314+
struct iadc_dma_channel *dma = &data->dma;
315+
316+
if (!dma->enabled) {
317+
return;
318+
}
319+
320+
dma_stop(dma->dma_dev, dma->dma_channel);
321+
322+
dma->enabled = false;
323+
}
324+
325+
static void iadc_dma_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status)
326+
{
327+
struct iadc_data *data = user_data;
328+
const struct device *dev = data->dev;
329+
330+
if (status < 0) {
331+
LOG_ERR("DMA transfer error: %d", status);
332+
adc_context_complete(&data->ctx, status);
333+
return;
334+
}
335+
336+
iadc_dma_stop(dev);
337+
338+
adc_context_on_sampling_done(&data->ctx, dev);
339+
}
340+
#endif
341+
125342
/* Oversampling and resolution are common for both ADC configs
126343
* because they are not configurable per channel inside a ADC
127344
* sequence and are common for a sequence.
@@ -152,8 +369,28 @@ static int iadc_set_config(const struct device *dev)
152369
uint32_t channels;
153370
int res;
154371

372+
if (data->dma.dma_dev) {
373+
scan_init.data_valid_level = _IADC_SCANFIFOCFG_DVL_VALID1;
374+
/* Only needed to wake up DMA if EM is 2/3 */
375+
scan_init.fifo_dma_wakeup = true;
376+
}
377+
155378
data->adc_config_count = 0;
156379

380+
if (data->dma.dma_dev) {
381+
if (data->alignment == _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20) {
382+
data->dma.dma_cfg.source_data_size = 4;
383+
data->dma.dma_cfg.dest_data_size = 4;
384+
data->dma.dma_cfg.source_burst_length = 4;
385+
data->dma.dma_cfg.dest_burst_length = 4;
386+
} else {
387+
data->dma.dma_cfg.source_data_size = 2;
388+
data->dma.dma_cfg.dest_data_size = 2;
389+
data->dma.dma_cfg.source_burst_length = 2;
390+
data->dma.dma_cfg.dest_burst_length = 2;
391+
}
392+
}
393+
157394
channels = data->channels;
158395

159396
/*
@@ -415,6 +652,12 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
415652
data->buffer = sequence->buffer;
416653
data->active_channels = channel_count;
417654

655+
656+
if (data->dma.dma_dev) {
657+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
658+
data->dma.blk_cfg.block_size = channel_count * data->data_size;
659+
}
660+
418661
data->channels = sequence->channels;
419662

420663
res = iadc_set_config(data->dev);
@@ -432,9 +675,19 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
432675
static void iadc_start_scan(const struct device *dev)
433676
{
434677
const struct iadc_config *config = dev->config;
678+
__maybe_unused struct iadc_data *data = dev->data;
435679
IADC_TypeDef *iadc = (IADC_TypeDef *)config->base;
436680

681+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
682+
if (data->dma.dma_dev) {
683+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
684+
iadc_dma_start(dev);
685+
} else {
686+
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
687+
}
688+
#else
437689
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
690+
#endif
438691

439692
sl_hal_iadc_start_scan(iadc);
440693
}
@@ -648,6 +901,13 @@ static int iadc_init(const struct device *dev)
648901
return ret;
649902
}
650903

904+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
905+
ret = iadc_dma_init(dev);
906+
if (ret < 0) {
907+
data->dma.dma_dev = NULL;
908+
}
909+
#endif
910+
651911
config->irq_cfg_func();
652912

653913
adc_context_unlock_unconditionally(&data->ctx);
@@ -664,6 +924,20 @@ static DEVICE_API(adc, iadc_api) = {
664924
.ref_internal = SL_HAL_IADC_DEFAULT_VREF,
665925
};
666926

927+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
928+
#define IADC_DMA_CHANNEL_INIT(n) \
929+
.dma = {.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR(n)), \
930+
.dma_cfg = { \
931+
.dma_slot = \
932+
SILABS_LDMA_REQSEL_TO_SLOT(DT_INST_DMAS_CELL_BY_IDX(n, 0, slot)), \
933+
.dma_callback = iadc_dma_cb, \
934+
}},
935+
#define IADC_DMA_CHANNEL(n) COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), \
936+
(IADC_DMA_CHANNEL_INIT(n)), ())
937+
#else
938+
#define IADC_DMA_CHANNEL(n)
939+
#endif
940+
667941
#define IADC_INIT(n) \
668942
PINCTRL_DT_INST_DEFINE(n); \
669943
PM_DEVICE_DT_INST_DEFINE(n, iadc_pm_action); \
@@ -681,7 +955,7 @@ static DEVICE_API(adc, iadc_api) = {
681955
static struct iadc_data iadc_data_##n = {ADC_CONTEXT_INIT_TIMER(iadc_data_##n, ctx), \
682956
ADC_CONTEXT_INIT_LOCK(iadc_data_##n, ctx), \
683957
ADC_CONTEXT_INIT_SYNC(iadc_data_##n, ctx), \
684-
}; \
958+
IADC_DMA_CHANNEL(n)}; \
685959
\
686960
static void iadc_config_func_##n(void) \
687961
{ \

0 commit comments

Comments
 (0)