/* * Copyright (c) 2017-2018, 2020, NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_kinetis_adc16 #include #include #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA #include #include #endif #include #define LOG_LEVEL CONFIG_ADC_LOG_LEVEL #include LOG_MODULE_REGISTER(adc_mcux_adc16); #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" struct mcux_adc16_config { ADC_Type *base; #ifndef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA void (*irq_config_func)(const struct device *dev); #endif uint32_t clk_source; /* ADC clock source selection */ uint32_t long_sample; /* ADC long sample mode selection */ uint32_t hw_trigger_src; /* ADC hardware trigger source */ /* defined in SIM module SOPT7 */ #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA uint32_t dma_slot; /* ADC DMA MUX slot */ #endif uint32_t trg_offset; uint32_t trg_bits; uint32_t alt_offset; uint32_t alt_bits; bool periodic_trigger; /* ADC enable periodic trigger */ bool channel_mux_b; bool high_speed; /* ADC enable high speed mode*/ bool continuous_convert; /* ADC enable continuous convert*/ }; #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA struct adc_edma_config { int32_t state; uint32_t dma_channel; void (*irq_call_back)(void); struct dma_config dma_cfg; struct dma_block_config dma_block; }; #endif struct mcux_adc16_data { const struct device *dev; struct adc_context ctx; #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA const struct device *dev_dma; struct adc_edma_config adc_dma_config; #endif uint16_t *buffer; uint16_t *repeat_buffer; uint32_t channels; uint8_t channel_id; }; #ifdef CONFIG_ADC_MCUX_ADC16_HW_TRIGGER #define SIM_SOPT7_ADCSET(x, shifts, mask) \ (((uint32_t)(((uint32_t)(x)) << shifts)) & mask) #endif #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA static void adc_dma_callback(const struct device *dma_dev, void *callback_arg, uint32_t channel, int error_code) { struct device *dev = (struct device *)callback_arg; struct mcux_adc16_data *data = dev->data; LOG_DBG("DMA done"); adc_context_on_sampling_done(&data->ctx, dev); } #endif #ifdef CONFIG_ADC_MCUX_ADC16_HW_TRIGGER static void adc_hw_trigger_enable(const struct device *dev) { const struct mcux_adc16_config *config = dev->config; /* enable ADC trigger channel */ SIM->SOPT7 |= SIM_SOPT7_ADCSET(config->hw_trigger_src, config->trg_offset, config->trg_bits) | SIM_SOPT7_ADCSET(1, config->alt_offset, config->alt_bits); } #endif static int mcux_adc16_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) { uint8_t channel_id = channel_cfg->channel_id; if (channel_id > (ADC_SC1_ADCH_MASK >> ADC_SC1_ADCH_SHIFT)) { LOG_ERR("Channel %d is not valid", channel_id); return -EINVAL; } if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { LOG_ERR("Invalid channel acquisition time"); return -EINVAL; } if (channel_cfg->differential) { LOG_ERR("Differential channels are not supported"); return -EINVAL; } if (channel_cfg->gain != ADC_GAIN_1) { LOG_ERR("Invalid channel gain"); return -EINVAL; } if (channel_cfg->reference != ADC_REF_INTERNAL) { LOG_ERR("Invalid channel reference"); return -EINVAL; } #ifdef CONFIG_ADC_MCUX_ADC16_HW_TRIGGER adc_hw_trigger_enable(dev); #endif return 0; } static int start_read(const struct device *dev, const struct adc_sequence *sequence) { const struct mcux_adc16_config *config = dev->config; struct mcux_adc16_data *data = dev->data; adc16_hardware_average_mode_t mode; adc16_resolution_t resolution; int error; uint32_t tmp32; ADC_Type *base = config->base; switch (sequence->resolution) { case 8: case 9: resolution = kADC16_Resolution8or9Bit; break; case 10: case 11: resolution = kADC16_Resolution10or11Bit; break; case 12: case 13: resolution = kADC16_Resolution12or13Bit; break; #if defined(FSL_FEATURE_ADC16_MAX_RESOLUTION) && \ (FSL_FEATURE_ADC16_MAX_RESOLUTION >= 16U) case 16: resolution = kADC16_Resolution16Bit; break; #endif default: LOG_ERR("Invalid resolution"); return -EINVAL; } tmp32 = base->CFG1 & ~(ADC_CFG1_MODE_MASK); tmp32 |= ADC_CFG1_MODE(resolution); base->CFG1 = tmp32; switch (sequence->oversampling) { case 0: mode = kADC16_HardwareAverageDisabled; break; case 2: mode = kADC16_HardwareAverageCount4; break; case 3: mode = kADC16_HardwareAverageCount8; break; case 4: mode = kADC16_HardwareAverageCount16; break; case 5: mode = kADC16_HardwareAverageCount32; break; default: LOG_ERR("Invalid oversampling"); return -EINVAL; } ADC16_SetHardwareAverage(config->base, mode); data->buffer = sequence->buffer; adc_context_start_read(&data->ctx, sequence); error = adc_context_wait_for_completion(&data->ctx); #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA dma_stop(data->dev_dma, data->adc_dma_config.dma_channel); #endif return error; } static int mcux_adc16_read(const struct device *dev, const struct adc_sequence *sequence) { struct mcux_adc16_data *data = dev->data; int error; adc_context_lock(&data->ctx, false, NULL); error = start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #ifdef CONFIG_ADC_ASYNC static int mcux_adc16_read_async(const struct device *dev, const struct adc_sequence *sequence, struct k_poll_signal *async) { struct mcux_adc16_data *data = dev->data; int error; adc_context_lock(&data->ctx, true, async); error = start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #endif static void mcux_adc16_start_channel(const struct device *dev) { const struct mcux_adc16_config *config = dev->config; struct mcux_adc16_data *data = dev->data; adc16_channel_config_t channel_config; uint32_t channel_group = 0U; data->channel_id = find_lsb_set(data->channels) - 1; LOG_DBG("Starting channel %d", data->channel_id); #if defined(FSL_FEATURE_ADC16_HAS_DIFF_MODE) && FSL_FEATURE_ADC16_HAS_DIFF_MODE channel_config.enableDifferentialConversion = false; #endif channel_config.enableInterruptOnConversionCompleted = true; channel_config.channelNumber = data->channel_id; ADC16_SetChannelConfig(config->base, channel_group, &channel_config); #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA LOG_DBG("Starting EDMA"); dma_start(data->dev_dma, data->adc_dma_config.dma_channel); #endif LOG_DBG("Starting channel done"); } static void adc_context_start_sampling(struct adc_context *ctx) { struct mcux_adc16_data *data = CONTAINER_OF(ctx, struct mcux_adc16_data, ctx); data->channels = ctx->sequence.channels; data->repeat_buffer = data->buffer; #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA LOG_DBG("config dma"); data->buffer = ctx->sequence.buffer; data->adc_dma_config.dma_block.block_size = ctx->sequence.buffer_size; data->adc_dma_config.dma_block.dest_address = (uint32_t)data->buffer; data->adc_dma_config.dma_cfg.head_block = &(data->adc_dma_config.dma_block); dma_config(data->dev_dma, data->adc_dma_config.dma_channel, &data->adc_dma_config.dma_cfg); #endif mcux_adc16_start_channel(data->dev); } static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) { struct mcux_adc16_data *data = CONTAINER_OF(ctx, struct mcux_adc16_data, ctx); if (repeat_sampling) { data->buffer = data->repeat_buffer; } } #ifndef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA static void mcux_adc16_isr(const struct device *dev) { const struct mcux_adc16_config *config = dev->config; struct mcux_adc16_data *data = dev->data; ADC_Type *base = config->base; uint32_t channel_group = 0U; uint16_t result; result = ADC16_GetChannelConversionValue(base, channel_group); LOG_DBG("Finished channel %d. Result is 0x%04x", data->channel_id, result); *data->buffer++ = result; data->channels &= ~BIT(data->channel_id); if (data->channels) { mcux_adc16_start_channel(dev); } else { adc_context_on_sampling_done(&data->ctx, dev); } } #endif static int mcux_adc16_init(const struct device *dev) { const struct mcux_adc16_config *config = dev->config; struct mcux_adc16_data *data = dev->data; ADC_Type *base = config->base; adc16_config_t adc_config; LOG_DBG("init adc"); ADC16_GetDefaultConfig(&adc_config); #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA adc_config.clockSource = (adc16_clock_source_t)config->clk_source; adc_config.longSampleMode = (adc16_long_sample_mode_t)config->long_sample; adc_config.enableHighSpeed = config->high_speed; adc_config.enableContinuousConversion = config->continuous_convert; #endif #if CONFIG_ADC_MCUX_ADC16_VREF_DEFAULT adc_config.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref; #else /* CONFIG_ADC_MCUX_ADC16_VREF_ALTERNATE */ adc_config.referenceVoltageSource = kADC16_ReferenceVoltageSourceValt; #endif #if CONFIG_ADC_MCUX_ADC16_CLK_DIV_RATIO_1 adc_config.clockDivider = kADC16_ClockDivider1; #elif CONFIG_ADC_MCUX_ADC16_CLK_DIV_RATIO_2 adc_config.clockDivider = kADC16_ClockDivider2; #elif CONFIG_ADC_MCUX_ADC16_CLK_DIV_RATIO_4 adc_config.clockDivider = kADC16_ClockDivider4; #else /* CONFIG_ADC_MCUX_ADC16_CLK_DIV_RATIO_8 */ adc_config.clockDivider = kADC16_ClockDivider8; #endif ADC16_Init(base, &adc_config); #if defined(FSL_FEATURE_ADC16_HAS_CALIBRATION) && \ FSL_FEATURE_ADC16_HAS_CALIBRATION ADC16_SetHardwareAverage(base, kADC16_HardwareAverageCount32); ADC16_DoAutoCalibration(base); #endif if (config->channel_mux_b) { ADC16_SetChannelMuxMode(base, kADC16_ChannelMuxB); } if (IS_ENABLED(CONFIG_ADC_MCUX_ADC16_HW_TRIGGER)) { ADC16_EnableHardwareTrigger(base, true); } else { ADC16_EnableHardwareTrigger(base, false); } data->dev = dev; /* dma related init */ #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA /* Enable DMA. */ ADC16_EnableDMA(base, true); data->adc_dma_config.dma_cfg.block_count = 1U; data->adc_dma_config.dma_cfg.dma_slot = config->dma_slot; data->adc_dma_config.dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY; data->adc_dma_config.dma_cfg.source_burst_length = 4U; data->adc_dma_config.dma_cfg.dest_burst_length = 4U; data->adc_dma_config.dma_cfg.channel_priority = 0U; data->adc_dma_config.dma_cfg.dma_callback = adc_dma_callback; data->adc_dma_config.dma_cfg.user_data = (void *)dev; data->adc_dma_config.dma_cfg.source_data_size = 4U; data->adc_dma_config.dma_cfg.dest_data_size = 4U; data->adc_dma_config.dma_block.source_address = (uint32_t)&base->R[0]; if (data->dev_dma == NULL || !device_is_ready(data->dev_dma)) { LOG_ERR("dma binding fail"); return -EINVAL; } if (config->periodic_trigger) { enum dma_channel_filter adc_filter = DMA_CHANNEL_PERIODIC; data->adc_dma_config.dma_channel = dma_request_channel(data->dev_dma, (void *)&adc_filter); } else { enum dma_channel_filter adc_filter = DMA_CHANNEL_NORMAL; data->adc_dma_config.dma_channel = dma_request_channel(data->dev_dma, (void *)&adc_filter); } if (data->adc_dma_config.dma_channel == -EINVAL) { LOG_ERR("can not allocate dma channel"); return -EINVAL; } LOG_DBG("dma allocated channel %d", data->adc_dma_config.dma_channel); #else config->irq_config_func(dev); #endif LOG_DBG("adc init done"); adc_context_unlock_unconditionally(&data->ctx); return 0; } static const struct adc_driver_api mcux_adc16_driver_api = { .channel_setup = mcux_adc16_channel_setup, .read = mcux_adc16_read, #ifdef CONFIG_ADC_ASYNC .read_async = mcux_adc16_read_async, #endif }; #ifdef CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA #define ACD16_MCUX_INIT(n) \ static const struct mcux_adc16_config mcux_adc16_config_##n = { \ .base = (ADC_Type *)DT_INST_REG_ADDR(n), \ .channel_mux_b = DT_INST_PROP(n, channel_mux_b), \ .clk_source = DT_INST_PROP_OR(n, clk_source, 0), \ .long_sample = DT_INST_PROP_OR(n, long_sample, 0), \ .high_speed = DT_INST_PROP(n, high_speed), \ .periodic_trigger = DT_INST_PROP(n, periodic_trigger), \ .continuous_convert = \ DT_INST_PROP(n, continuous_convert), \ .hw_trigger_src = \ DT_INST_PROP_OR(n, hw_trigger_src, 0), \ .dma_slot = DT_INST_DMAS_CELL_BY_IDX(n, 0, source), \ .trg_offset = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, offset), \ .trg_bits = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, bits), \ .alt_offset = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, offset), \ .alt_bits = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, bits), \ }; \ \ static struct mcux_adc16_data mcux_adc16_data_##n = { \ ADC_CONTEXT_INIT_TIMER(mcux_adc16_data_##n, ctx), \ ADC_CONTEXT_INIT_LOCK(mcux_adc16_data_##n, ctx), \ ADC_CONTEXT_INIT_SYNC(mcux_adc16_data_##n, ctx), \ .dev_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(n, adc##n)), \ }; \ \ DEVICE_DT_INST_DEFINE(n, &mcux_adc16_init, \ NULL, \ &mcux_adc16_data_##n, \ &mcux_adc16_config_##n, \ POST_KERNEL, \ CONFIG_ADC_INIT_PRIORITY, \ &mcux_adc16_driver_api); #else #define ACD16_MCUX_INIT(n) \ static void mcux_adc16_config_func_##n(const struct device *dev); \ static const struct mcux_adc16_config mcux_adc16_config_##n = { \ .base = (ADC_Type *)DT_INST_REG_ADDR(n), \ .irq_config_func = mcux_adc16_config_func_##n, \ .channel_mux_b = DT_INST_PROP(n, channel_mux_b), \ .clk_source = DT_INST_PROP_OR(n, clk_source, 0), \ .long_sample = DT_INST_PROP_OR(n, long_sample, 0), \ .high_speed = DT_INST_PROP(n, high_speed), \ .continuous_convert = \ DT_INST_PROP(n, continuous_convert), \ }; \ static struct mcux_adc16_data mcux_adc16_data_##n = { \ ADC_CONTEXT_INIT_TIMER(mcux_adc16_data_##n, ctx), \ ADC_CONTEXT_INIT_LOCK(mcux_adc16_data_##n, ctx), \ ADC_CONTEXT_INIT_SYNC(mcux_adc16_data_##n, ctx), \ }; \ \ DEVICE_DT_INST_DEFINE(n, &mcux_adc16_init, \ NULL, \ &mcux_adc16_data_##n, \ &mcux_adc16_config_##n, \ POST_KERNEL, \ CONFIG_ADC_INIT_PRIORITY, \ &mcux_adc16_driver_api); \ \ static void mcux_adc16_config_func_##n(const struct device *dev) \ { \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ mcux_adc16_isr, \ DEVICE_DT_INST_GET(n), 0); \ \ irq_enable(DT_INST_IRQN(n)); \ } #endif DT_INST_FOREACH_STATUS_OKAY(ACD16_MCUX_INIT)