/*
 * Copyright (c) 2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT espressif_esp32_adc

#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);

#define ADC_RESOLUTION_MIN	SOC_ADC_DIGI_MIN_BITWIDTH
#define ADC_RESOLUTION_MAX	SOC_ADC_DIGI_MAX_BITWIDTH

#if CONFIG_SOC_SERIES_ESP32
#define ADC_CALI_SCHEME		ESP_ADC_CAL_VAL_EFUSE_VREF
/* Due to significant measurement discrepancy in higher voltage range, we
 * clip the value instead of yet another correction. The IDF implementation
 * for ESP32-S2 is doing it, so we copy that approach in Zephyr driver
 */
#define ADC_CLIP_MVOLT_12DB     2550
#elif CONFIG_SOC_SERIES_ESP32S3
#define ADC_CALI_SCHEME		ESP_ADC_CAL_VAL_EFUSE_TP_FIT
#else
#define ADC_CALI_SCHEME		ESP_ADC_CAL_VAL_EFUSE_TP
#endif

/* Validate if resolution in bits is within allowed values */
#define VALID_RESOLUTION(r) ((r) >= ADC_RESOLUTION_MIN && (r) <= ADC_RESOLUTION_MAX)
#define INVALID_RESOLUTION(r) (!VALID_RESOLUTION(r))

/* 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 {
	adc_atten_t attenuation[SOC_ADC_MAX_CHANNEL_NUM];
	uint8_t resolution[SOC_ADC_MAX_CHANNEL_NUM];
	esp_adc_cal_characteristics_t chars[SOC_ADC_MAX_CHANNEL_NUM];
	uint16_t meas_ref_internal;
	uint16_t *buffer;
	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 */
static inline int gain_to_atten(enum adc_gain gain, adc_atten_t *atten)
{
	switch (gain) {
	case ADC_GAIN_1:
		*atten = ADC_ATTEN_DB_0;
		break;
	case ADC_GAIN_4_5:
		*atten = ADC_ATTEN_DB_2_5;
		break;
	case ADC_GAIN_1_2:
		*atten = ADC_ATTEN_DB_6;
		break;
	case ADC_GAIN_1_4:
		*atten = ADC_ATTEN_DB_12;
		break;
	default:
		return -ENOTSUP;
	}
	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)
{
	if (!val_mv) {
		return;
	}
	switch (atten) {
	case ADC_ATTEN_DB_2_5:
		*val_mv = (*val_mv * 4) / 5; /* 1/ADC_GAIN_4_5 */
		break;
	case ADC_ATTEN_DB_6:
		*val_mv = *val_mv >> 1; /* 1/ADC_GAIN_1_2 */
		break;
	case ADC_ATTEN_DB_12:
		*val_mv = *val_mv / 4; /* 1/ADC_GAIN_1_4 */
		break;
	case ADC_ATTEN_DB_0: /* 1/ADC_GAIN_1 */
	default:
		break;
	}
}
#endif /* !defined(CONFIG_ADC_ESP32_DMA) */

static void adc_hw_calibration(adc_unit_t unit)
{
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
	adc_hal_calibration_init(unit);
	for (int j = 0; j < SOC_ADC_ATTEN_NUM; j++) {
		adc_calc_hw_calibration_code(unit, j);
#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
		/* Load the channel compensation from efuse */
		for (int k = 0; k < SOC_ADC_CHANNEL_NUM(unit); k++) {
			adc_load_hw_calibration_chan_compens(unit, k, j);
		}
#endif /* SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED */
	}
#endif /* SOC_ADC_CALIBRATION_V1_SUPPORTED */
}

static bool adc_calibration_init(const struct device *dev)
{
	switch (esp_adc_cal_check_efuse(ADC_CALI_SCHEME)) {
	case ESP_ERR_NOT_SUPPORTED:
		LOG_WRN("Skip software calibration - Not supported!");
		break;
	case ESP_ERR_INVALID_VERSION:
		LOG_WRN("Skip software calibration - Invalid version!");
		break;
	case ESP_OK:
		LOG_DBG("Software calibration possible");
		return true;
	default:
		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;
	struct adc_esp32_data *data = dev->data;
	int reading;
	uint32_t cal, cal_mv;

	uint8_t channel_id = find_lsb_set(seq->channels) - 1;

	if (seq->buffer_size < 2) {
		LOG_ERR("Sequence buffer space too low '%d'", seq->buffer_size);
		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) {
			LOG_ERR("Extra samplings not supported");
			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)) {
		LOG_ERR("unsupported resolution (%d)", seq->resolution);
		return -ENOTSUP;
	}

	if (seq->calibrate) {
		/* TODO: Does this mean actual Vref measurement on selected GPIO ?*/
		LOG_ERR("calibration is not supported");
		return -ENOTSUP;
	}

	data->resolution[channel_id] = seq->resolution;

#if CONFIG_SOC_SERIES_ESP32C3
	/* NOTE: nothing to set on ESP32C3 SoC */
	if (conf->unit == ADC_UNIT_1) {
		adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
	}
#else
	adc_set_data_width(conf->unit, 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);
	}
	if (conf->unit == ADC_UNIT_2) {
		if (adc2_get_raw(channel_id, ADC_WIDTH_BIT_DEFAULT, &reading)) {
			LOG_ERR("Conversion timeout on '%s' channel %d", dev->name, channel_id);
			return -ETIMEDOUT;
		}
	}

	/* Calibration scheme is available */
	if (data->calibrate) {
		data->chars[channel_id].bit_width = data->resolution[channel_id];
		/* Get corrected voltage output */
		cal = cal_mv = esp_adc_cal_raw_to_voltage(reading, &data->chars[channel_id]);

#if CONFIG_SOC_SERIES_ESP32
		if (data->attenuation[channel_id] == ADC_ATTEN_DB_12) {
			if (cal > ADC_CLIP_MVOLT_12DB) {
				cal = ADC_CLIP_MVOLT_12DB;
			}
		}
#endif /* CONFIG_SOC_SERIES_ESP32 */

		/* Fit according to selected attenuation */
		atten_to_gain(data->attenuation[channel_id], &cal);
		if (data->meas_ref_internal > 0) {
			cal = (cal << data->resolution[channel_id]) / data->meas_ref_internal;
		}
	} else {
		LOG_DBG("Using uncalibrated values!");
		/* Uncalibrated raw value */
		cal = reading;
	}

	/* Store result */
	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;
}

