Skip to content

Commit 84f5354

Browse files
committed
drivers: adc: Adding DMA transfer capability to LPADC
Adding DMA transfer capability to LPADC Signed-off-by: Zhaoxiang Jin <[email protected]>
1 parent dba4adb commit 84f5354

File tree

2 files changed

+186
-7
lines changed

2 files changed

+186
-7
lines changed

drivers/adc/Kconfig.mcux

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ config LPADC_CHANNEL_COUNT
121121
15 ADC channels. This value corresponds to how many of the CMD
122122
registers can be configured within the ADC.
123123

124+
config LPADC_ENABLE_DMA
125+
bool "Enable DMA for lpadc driver"
126+
select DMA
127+
help
128+
Enable the DMA for LPADC driver.
124129

125130
endif # ADC_MCUX_LPADC
126131

drivers/adc/adc_mcux_lpadc.c

Lines changed: 181 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include <zephyr/drivers/pinctrl.h>
2020
#include <zephyr/drivers/opamp.h>
2121

22+
#ifdef CONFIG_LPADC_ENABLE_DMA
23+
#include <zephyr/drivers/dma.h>
24+
#endif
25+
#include <string.h>
26+
2227
#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
2328
#include <zephyr/logging/log.h>
2429
#include <zephyr/irq.h>
@@ -60,6 +65,11 @@ struct mcux_lpadc_config {
6065
* (from that channel node's zephyr,vref-mv)
6166
*/
6267
uint16_t opamp_vref_mv;
68+
#ifdef CONFIG_LPADC_ENABLE_DMA
69+
const struct device *dma_dev;
70+
uint32_t dma_channel;
71+
uint32_t dma_slot;
72+
#endif
6373
};
6474

6575
struct mcux_lpadc_data {
@@ -77,6 +87,14 @@ struct mcux_lpadc_data {
7787
*/
7888
uint16_t sample_min_raw;
7989
uint16_t sample_max_raw;
90+
uint8_t channels_count;
91+
bool use_dma;
92+
#ifdef CONFIG_LPADC_ENABLE_DMA
93+
struct dma_config dma_cfg;
94+
struct dma_block_config dma_block;
95+
/* Staging buffer for 32-bit RESFIFO words (max channels per round) */
96+
uint32_t dma_results[CONFIG_LPADC_CHANNEL_COUNT];
97+
#endif
8098
};
8199

82100
static int mcux_lpadc_acquisition_time_setup(const struct device *dev, uint16_t acq_time,
@@ -461,13 +479,144 @@ static void mcux_lpadc_start_channel(const struct device *dev)
461479
LPADC_DoSoftwareTrigger(config->base, 1);
462480
}
463481

482+
#ifdef CONFIG_LPADC_ENABLE_DMA
483+
static void mcux_lpadc_dma_callback(const struct device *dma_dev, void *user_data,
484+
uint32_t channel, int status)
485+
{
486+
const struct device *dev = (const struct device *)user_data;
487+
const struct mcux_lpadc_config *config = dev->config;
488+
struct mcux_lpadc_data *data = dev->data;
489+
490+
if (!data->use_dma) {
491+
return;
492+
}
493+
494+
(void)dma_stop(config->dma_dev, config->dma_channel);
495+
496+
if (status < 0) {
497+
adc_context_complete(&data->ctx, status);
498+
return;
499+
}
500+
501+
/* Disable LPADC DMA request */
502+
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
503+
LPADC_EnableFIFO0WatermarkDMA(config->base, false);
504+
#else
505+
LPADC_EnableFIFOWatermarkDMA(config->base, false);
506+
#endif
507+
508+
/* Process the data in staging buffer and pass to data->buffer in enabled channel order. */
509+
uint32_t written = 0U;
510+
for (uint8_t ch = 0U; ch < CONFIG_LPADC_CHANNEL_COUNT && written < data->channels_count; ch++) {
511+
if (data->ctx.sequence.channels & BIT(ch)) {
512+
lpadc_sample_channel_mode_t conv_mode = data->cmd_config[ch].sampleChannelMode;
513+
uint32_t fifo_data = data->dma_results[written];
514+
uint16_t conv_result = (uint16_t)(fifo_data & 0xFFFF);
515+
uint16_t tmp;
516+
517+
if (data->ctx.sequence.resolution < 15) {
518+
tmp = (uint16_t)((conv_result >> 3) & 0x0FFF);
519+
#if !(defined(FSL_FEATURE_LPADC_HAS_B_SIDE_CHANNELS) && (FSL_FEATURE_LPADC_HAS_B_SIDE_CHANNELS == 0U))
520+
#if defined(FSL_FEATURE_LPADC_HAS_CMDL_DIFF) && FSL_FEATURE_LPADC_HAS_CMDL_DIFF
521+
if (conv_mode == kLPADC_SampleChannelDiffBothSideAB ||
522+
conv_mode == kLPADC_SampleChannelDiffBothSideBA) {
523+
#else
524+
if (conv_mode == kLPADC_SampleChannelDiffBothSide) {
525+
#endif
526+
if (conv_result & 0x8000) {
527+
tmp = (uint16_t)(tmp - 0x1000);
528+
}
529+
}
530+
#endif
531+
} else {
532+
tmp = conv_result;
533+
}
534+
535+
*data->buffer++ = tmp;
536+
written++;
537+
}
538+
}
539+
540+
adc_context_on_sampling_done(&data->ctx, dev);
541+
}
542+
543+
/* Configure and start DMA before triggering conversions */
544+
static void mcux_lpadc_dma_configure(struct mcux_lpadc_data *data)
545+
{
546+
const struct device *dev = data->dev;
547+
const struct mcux_lpadc_config *config = dev->config;
548+
549+
if (data->use_dma) {
550+
if (data->channels_count == 0U) {
551+
adc_context_complete(&data->ctx, -EINVAL);
552+
return;
553+
}
554+
555+
/* Setup one DMA block from RESFIFO to staging buffer */
556+
memset(&data->dma_block, 0, sizeof(data->dma_block));
557+
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
558+
data->dma_block.source_address = (uint32_t)&(config->base->RESFIFO[0]);
559+
#else
560+
data->dma_block.source_address = (uint32_t)&(config->base->RESFIFO);
561+
#endif
562+
data->dma_block.dest_address = (uint32_t)data->dma_results;
563+
data->dma_block.block_size = data->channels_count * sizeof(uint32_t);
564+
data->dma_block.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
565+
data->dma_block.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
566+
data->dma_block.source_reload_en = 0;
567+
data->dma_block.dest_reload_en = 0;
568+
569+
/* DMA configuration */
570+
memset(&data->dma_cfg, 0, sizeof(data->dma_cfg));
571+
data->dma_cfg.dma_slot = config->dma_slot;
572+
data->dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
573+
data->dma_cfg.source_data_size = sizeof(uint32_t);
574+
data->dma_cfg.dest_data_size = sizeof(uint32_t);
575+
/* Use 32-bit per request to match LPADC RESFIFO register width */
576+
data->dma_cfg.source_burst_length = sizeof(uint32_t);
577+
data->dma_cfg.dest_burst_length = sizeof(uint32_t);
578+
data->dma_cfg.block_count = 1;
579+
data->dma_cfg.head_block = &data->dma_block;
580+
data->dma_cfg.user_data = (void *)dev;
581+
data->dma_cfg.dma_callback = mcux_lpadc_dma_callback;
582+
583+
/* Enable LPADC DMA request on FIFO watermark */
584+
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
585+
LPADC_EnableFIFO0WatermarkDMA(config->base, true);
586+
#else
587+
LPADC_EnableFIFOWatermarkDMA(config->base, true);
588+
#endif
589+
590+
/* Configure and start DMA */
591+
if (dma_config(config->dma_dev, config->dma_channel, &data->dma_cfg) == 0) {
592+
(void)dma_start(config->dma_dev, config->dma_channel);
593+
}
594+
}
595+
}
596+
#endif /* CONFIG_LPADC_ENABLE_DMA */
597+
464598
static void adc_context_start_sampling(struct adc_context *ctx)
465599
{
466600
struct mcux_lpadc_data *data =
467601
CONTAINER_OF(ctx, struct mcux_lpadc_data, ctx);
602+
uint32_t channel_mask;
603+
uint8_t count = 0;
468604

469605
data->channels = ctx->sequence.channels;
470606
data->repeat_buffer = data->buffer;
607+
channel_mask = data->channels;
608+
609+
while (channel_mask) {
610+
count += (uint8_t)(channel_mask & 1U);
611+
channel_mask >>= 1U;
612+
}
613+
614+
/* Record the number of channels required for the sequence. */
615+
data->channels_count = count;
616+
617+
#ifdef CONFIG_LPADC_ENABLE_DMA
618+
mcux_lpadc_dma_configure(data);
619+
#endif
471620

472621
mcux_lpadc_start_channel(data->dev);
473622
}
@@ -638,15 +787,27 @@ static int mcux_lpadc_init(const struct device *dev)
638787
LPADC_DoAutoCalibration(base);
639788
#endif /* FSL_FEATURE_LPADC_HAS_CFG_CALOFS */
640789

641-
/* Enable the watermark interrupt. */
642-
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) \
643-
&& (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
644-
LPADC_EnableInterrupts(base, kLPADC_FIFO0WatermarkInterruptEnable);
790+
data->use_dma = false;
791+
792+
#if defined(CONFIG_LPADC_ENABLE_DMA)
793+
/* If DMA is present in DT and device is ready, use DMA and skip IRQs */
794+
if (config->dma_dev && device_is_ready(config->dma_dev)) {
795+
data->use_dma = true;
796+
} else {
797+
LOG_WRN("DMA device not ready, falling back to IRQ mode");
798+
}
799+
#endif
800+
801+
/* Enable the watermark interrupt if not using DMA or if DMA setup failed */
802+
if (!IS_ENABLED(CONFIG_LPADC_ENABLE_DMA) || !data->use_dma) {
803+
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
804+
LPADC_EnableInterrupts(base, kLPADC_FIFO0WatermarkInterruptEnable);
645805
#else
646-
LPADC_EnableInterrupts(base, kLPADC_FIFOWatermarkInterruptEnable);
647-
#endif /* FSL_FEATURE_LPADC_FIFO_COUNT */
806+
LPADC_EnableInterrupts(base, kLPADC_FIFOWatermarkInterruptEnable);
807+
#endif
808+
config->irq_config_func(dev);
809+
}
648810

649-
config->irq_config_func(dev);
650811
data->dev = dev;
651812

652813
/* Initialize OPAMP gain control context */
@@ -683,6 +844,18 @@ static DEVICE_API(adc, mcux_lpadc_driver_api) = {
683844
(COND_CODE_1(DT_NODE_HAS_PROP(OPAMP_NODE(n), programmable_gain), \
684845
(DT_PROP_LEN(OPAMP_NODE(n), programmable_gain)), (0))), (0)),
685846

847+
#if defined(CONFIG_LPADC_ENABLE_DMA)
848+
#define DMA_INIT(n) \
849+
.dma_dev = COND_CODE_1(DT_INST_DMAS_HAS_NAME(n, fifoa), \
850+
(DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(n, fifoa))), (NULL)), \
851+
.dma_channel = COND_CODE_1(DT_INST_DMAS_HAS_NAME(n, fifoa), \
852+
(DT_INST_DMAS_CELL_BY_NAME(n, fifoa, mux)), (0)), \
853+
.dma_slot = COND_CODE_1(DT_INST_DMAS_HAS_NAME(n, fifoa), \
854+
(DT_INST_DMAS_CELL_BY_NAME(n, fifoa, source)), (0)),
855+
#else
856+
#define DMA_INIT(n)
857+
#endif
858+
686859
#define LPADC_MCUX_INIT(n) \
687860
\
688861
static void mcux_lpadc_config_func_##n(const struct device *dev); \
@@ -718,6 +891,7 @@ static DEVICE_API(adc, mcux_lpadc_driver_api) = {
718891
.sample_max = COND_CODE_1(DT_INST_NODE_HAS_PROP(n, ideal_sample_range), \
719892
(DT_PROP_BY_IDX(DT_DRV_INST(n), ideal_sample_range, 1)), (UINT32_MAX)), \
720893
OPAMP_GAINS_INIT(n) \
894+
DMA_INIT(n) \
721895
}; \
722896
static struct mcux_lpadc_data mcux_lpadc_data_##n = { \
723897
ADC_CONTEXT_INIT_TIMER(mcux_lpadc_data_##n, ctx), \

0 commit comments

Comments
 (0)