Skip to content

Commit 320b287

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 d566ae8 commit 320b287

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;
@@ -119,6 +130,104 @@ static void iadc_configure_scan_table_entry(sl_hal_iadc_scan_table_entry_t *entr
119130
};
120131
}
121132

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

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

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

156285
/*
@@ -340,6 +469,11 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
340469
data->buffer = sequence->buffer;
341470
data->active_channels = channel_count;
342471

472+
if (data->dma.dma_dev) {
473+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
474+
data->dma.blk_cfg.block_size = channel_count * data->data_size;
475+
}
476+
343477
data->channels = sequence->channels;
344478

345479
res = iadc_set_config(data->dev);
@@ -357,9 +491,19 @@ static int start_read(const struct device *dev, const struct adc_sequence *seque
357491
static void iadc_start_scan(const struct device *dev)
358492
{
359493
const struct iadc_config *config = dev->config;
494+
__maybe_unused struct iadc_data *data = dev->data;
360495
IADC_TypeDef *iadc = (IADC_TypeDef *)config->base;
361496

497+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
498+
if (data->dma.dma_dev) {
499+
data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer;
500+
iadc_dma_start(dev);
501+
} else {
502+
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
503+
}
504+
#else
362505
sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE);
506+
#endif
363507

364508
sl_hal_iadc_start_scan(iadc);
365509
}
@@ -569,6 +713,13 @@ static int iadc_init(const struct device *dev)
569713
return ret;
570714
}
571715

716+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
717+
ret = iadc_dma_init(dev);
718+
if (ret < 0) {
719+
data->dma.dma_dev = NULL;
720+
}
721+
#endif
722+
572723
config->irq_cfg_func();
573724

574725
adc_context_unlock_unconditionally(&data->ctx);
@@ -585,6 +736,17 @@ static DEVICE_API(adc, iadc_api) = {
585736
.ref_internal = SL_HAL_IADC_DEFAULT_VREF,
586737
};
587738

739+
#ifdef CONFIG_ADC_SILABS_IADC_DMA
740+
#define IADC_DMA_CHANNEL_INIT(n) \
741+
.dma.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR(n)), \
742+
.dma.dma_cfg.dma_slot = SILABS_LDMA_REQSEL_TO_SLOT(DT_INST_DMAS_CELL_BY_IDX(n, 0, slot)), \
743+
.dma.dma_cfg.dma_callback = iadc_dma_cb,
744+
#define IADC_DMA_CHANNEL(n) \
745+
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), (IADC_DMA_CHANNEL_INIT(n)), ())
746+
#else
747+
#define IADC_DMA_CHANNEL(n)
748+
#endif
749+
588750
#define IADC_INIT(n) \
589751
PINCTRL_DT_INST_DEFINE(n); \
590752
PM_DEVICE_DT_INST_DEFINE(n, iadc_pm_action); \
@@ -603,6 +765,7 @@ static DEVICE_API(adc, iadc_api) = {
603765
ADC_CONTEXT_INIT_TIMER(iadc_data_##n, ctx), \
604766
ADC_CONTEXT_INIT_LOCK(iadc_data_##n, ctx), \
605767
ADC_CONTEXT_INIT_SYNC(iadc_data_##n, ctx), \
768+
IADC_DMA_CHANNEL(n) \
606769
}; \
607770
\
608771
static void iadc_config_func_##n(void) \

0 commit comments

Comments
 (0)