#ifdef CONFIG_ADC_ASYNC
static int adc_esp32_read_async(const struct device *dev,
				const struct adc_sequence *sequence,
				struct k_poll_signal *async)
{
	(void)(dev);
	(void)(sequence);
	(void)(async);

	return -ENOTSUP;
}
#endif /* CONFIG_ADC_ASYNC */

static int adc_esp32_channel_setup(const struct device *dev, const struct adc_channel_cfg *cfg)
{
	const struct adc_esp32_conf *conf = (const struct adc_esp32_conf *)dev->config;
	struct adc_esp32_data *data = (struct adc_esp32_data *) dev->data;

	if (cfg->channel_id >= conf->channel_count) {
		LOG_ERR("Unsupported channel id '%d'", cfg->channel_id);
		return -ENOTSUP;
	}

	if (cfg->reference != ADC_REF_INTERNAL) {
		LOG_ERR("Unsupported channel reference '%d'", cfg->reference);
		return -ENOTSUP;
	}

	if (cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
		LOG_ERR("Unsupported acquisition_time '%d'", cfg->acquisition_time);
		return -ENOTSUP;
	}

	if (cfg->differential) {
		LOG_ERR("Differential channels are not supported");
		return -ENOTSUP;
	}

	if (gain_to_atten(cfg->gain, &data->attenuation[cfg->channel_id])) {
		LOG_ERR("Unsupported gain value '%d'", cfg->gain);
		return -ENOTSUP;
	}

	/* Prepare channel */
	if (conf->unit == ADC_UNIT_1) {
		adc1_config_channel_atten(cfg->channel_id, data->attenuation[cfg->channel_id]);
	}
	if (conf->unit == ADC_UNIT_2) {
		adc2_config_channel_atten(cfg->channel_id, data->attenuation[cfg->channel_id]);
	}

	if (data->calibrate) {
		esp_adc_cal_value_t cal = esp_adc_cal_characterize(conf->unit,
						data->attenuation[cfg->channel_id],
						data->resolution[cfg->channel_id],
						data->meas_ref_internal,
						&data->chars[cfg->channel_id]);
		if (cal >= ESP_ADC_CAL_VAL_NOT_SUPPORTED) {
			LOG_ERR("Calibration error or not supported");
			return -EIO;
		}
		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,
	};

	int 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;
}

static int adc_esp32_init(const struct device *dev)
{
	struct adc_esp32_data *data = (struct adc_esp32_data *) dev->data;
	const struct adc_esp32_conf *conf = (struct adc_esp32_conf *) dev->config;

	adc_hw_calibration(conf->unit);

#if CONFIG_SOC_SERIES_ESP32S2 || CONFIG_SOC_SERIES_ESP32C3
	if (conf->unit == ADC_UNIT_2) {
		adc2_init_code_calibration();
	}
#endif /* CONFIG_SOC_SERIES_ESP32S2 || CONFIG_SOC_SERIES_ESP32C3 */

#if defined(CONFIG_ADC_ESP32_DMA)
	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;
	}

	/* Default reference voltage. This could be calibrated externaly */
	data->meas_ref_internal = ADC_ESP32_DEFAULT_VREF_INTERNAL;

	/* Check if calibration is possible */
	data->calibrate = adc_calibration_init(dev);

	return 0;
}

static DEVICE_API(adc, api_esp32_driver_api) = {
	.channel_setup = adc_esp32_channel_setup,
	.read          = adc_esp32_read,
#ifdef CONFIG_ADC_ASYNC
	.read_async    = adc_esp32_read_async,
#endif /* CONFIG_ADC_ASYNC */
	.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 = {			\
	};									\
										\
DEVICE_DT_INST_DEFINE(inst, &adc_esp32_init, NULL,				\
		&adc_esp32_data_##inst,						\
		&adc_esp32_conf_##inst,						\
		POST_KERNEL,							\
		CONFIG_ADC_INIT_PRIORITY,					\
		&api_esp32_driver_api);

DT_INST_FOREACH_STATUS_OKAY(ESP32_ADC_INIT)
