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
6575struct 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
82100static 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+
464598static 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