drivers: adc: esp32: dma enable

adc dma mode operation on ESP32S3 and ESP32C3

Signed-off-by: Marcio Ribeiro <marcio.ribeiro@espressif.com>
This commit is contained in:
Marcio Ribeiro 2024-01-18 10:45:38 -03:00 committed by Carles Cufí
parent bb770c5965
commit 19d8ade5fa
2 changed files with 411 additions and 1 deletions

View file

@ -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

View file

@ -9,12 +9,25 @@
#include <errno.h>
#include <hal/adc_hal.h>
#include <hal/adc_types.h>
#include <soc/adc_periph.h>
#include <esp_adc_cal.h>
#include <esp_clk_tree.h>
#include <esp_private/periph_ctrl.h>
#include <esp_private/sar_periph_ctrl.h>
#include <esp_private/adc_share_hw_ctrl.h>
#if defined(CONFIG_ADC_ESP32_DMA)
#if !SOC_GDMA_SUPPORTED
#error "SoCs without GDMA peripheral are not supported!"
#endif
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/dma/dma_esp32.h>
#endif
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
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 = { \