diff --git a/drivers/adc/Kconfig.esp32 b/drivers/adc/Kconfig.esp32 index 8fc8c9cabce..604d0ad49ed 100644 --- a/drivers/adc/Kconfig.esp32 +++ b/drivers/adc/Kconfig.esp32 @@ -8,3 +8,15 @@ config ADC_ESP32 depends on DT_HAS_ESPRESSIF_ESP32_ADC_ENABLED help Enable the driver implementation for the ESP32 ADC + +if ADC_ESP32 + +config ADC_ESP32_DMA + bool "ESP32 ADC DMA Support" + default n + depends on DT_HAS_ESPRESSIF_ESP32_GDMA_ENABLED + help + Enable the ADC DMA mode for ADC instances + that enable dma channels in their device tree node. + +endif diff --git a/drivers/adc/adc_esp32.c b/drivers/adc/adc_esp32.c index 9b47fdf6bd7..4e45a3ea22e 100644 --- a/drivers/adc/adc_esp32.c +++ b/drivers/adc/adc_esp32.c @@ -9,12 +9,25 @@ #include #include #include +#include #include +#include #include +#include +#include + +#if defined(CONFIG_ADC_ESP32_DMA) +#if !SOC_GDMA_SUPPORTED +#error "SoCs without GDMA peripheral are not supported!" +#endif +#include +#include +#endif #include #include #include +#include #include LOG_MODULE_REGISTER(adc_esp32, CONFIG_ADC_LOG_LEVEL); @@ -43,9 +56,16 @@ LOG_MODULE_REGISTER(adc_esp32, CONFIG_ADC_LOG_LEVEL); /* Default internal reference voltage */ #define ADC_ESP32_DEFAULT_VREF_INTERNAL (1100) +#define ADC_DMA_BUFFER_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + struct adc_esp32_conf { adc_unit_t unit; uint8_t channel_count; +#if defined(CONFIG_ADC_ESP32_DMA) + const struct device *gpio_port; + const struct device *dma_dev; + uint8_t dma_channel; +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ }; struct adc_esp32_data { @@ -54,8 +74,12 @@ struct adc_esp32_data { esp_adc_cal_characteristics_t chars[SOC_ADC_MAX_CHANNEL_NUM]; uint16_t meas_ref_internal; uint16_t *buffer; - uint16_t *buffer_repeat; bool calibrate; +#if defined(CONFIG_ADC_ESP32_DMA) + adc_hal_dma_ctx_t adc_hal_dma_ctx; + uint8_t *dma_buffer; + struct k_sem dma_conv_wait_lock; +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ }; /* Convert zephyr,gain property to the ESP32 attenuation */ @@ -80,6 +104,7 @@ static inline int gain_to_atten(enum adc_gain gain, adc_atten_t *atten) return 0; } +#if !defined(CONFIG_ADC_ESP32_DMA) /* Convert voltage by inverted attenuation to support zephyr gain values */ static void atten_to_gain(adc_atten_t atten, uint32_t *val_mv) { @@ -101,6 +126,7 @@ static void atten_to_gain(adc_atten_t atten, uint32_t *val_mv) break; } } +#endif /* !defined(CONFIG_ADC_ESP32_DMA) */ static bool adc_calibration_init(const struct device *dev) { @@ -120,9 +146,229 @@ static bool adc_calibration_init(const struct device *dev) LOG_ERR("Invalid arg"); break; } + return false; } +#if defined(CONFIG_ADC_ESP32_DMA) + +static void IRAM_ATTR adc_esp32_dma_conv_done(const struct device *dma_dev, void *user_data, + uint32_t channel, int status) +{ + ARG_UNUSED(dma_dev); + ARG_UNUSED(status); + + const struct device *dev = user_data; + struct adc_esp32_data *data = dev->data; + + k_sem_give(&data->dma_conv_wait_lock); +} + +static int adc_esp32_dma_start(const struct device *dev, uint8_t *buf, size_t len) +{ + const struct adc_esp32_conf *conf = dev->config; + struct adc_esp32_data *data = dev->data; + + int err = 0; + struct dma_config dma_cfg = {0}; + struct dma_status dma_status = {0}; + struct dma_block_config dma_blk = {0}; + + err = dma_get_status(conf->dma_dev, conf->dma_channel, &dma_status); + if (err) { + LOG_ERR("Unable to get dma channel[%u] status (%d)", + (unsigned int)conf->dma_channel, err); + return -EINVAL; + } + + if (dma_status.busy) { + LOG_ERR("dma channel[%u] is busy!", (unsigned int)conf->dma_channel); + return -EBUSY; + } + + unsigned int key = irq_lock(); + + dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY; + dma_cfg.dma_callback = adc_esp32_dma_conv_done; + dma_cfg.user_data = (void *)dev; + dma_cfg.dma_slot = ESP_GDMA_TRIG_PERIPH_ADC0; + dma_cfg.block_count = 1; + dma_cfg.head_block = &dma_blk; + dma_blk.block_size = len; + dma_blk.dest_address = (uint32_t)buf; + + err = dma_config(conf->dma_dev, conf->dma_channel, &dma_cfg); + if (err) { + LOG_ERR("Error configuring dma (%d)", err); + goto unlock; + } + + err = dma_start(conf->dma_dev, conf->dma_channel); + if (err) { + LOG_ERR("Error starting dma (%d)", err); + goto unlock; + } + +unlock: + irq_unlock(key); + return err; +} + +static int adc_esp32_dma_stop(const struct device *dev) +{ + const struct adc_esp32_conf *conf = dev->config; + unsigned int key = irq_lock(); + int err = 0; + + err = dma_stop(conf->dma_dev, conf->dma_channel); + if (err) { + LOG_ERR("Error stopping dma (%d)", err); + } + + irq_unlock(key); + return err; +} + +static int adc_esp32_fill_digi_pattern(const struct device *dev, const struct adc_sequence *seq, + void *pattern_config, uint32_t *pattern_len, uint32_t *unit_attenuation) +{ + const struct adc_esp32_conf *conf = dev->config; + struct adc_esp32_data *data = dev->data; + + adc_digi_pattern_config_t *adc_digi_pattern_config = + (adc_digi_pattern_config_t *)pattern_config; + const uint32_t unit_atten_uninit = 999; + uint32_t channel_mask = 1, channels_copy = seq->channels; + + *pattern_len = 0; + *unit_attenuation = unit_atten_uninit; + for (uint8_t channel_id = 0; channel_id < conf->channel_count; channel_id++) { + if (channels_copy & channel_mask) { + if (*unit_attenuation == unit_atten_uninit) { + *unit_attenuation = data->attenuation[channel_id]; + } else if (*unit_attenuation != data->attenuation[channel_id]) { + LOG_ERR("Channel[%u] attenuation different of unit[%u] attenuation", + (unsigned int)channel_id, (unsigned int)conf->unit); + return -EINVAL; + } + + adc_digi_pattern_config->atten = data->attenuation[channel_id]; + adc_digi_pattern_config->channel = channel_id; + adc_digi_pattern_config->unit = conf->unit; + adc_digi_pattern_config->bit_width = seq->resolution; + adc_digi_pattern_config++; + + *pattern_len += 1; + if (*pattern_len > SOC_ADC_PATT_LEN_MAX) { + LOG_ERR("Max pattern len is %d", SOC_ADC_PATT_LEN_MAX); + return -EINVAL; + } + + channels_copy &= ~channel_mask; + if (!channels_copy) { + break; + } + } + channel_mask <<= 1; + } + + return 0; +} + +static void adc_esp32_digi_start(const struct device *dev, void *pattern_config, + uint32_t pattern_len, uint32_t number_of_samplings, + uint32_t sample_freq_hz, uint32_t unit_attenuation) +{ + const struct adc_esp32_conf *conf = dev->config; + struct adc_esp32_data *data = dev->data; + + sar_periph_ctrl_adc_continuous_power_acquire(); + adc_lock_acquire(conf->unit); + +#if SOC_ADC_CALIBRATION_V1_SUPPORTED + adc_set_hw_calibration_code(conf->unit, unit_attenuation); +#endif /* SOC_ADC_CALIBRATION_V1_SUPPORTED */ + +#if SOC_ADC_ARBITER_SUPPORTED + if (conf->unit == ADC_UNIT_2) { + adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT(); + + adc_hal_arbiter_config(&config); + } +#endif /* SOC_ADC_ARBITER_SUPPORTED */ + + adc_hal_digi_ctrlr_cfg_t adc_hal_digi_ctrlr_cfg; + soc_module_clk_t clk_src = ADC_DIGI_CLK_SRC_DEFAULT; + uint32_t clk_src_freq_hz = 0; + + esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, + &clk_src_freq_hz); + + adc_hal_digi_ctrlr_cfg.conv_mode = + (conf->unit == ADC_UNIT_1)?ADC_CONV_SINGLE_UNIT_1:ADC_CONV_SINGLE_UNIT_2; + adc_hal_digi_ctrlr_cfg.clk_src = clk_src; + adc_hal_digi_ctrlr_cfg.clk_src_freq_hz = clk_src_freq_hz; + adc_hal_digi_ctrlr_cfg.sample_freq_hz = sample_freq_hz; + adc_hal_digi_ctrlr_cfg.adc_pattern = (adc_digi_pattern_config_t *)pattern_config; + adc_hal_digi_ctrlr_cfg.adc_pattern_len = pattern_len; + + uint32_t number_of_adc_digi_samples = number_of_samplings * pattern_len; + + adc_hal_dma_config_t adc_hal_dma_config = { + .dev = (void *)GDMA_LL_GET_HW(0), + .eof_desc_num = 1, + .eof_step = 1, + .dma_chan = conf->dma_channel, + .eof_num = number_of_adc_digi_samples, + }; + + adc_hal_dma_ctx_config(&data->adc_hal_dma_ctx, &adc_hal_dma_config); + + adc_hal_set_controller(conf->unit, ADC_HAL_CONTINUOUS_READ_MODE); + adc_hal_digi_init(&data->adc_hal_dma_ctx); + adc_hal_digi_controller_config(&data->adc_hal_dma_ctx, &adc_hal_digi_ctrlr_cfg); + adc_hal_digi_start(&data->adc_hal_dma_ctx, data->dma_buffer); +} + +static void adc_esp32_digi_stop(const struct device *dev) +{ + const struct adc_esp32_conf *conf = dev->config; + struct adc_esp32_data *data = dev->data; + + adc_hal_digi_dis_intr(&data->adc_hal_dma_ctx, ADC_HAL_DMA_INTR_MASK); + adc_hal_digi_clr_intr(&data->adc_hal_dma_ctx, ADC_HAL_DMA_INTR_MASK); + adc_hal_digi_stop(&data->adc_hal_dma_ctx); + adc_hal_digi_deinit(&data->adc_hal_dma_ctx); + adc_lock_release(conf->unit); + sar_periph_ctrl_adc_continuous_power_release(); +} + +static void adc_esp32_fill_seq_buffer(const void *seq_buffer, const void *dma_buffer, + uint32_t number_of_samples) +{ + uint16_t *sample = (uint16_t *)seq_buffer; + adc_digi_output_data_t *digi_data = (adc_digi_output_data_t *)dma_buffer; + + for (uint32_t k = 0; k < number_of_samples; k++) { + *sample++ = (uint16_t)(digi_data++)->type2.data; + } +} + +static int adc_esp32_wait_for_dma_conv_done(const struct device *dev) +{ + struct adc_esp32_data *data = dev->data; + int err = 0; + + err = k_sem_take(&data->dma_conv_wait_lock, K_FOREVER); + if (err) { + LOG_ERR("Error taking dma_conv_wait_lock (%d)", err); + } + + return err; +} + +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ + static int adc_esp32_read(const struct device *dev, const struct adc_sequence *seq) { const struct adc_esp32_conf *conf = dev->config; @@ -137,10 +383,12 @@ static int adc_esp32_read(const struct device *dev, const struct adc_sequence *s return -ENOMEM; } +#if !defined(CONFIG_ADC_ESP32_DMA) if (seq->channels > BIT(channel_id)) { LOG_ERR("Multi-channel readings not supported"); return -ENOTSUP; } +#endif /* !defined(CONFIG_ADC_ESP32_DMA) */ if (seq->options) { if (seq->options->extra_samplings) { @@ -148,10 +396,12 @@ static int adc_esp32_read(const struct device *dev, const struct adc_sequence *s return -ENOTSUP; } +#if !defined(CONFIG_ADC_ESP32_DMA) if (seq->options->interval_us) { LOG_ERR("Interval between samplings not supported"); return -ENOTSUP; } +#endif /* !defined(CONFIG_ADC_ESP32_DMA) */ } if (INVALID_RESOLUTION(seq->resolution)) { @@ -176,6 +426,7 @@ static int adc_esp32_read(const struct device *dev, const struct adc_sequence *s adc_set_data_width(conf->unit, WIDTH_MASK(data->resolution[channel_id])); #endif /* CONFIG_SOC_SERIES_ESP32C3 */ +#if !defined(CONFIG_ADC_ESP32_DMA) /* Read raw value */ if (conf->unit == ADC_UNIT_1) { reading = adc1_get_raw(channel_id); @@ -216,6 +467,74 @@ static int adc_esp32_read(const struct device *dev, const struct adc_sequence *s data->buffer = (uint16_t *) seq->buffer; data->buffer[0] = cal; +#else /* !defined(CONFIG_ADC_ESP32_DMA) */ + + int err = 0; + uint32_t adc_pattern_len, unit_attenuation; + adc_digi_pattern_config_t adc_digi_pattern_config[SOC_ADC_MAX_CHANNEL_NUM]; + + err = adc_esp32_fill_digi_pattern(dev, seq, &adc_digi_pattern_config, + &adc_pattern_len, &unit_attenuation); + if (err || adc_pattern_len == 0) { + return -EINVAL; + } + + const struct adc_sequence_options *options = seq->options; + uint32_t sample_freq_hz = SOC_ADC_SAMPLE_FREQ_THRES_HIGH, + number_of_samplings = 1; + + if (options != NULL) { + number_of_samplings = seq->buffer_size / (adc_pattern_len * sizeof(uint16_t)); + + if (options->interval_us) { + sample_freq_hz = MHZ(1) / options->interval_us; + } + } + + if (!number_of_samplings) { + LOG_ERR("buffer_size insufficient to store at least one set of samples!"); + return -EINVAL; + } + + if (sample_freq_hz < SOC_ADC_SAMPLE_FREQ_THRES_LOW || + sample_freq_hz > SOC_ADC_SAMPLE_FREQ_THRES_HIGH) { + LOG_ERR("ADC sampling frequency out of range: %uHz", sample_freq_hz); + return -EINVAL; + } + + uint32_t number_of_adc_samples = number_of_samplings * adc_pattern_len; + uint32_t number_of_adc_dma_data_bytes = + number_of_adc_samples * SOC_ADC_DIGI_DATA_BYTES_PER_CONV; + + if (number_of_adc_dma_data_bytes > ADC_DMA_BUFFER_SIZE) { + LOG_ERR("dma buffer size insufficient to store a complete sequence!"); + return -EINVAL; + } + + err = adc_esp32_dma_start(dev, data->dma_buffer, number_of_adc_dma_data_bytes); + if (err) { + return err; + } + + adc_esp32_digi_start(dev, &adc_digi_pattern_config, adc_pattern_len, number_of_samplings, + sample_freq_hz, unit_attenuation); + + err = adc_esp32_wait_for_dma_conv_done(dev); + if (err) { + return err; + } + + adc_esp32_digi_stop(dev); + + err = adc_esp32_dma_stop(dev); + if (err) { + return err; + } + + adc_esp32_fill_seq_buffer(seq->buffer, data->dma_buffer, number_of_adc_samples); + +#endif /* !defined(CONFIG_ADC_ESP32_DMA) */ + return 0; } @@ -284,6 +603,34 @@ static int adc_esp32_channel_setup(const struct device *dev, const struct adc_ch LOG_DBG("Using ADC calibration method %d", cal); } +#if defined(CONFIG_ADC_ESP32_DMA) + + if (!SOC_ADC_DIG_SUPPORTED_UNIT(conf->unit)) { + LOG_ERR("ADC2 dma mode is no longer supported, please use ADC1!"); + return -EINVAL; + } + + int io_num = adc_channel_io_map[conf->unit][cfg->channel_id]; + + if (io_num < 0) { + LOG_ERR("Channel %u not supported!", cfg->channel_id); + return -ENOTSUP; + } + + struct gpio_dt_spec gpio = { + .port = conf->gpio_port, + .dt_flags = 0, + .pin = io_num, + }; + + err = gpio_pin_configure_dt(&gpio, GPIO_DISCONNECTED); + if (err) { + LOG_ERR("Error disconnecting io (%d)", io_num); + return err; + } + +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ + return 0; } @@ -291,9 +638,41 @@ static int adc_esp32_init(const struct device *dev) { struct adc_esp32_data *data = (struct adc_esp32_data *) dev->data; +#if defined(CONFIG_ADC_ESP32_DMA) + struct adc_esp32_conf *conf = (struct adc_esp32_conf *) dev->config; + + if (!device_is_ready(conf->gpio_port)) { + LOG_ERR("gpio0 port not ready"); + return -ENODEV; + } + + if (k_sem_init(&data->dma_conv_wait_lock, 0, 1)) { + LOG_ERR("dma_conv_wait_lock initialization failed!"); + return -EINVAL; + } + + data->adc_hal_dma_ctx.rx_desc = k_aligned_alloc(sizeof(uint32_t), + sizeof(dma_descriptor_t)); + if (!data->adc_hal_dma_ctx.rx_desc) { + LOG_ERR("rx_desc allocation failed!"); + return -ENOMEM; + } + LOG_DBG("rx_desc = 0x%08X", (unsigned int)data->adc_hal_dma_ctx.rx_desc); + + data->dma_buffer = k_aligned_alloc(sizeof(uint32_t), ADC_DMA_BUFFER_SIZE); + if (!data->dma_buffer) { + LOG_ERR("dma buffer allocation failed!"); + k_free(data->adc_hal_dma_ctx.rx_desc); + return -ENOMEM; + } + LOG_DBG("data->dma_buffer = 0x%08X", (unsigned int)data->dma_buffer); + +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ + for (uint8_t i = 0; i < ARRAY_SIZE(data->resolution); i++) { data->resolution[i] = ADC_RESOLUTION_MAX; } + for (uint8_t i = 0; i < ARRAY_SIZE(data->attenuation); i++) { data->attenuation[i] = ADC_ATTEN_DB_0; } @@ -316,11 +695,30 @@ static const struct adc_driver_api api_esp32_driver_api = { .ref_internal = ADC_ESP32_DEFAULT_VREF_INTERNAL, }; +#if defined(CONFIG_ADC_ESP32_DMA) + +#define ADC_ESP32_CONF_GPIO_PORT_INIT .gpio_port = DEVICE_DT_GET(DT_NODELABEL(gpio0)), + +#define ADC_ESP32_CONF_DMA_INIT(n) .dma_dev = COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), \ + (DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_IDX(n, 0))), \ + (NULL)), \ + .dma_channel = COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), \ + (DT_INST_DMAS_CELL_BY_IDX(n, 0, channel)), \ + (0xff)), +#else + +#define ADC_ESP32_CONF_GPIO_PORT_INIT +#define ADC_ESP32_CONF_DMA_INIT(inst) + +#endif /* defined(CONFIG_ADC_ESP32_DMA) */ + #define ESP32_ADC_INIT(inst) \ \ static const struct adc_esp32_conf adc_esp32_conf_##inst = { \ .unit = DT_PROP(DT_DRV_INST(inst), unit) - 1, \ .channel_count = DT_PROP(DT_DRV_INST(inst), channel_count), \ + ADC_ESP32_CONF_GPIO_PORT_INIT \ + ADC_ESP32_CONF_DMA_INIT(inst) \ }; \ \ static struct adc_esp32_data adc_esp32_data_##inst = { \