/**
 * @file ad2s1210.c
 * @brief ad2s1210 driver module
 *
 * @copyright Copyright (c) 2025, Kickmaker
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#define DT_DRV_COMPAT adi_ad2s1210

#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util_macro.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ad2s1210, CONFIG_SENSOR_LOG_LEVEL);

/** Position register address */
#define AD2S1210_REG_POSITION         0x80
/** Velocity register address */
#define AD2S1210_REG_VELOCITY         0x82
/** Loss of Signal threshold register address */
#define AD2S1210_REG_LOS_THRD         0x88
/** Degradation of Signal overrange threshold register address */
#define AD2S1210_REG_DOS_OVR_THRD     0x89
/** Degradation of Signal mismatch threshold register address */
#define AD2S1210_REG_DOS_MIS_THRD     0x8A
/** Degradation of Signal reset maximum threshold register address */
#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B
/** Degradation of Signal reset minimum threshold register address */
#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C
/** Loss of Tracking high threshold register address */
#define AD2S1210_REG_LOT_HIGH_THRD    0x8D
/** Loss of Tracking low threshold register address */
#define AD2S1210_REG_LOT_LOW_THRD     0x8E
/** Excitation frequency register address */
#define AD2S1210_REG_EXCIT_FREQ       0x91
/** Control register address */
#define AD2S1210_REG_CONTROL          0x92
/** Resolution bit 0 control mask */
#define AD2S1210_CONTROL_RES0_MASK    BIT(0)
/** Resolution bit 1 control mask */
#define AD2S1210_CONTROL_RES1_MASK    BIT(1)
/** Combined resolution control mask */
#define AD2S1210_CONTROL_RES_MASK     (AD2S1210_CONTROL_RES0_MASK | AD2S1210_CONTROL_RES1_MASK)
/** Hysteresis enable control bit */
#define AD2S1210_ENABLE_HYSTERESIS    BIT(4)

/** Software reset register address */
#define AD2S1210_REG_SOFT_RESET 0xF0
/** Fault register address */
#define AD2S1210_REG_FAULT      0xFF

/** Minimum valid register address */
#define AD2S1210_REG_MIN AD2S1210_REG_POSITION

/** Minimum input clock frequency in Hz */
#define AD2S1210_MIN_CLKIN  6144000
/** Maximum input clock frequency in Hz */
#define AD2S1210_MAX_CLKIN  10240000
/** Minimum excitation frequency in Hz */
#define AD2S1210_MIN_EXCIT  2000
/** Maximum excitation frequency in Hz */
#define AD2S1210_MAX_EXCIT  20000
/** Excitation frequency step size in Hz */
#define AD2S1210_STEP_EXCIT 250
/** Minimum frequency control word value */
#define AD2S1210_MIN_FCW    0x4
/** Maximum frequency control word value */
#define AD2S1210_MAX_FCW    0x50

/** Maximum number of resolution bits */
#define AD2S1210_MAX_RESOLUTION_BITS 16

/** AD2S1210 modes */
enum ad2s1210_mode {
	/** Normal position mode */
	AD2S1210_MODE_POSITION = 0,
	/** Reserved mode (unused) */
	AD2S1210_MODE_RESERVED,
	/** Normal velocity mode */
	AD2S1210_MODE_VELOCITY,
	/** Configuration mode */
	AD2S1210_MODE_CONFIG,
};

/** Analog resolution */
enum ad2s1210_res {
	/** Data resolution 10 bits */
	AD2S1210_RES_10BIT,
	/** Data resolution 12 bits */
	AD2S1210_RES_12BIT,
	/** Data resolution 14 bits */
	AD2S1210_RES_14BIT,
	/** Data resolution 16 bits */
	AD2S1210_RES_16BIT,
	/** Maximum number of resolution values, used for lookup tables and loops */
	AD2S1210_RES_MAX_VAL,
};

/** AD2S1210 channels */
enum ad2s1210_channel {
	/** Position channel */
	AD2S1210_POS,
	/** Velocity channel */
	AD2S1210_VEL,
};

/** Enumeration for array of mode pins */
enum ad2s1210_mode_pin {
	/** Mode pin A0 */
	AD2S1210_MODE_PIN_A0 = 0,
	/** Mode pin A1 */
	AD2S1210_MODE_PIN_A1,
	/** Number of mode pin values in enum */
	AD2S1210_MODE_PIN_MAX_VAL,
};

/** Enumeration for array of resolution pins */
enum ad2s1210_res_pin {
	/** Resolution pin RES0 */
	AD2S1210_RES_PIN_RES0 = 0,
	/** Resolution pin RES1 */
	AD2S1210_RES_PIN_RES1,
	/** Number of resolution pin values in enum */
	AD2S1210_RES_PIN_MAX_VAL,
};

/** Enumeration for array of fault pins */
enum ad2s1210_fault_pin {
	/** Fault pin LOT */
	AD2S1210_FAULT_PIN_LOT = 0,
	/** Fault pin DOS */
	AD2S1210_FAULT_PIN_DOS,
	/** Number of fault pin values in enum */
	AD2S1210_FAULT_PIN_MAX_VAL,
};

/** Enumeration for supported clock in frequency */
enum ad2s1210_clockin_frequency {
	/** Clock In frequency 8.192MHz */
	AD2S1210_CLOCKIN_8MHZ192,
	/** Clock In frequency 10.24MHz  */
	AD2S1210_CLOCKIN_10MHZ24,
	/** Unknown clock in frequency */
	AD2S1210_CLOCKIN_UNKNOWN,
};

/** ad2s1210 data structure */
struct ad2s1210_data {
	/** Clock In frequency enumeration */
	enum ad2s1210_clockin_frequency clock;
	/** Configuration mode */
	enum ad2s1210_mode mode;
	/** Resolution */
	enum ad2s1210_res resolution;
	/** Position data */
	uint16_t position;
	/** Velocity data */
	int16_t velocity;
};

/** ad2s1210 configuration structure */
struct ad2s1210_config {
	/** Sample gpio pin */
	struct gpio_dt_spec sample_gpio;
	/** Mode selection gpio pins (A0 and A1) */
	struct gpio_dt_spec mode_gpios[AD2S1210_MODE_PIN_MAX_VAL];
	/** Reset gpio pin */
	struct gpio_dt_spec reset_gpio;
	/** Resolution selection gpio pins (RES0 and RES1) */
	struct gpio_dt_spec resolution_gpios[AD2S1210_RES_PIN_MAX_VAL];
	/** Fault indication gpio pins (LOT and DOS) */
	struct gpio_dt_spec fault_gpios[AD2S1210_FAULT_PIN_MAX_VAL];
	/** SPI configuration */
	struct spi_dt_spec spi;
	/** Clock frequency in Hz */
	uint32_t clock_frequency;
	/** Assigned resolution in bits */
	uint8_t assigned_resolution_bits;
	/** Flag indicating if resolution pins are defined in devicetree */
	bool have_resolution_pins;
};

/**
 * @brief Lookup table for maximum velocity range in RPM based on clock and resolution
 *
 * @see See Tracking Rate table in ad2s1210 datasheet
 */
static const int32_t table_velocity_range_rpm[AD2S1210_RES_MAX_VAL][AD2S1210_CLOCKIN_UNKNOWN] = {
	[AD2S1210_RES_16BIT] = {
			[AD2S1210_CLOCKIN_8MHZ192] = 125 * 60,
			[AD2S1210_CLOCKIN_10MHZ24] = 9375,
		},
	[AD2S1210_RES_14BIT] = {
			[AD2S1210_CLOCKIN_8MHZ192] = 500 * 60,
			[AD2S1210_CLOCKIN_10MHZ24] = 625 * 60,
		},
	[AD2S1210_RES_12BIT] = {
			[AD2S1210_CLOCKIN_8MHZ192] = 1000 * 60,
			[AD2S1210_CLOCKIN_10MHZ24] = 1250 * 60,
		},
	[AD2S1210_RES_10BIT] = {
		[AD2S1210_CLOCKIN_8MHZ192] = 2500 * 60,
		[AD2S1210_CLOCKIN_10MHZ24] = 3125 * 60,
	}};

/**
 * @brief Set the operation mode of the device.
 *
 * @param dev ad2s1210 device
 * @param mode Operation mode to set the device
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_set_mode_pins(const struct device *dev, enum ad2s1210_mode mode);

/**
 * @brief Get data from a specific channel of the device
 *
 * @param dev ad2s1210 device
 * @param chn Channel to read from (position or velocity)
 * @param data Pointer to store the read data
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_get_channel_data(const struct device *dev, enum ad2s1210_channel chn,
				     uint16_t *data);

/**
 * @brief Fetch samples from the sensor
 *
 * @param dev ad2s1210 device
 * @param chan Channel to sample
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_sample_fetch(const struct device *dev, enum sensor_channel chan);

/**
 * @brief Get channel values from the sensor
 *
 * @param dev ad2s1210 device
 * @param chan Channel to get value from
 * @param val Pointer to store the channel value
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_channel_get(const struct device *dev, enum sensor_channel chan,
				struct sensor_value *val);

/**
 * @brief Write a value to a device register
 *
 * @param dev ad2s1210 device
 * @param addr Register address
 * @param val Value to write
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_reg_write(const struct device *dev, uint8_t addr, uint8_t val);

/**
 * @brief Read a value from a device register
 *
 * @param dev ad2s1210 device
 * @param addr Register address
 * @param val Pointer to store the read value
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_reg_read(const struct device *dev, uint8_t addr, uint8_t *val);

/**
 * @brief Set the resolution of the device
 *
 * @param dev ad2s1210 device
 * @param resolution Resolution to set (10, 12, 14 or 16 bits)
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_set_resolution(const struct device *dev, enum ad2s1210_res resolution);

/**
 * @brief Enable or disable hysteresis
 *
 * @param dev ad2s1210 device
 * @param enable True to enable hysteresis, false to disable
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_set_hysteresis(const struct device *dev, bool enable);

/**
 * @brief Check if hysteresis is enabled
 *
 * @param dev ad2s1210 device
 * @return 1 if enabled, 0 if disabled, negative error code otherwise
 */
static __maybe_unused int ad2s1210_hysteresis_is_enabled(const struct device *dev);

/**
 * @brief Reinitialize the excitation frequency
 *
 * @param dev ad2s1210 device
 * @param fexcit Excitation frequency to set
 * @return 0 if successful, negative error code otherwise
 */
static __maybe_unused int ad2s1210_reinit_excitation_frequency(const struct device *dev,
							       uint16_t fexcit);

/**
 * @brief Initialize the ad2s1210 device
 *
 * @param dev ad2s1210 device
 * @return 0 if successful, negative error code otherwise
 */
static int ad2s1210_init(const struct device *dev);

static int ad2s1210_get_channel_data(const struct device *dev, enum ad2s1210_channel chn,
				     uint16_t *data)
{
	const struct ad2s1210_config *config = dev->config;
	int ret;
	enum ad2s1210_mode mode = AD2S1210_MODE_POSITION;
	uint8_t rx_buf[2] = {0};

	if (chn == AD2S1210_VEL) {
		mode = AD2S1210_MODE_VELOCITY;
	}

	ret = ad2s1210_set_mode_pins(dev, mode);
	if (ret < 0) {
		return ret;
	}

	/* Read data over SPI */
	const struct spi_buf rx_buf_arr = {
		.buf = rx_buf,
		.len = sizeof(rx_buf),
	};
	const struct spi_buf_set rx = {
		.buffers = &rx_buf_arr,
		.count = 1,
	};

	ret = spi_read_dt(&config->spi, &rx);
	if (ret < 0) {
		return ret;
	}

	/* Combine bytes into 16-bit value */
	*data = sys_get_be16(rx_buf);
	return 0;
}

static int ad2s1210_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
	const struct ad2s1210_config *config = dev->config;
	struct ad2s1210_data *data = dev->data;
	uint16_t raw_data;
	int ret;

	/* Pull sample pin to trigger measurement */
	ret = gpio_pin_set_dt(&config->sample_gpio, 1);
	if (ret < 0) {
		return ret;
	}

	ret = gpio_pin_set_dt(&config->sample_gpio, 0);
	if (ret < 0) {
		return ret;
	}

	switch (chan) {
	case SENSOR_CHAN_ROTATION:
		ret = ad2s1210_get_channel_data(dev, AD2S1210_POS, &raw_data);
		if (ret) {
			return ret;
		}
		data->position = raw_data;
		break;
	case SENSOR_CHAN_RPM:
		ret = ad2s1210_get_channel_data(dev, AD2S1210_VEL, &raw_data);
		if (ret) {
			return ret;
		}
		data->velocity = (int16_t)raw_data;
		break;
	case SENSOR_CHAN_ALL:
		ret = ad2s1210_get_channel_data(dev, AD2S1210_POS, &raw_data);
		if (ret) {
			return ret;
		}
		data->position = raw_data;

		ret = ad2s1210_get_channel_data(dev, AD2S1210_VEL, &raw_data);
		if (ret) {
			return ret;
		}
		data->velocity = (int16_t)raw_data;
		break;
	default:
		return -ENOTSUP;
	}

	return 0;
}

static int ad2s1210_channel_get(const struct device *dev, enum sensor_channel chan,
				struct sensor_value *val)
{
	const struct ad2s1210_data *data = dev->data;

	switch ((int16_t)chan) {
	case SENSOR_CHAN_ROTATION: {
		/* Scale factor of 1000000 for decimal places */
		/* position = (data->position * 360 * 1000000) / 65536 */
		uint64_t scaled_pos =
			((uint64_t)data->position * 360 * 1000000) >> AD2S1210_MAX_RESOLUTION_BITS;

		val->val1 = scaled_pos / 1000000;
		val->val2 = scaled_pos % 1000000;
	} break;

	case SENSOR_CHAN_RPM: {
		/* Get max range from lookup table */
		int32_t range = table_velocity_range_rpm[data->resolution][data->clock];
		/* Convert raw velocity to RPM */
		int rpm = ((int32_t)data->velocity * range) /
			  ((1 << (AD2S1210_MAX_RESOLUTION_BITS - 1)) - 1);

		val->val1 = rpm;
		val->val2 = 0;
	} break;

	default:
		return -ENOTSUP;
	}

	return 0;
}

/** ad2s1210 driver API interface */
static DEVICE_API(sensor, ad2s1210_api) = {
	.sample_fetch = &ad2s1210_sample_fetch,
	.channel_get = &ad2s1210_channel_get,
};

static int ad2s1210_set_mode_pins(const struct device *dev, enum ad2s1210_mode mode)
{
	const struct ad2s1210_config *config = dev->config;
	struct ad2s1210_data *data = dev->data;
	int ret;

	if (data->mode == mode) {
		return 0;
	}

	/* Set A0 pin (bit 0 of mode) */
	ret = gpio_pin_set_dt(&config->mode_gpios[AD2S1210_MODE_PIN_A0], mode & BIT(0));
	if (ret < 0) {
		LOG_ERR("Could not set A0 pin (%d)", ret);
		return ret;
	}

	/* Set A1 pin (bit 1 of mode) */
	ret = gpio_pin_set_dt(&config->mode_gpios[AD2S1210_MODE_PIN_A1], !!(mode & BIT(1)));
	if (ret < 0) {
		LOG_ERR("Could not set A1 pin (%d)", ret);
		return ret;
	}

	data->mode = mode;
	return 0;
}

static int ad2s1210_reg_read(const struct device *dev, uint8_t addr, uint8_t *val)
{
	const struct ad2s1210_config *config = dev->config;
	int ret;
	uint8_t tx_buf;
	uint8_t tx_buf2 = 0;
	uint8_t rx_buf = 0;

	/* Validate register address */
	if (addr < AD2S1210_REG_MIN) {
		return -EINVAL;
	}

	/* Set device to CONFIG mode */
	ret = ad2s1210_set_mode_pins(dev, AD2S1210_MODE_CONFIG);
	if (ret < 0) {
		return ret;
	}

	/* Write register address */
	tx_buf = addr;
	const struct spi_buf tx_buf_arr = {
		.buf = &tx_buf,
		.len = 1,
	};
	const struct spi_buf_set tx = {
		.buffers = &tx_buf_arr,
		.count = 1,
	};

	ret = spi_write_dt(&config->spi, &tx);
	if (ret < 0) {
		return ret;
	}

	/*
	 * Read register value while writing valid address
	 *
	 * While ad2s1210 will present data of the previous cycle on the SDO pins
	 * it will try to read from the address currently on the SDI pins.
	 * An invalid address might cause undefined behavior so better to have
	 * some valid address in buf while we read the result.
	 */
	tx_buf2 = addr;
	const struct spi_buf tx_buf_arr2 = {
		.buf = &tx_buf2,
		.len = 1,
	};
	const struct spi_buf rx_buf_arr = {
		.buf = &rx_buf,
		.len = 1,
	};
	const struct spi_buf_set tx2 = {
		.buffers = &tx_buf_arr2,
		.count = 1,
	};
	const struct spi_buf_set rx = {
		.buffers = &rx_buf_arr,
		.count = 1,
	};

	ret = spi_transceive_dt(&config->spi, &tx2, &rx);
	if (ret < 0) {
		return ret;
	}

	*val = rx_buf;
	return 0;
}

static int ad2s1210_reg_write(const struct device *dev, uint8_t addr, uint8_t val)
{
	const struct ad2s1210_config *config = dev->config;
	int ret;
	uint8_t tx_buf[2];

	/* Validate register address */
	if (addr < AD2S1210_REG_MIN) {
		return -EINVAL;
	}

	/* Set device to CONFIG mode */
	ret = ad2s1210_set_mode_pins(dev, AD2S1210_MODE_CONFIG);
	if (ret < 0) {
		return ret;
	}

	/* Prepare data to send */
	tx_buf[0] = addr;
	tx_buf[1] = val;

	/* Write register value */
	const struct spi_buf tx_buf_arr = {
		.buf = tx_buf,
		.len = sizeof(tx_buf),
	};
	const struct spi_buf_set tx = {
		.buffers = &tx_buf_arr,
		.count = 1,
	};

	ret = spi_write_dt(&config->spi, &tx);
	if (ret < 0) {
		return ret;
	}

	return 0;
}

static int ad2s1210_set_resolution(const struct device *dev, enum ad2s1210_res resolution)
{
	const struct ad2s1210_config *config = dev->config;
	struct ad2s1210_data *data = dev->data;
	int ret;
	uint8_t control;

	ret = ad2s1210_reg_read(dev, AD2S1210_REG_CONTROL, &control);
	if (ret < 0) {
		return ret;
	}

	control &= ~(AD2S1210_CONTROL_RES_MASK);

	switch (resolution) {
	case AD2S1210_RES_10BIT:
		break;
	case AD2S1210_RES_12BIT:
		control |= AD2S1210_CONTROL_RES1_MASK;
		break;
	case AD2S1210_RES_14BIT:
		control |= AD2S1210_CONTROL_RES0_MASK;
		break;
	case AD2S1210_RES_16BIT:
		control |= (AD2S1210_CONTROL_RES1_MASK | AD2S1210_CONTROL_RES0_MASK);
		break;
	default:
		LOG_ERR("Invalid resolution: %d", resolution);
		return -EINVAL;
	}

	ret = ad2s1210_reg_write(dev, AD2S1210_REG_CONTROL, control);
	if (ret < 0) {
		return ret;
	}

	if (!config->have_resolution_pins) {
		data->resolution = resolution;
		return 0;
	}

	/* Set RES0 pin */
	ret = gpio_pin_set_dt(&config->resolution_gpios[AD2S1210_RES_PIN_RES0],
			      !!(control & AD2S1210_CONTROL_RES0_MASK));
	if (ret < 0) {
		return ret;
	}

	/* Set RES1 pin */
	ret = gpio_pin_set_dt(&config->resolution_gpios[AD2S1210_RES_PIN_RES1],
			      !!(control & AD2S1210_CONTROL_RES1_MASK));
	if (ret < 0) {
		return ret;
	}

	data->resolution = resolution;
	return 0;
}

static int ad2s1210_set_hysteresis(const struct device *dev, bool enable)
{
	int ret;
	uint8_t control;

	ret = ad2s1210_reg_read(dev, AD2S1210_REG_CONTROL, &control);
	if (ret < 0) {
		return ret;
	}

	control &= ~AD2S1210_ENABLE_HYSTERESIS;
	if (enable) {
		control |= AD2S1210_ENABLE_HYSTERESIS;
	}

	return ad2s1210_reg_write(dev, AD2S1210_REG_CONTROL, control);
}

static __maybe_unused int ad2s1210_hysteresis_is_enabled(const struct device *dev)
{
	int ret;
	uint8_t control;

	ret = ad2s1210_reg_read(dev, AD2S1210_REG_CONTROL, &control);
	if (ret < 0) {
		return ret;
	}

	if (control & AD2S1210_ENABLE_HYSTERESIS) {
		return 1;
	}

	return 0;
}

static __maybe_unused int ad2s1210_reinit_excitation_frequency(const struct device *dev,
							       uint16_t frequency)
{
	const struct ad2s1210_config *config = dev->config;
	int ret;
	uint32_t fcw;

	/* Calculate the frequency control word */
	fcw = (frequency * BIT(15)) / config->clock_frequency;
	if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) {
		return -EINVAL;
	}

	/* Write the frequency control word */
	ret = ad2s1210_reg_write(dev, AD2S1210_REG_EXCIT_FREQ, (int8_t)fcw);
	if (ret < 0) {
		return ret;
	}

	/* Software reset to reinitialize excitation frequency output */
	return ad2s1210_reg_write(dev, AD2S1210_REG_SOFT_RESET, 0);
}

static int ad2s1210_init(const struct device *dev) /* cppcheck-suppress unusedFunction */
{
	const struct ad2s1210_config *config = dev->config;
	struct ad2s1210_data *data = dev->data;
	int ret;
	enum ad2s1210_res resolution;

	/* Check if SPI bus is ready */
	if (!spi_is_ready_dt(&config->spi)) {
		LOG_ERR("SPI bus not ready");
		return -ENODEV;
	}

	/* Check if sample GPIO port is ready */
	if (!gpio_is_ready_dt(&config->sample_gpio)) {
		LOG_ERR("Sample GPIO port not ready");
		return -ENODEV;
	}

	/* Check if mode GPIO ports are ready (required) */
	for (uint8_t idx = 0; idx < (uint8_t)AD2S1210_MODE_PIN_MAX_VAL; idx++) {
		if (!gpio_is_ready_dt(&config->mode_gpios[idx])) {
			LOG_ERR("Mode GPIO %d port not ready", idx);
			return -ENODEV;
		}
	}

	/* Check if resolution GPIO ports are ready */

	if (config->have_resolution_pins) {
		for (uint8_t idx = 0; idx < (uint8_t)AD2S1210_RES_PIN_MAX_VAL; idx++) {
			if (!gpio_is_ready_dt(&config->resolution_gpios[idx])) {
				LOG_ERR("Resolution GPIO %d port not ready", idx);
				return -ENODEV;
			}
		}
	}

	/* Check if reset GPIO port is ready */
	if (config->reset_gpio.port && !gpio_is_ready_dt(&config->reset_gpio)) {
		LOG_ERR("Reset GPIO port not ready");
		return -ENODEV;
	}

	/* Check if fault GPIO ports are ready */
	for (uint8_t idx = 0; idx < (uint8_t)AD2S1210_FAULT_PIN_MAX_VAL; idx++) {
		if (config->fault_gpios[idx].port && !gpio_is_ready_dt(&config->fault_gpios[idx])) {
			LOG_ERR("Fault GPIO %d port not ready", idx);
			return -ENODEV;
		}
	}

	/* Configure sample pin as output */
	ret = gpio_pin_configure_dt(&config->sample_gpio, GPIO_OUTPUT_INACTIVE);
	if (ret < 0) {
		LOG_ERR("Could not configure sample GPIO (%d)", ret);
		return ret;
	}

	/* Configure mode selection pins (required), default to CONFIG */
	for (uint8_t i = 0; i < (uint8_t)AD2S1210_MODE_PIN_MAX_VAL; i++) {
		ret = gpio_pin_configure_dt(&config->mode_gpios[i], GPIO_OUTPUT_ACTIVE);
		if (ret < 0) {
			LOG_ERR("Could not configure mode GPIO %d (%d)", i, ret);
			return ret;
		}
	}

	/* Configure resolution selection pins */
	if (config->have_resolution_pins) {
		for (uint8_t i = 0; i < (uint8_t)AD2S1210_RES_PIN_MAX_VAL; i++) {
			ret = gpio_pin_configure_dt(&config->resolution_gpios[i],
						    GPIO_OUTPUT_INACTIVE);
			if (ret < 0) {
				LOG_ERR("Could not configure resolution GPIO %d (%d)", i, ret);
				return ret;
			}
		}
	}

	/* Configure reset pin */
	if (config->reset_gpio.port) {
		ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
		if (ret < 0) {
			LOG_ERR("Could not configure reset GPIO (%d)", ret);
			return ret;
		}
	}

	/* Configure fault indication pins as inputs */
	for (uint8_t i = 0; i < (uint8_t)AD2S1210_FAULT_PIN_MAX_VAL; i++) {
		if (config->fault_gpios[i].port) {
			ret = gpio_pin_configure_dt(&config->fault_gpios[i], GPIO_INPUT);
			if (ret < 0) {
				LOG_ERR("Could not configure fault GPIO %d (%d)", i, ret);
				return ret;
			}
		}
	}

	data->mode = AD2S1210_MODE_CONFIG;

	/* Set initial mode to CONFIG */
	ret = ad2s1210_set_mode_pins(dev, AD2S1210_MODE_CONFIG);
	if (ret < 0) {
		return ret;
	}

	/*
	 * Check clock frequency is 8 or 10 MHz as these are the only values we
	 * have in the velocity lookup table.
	 */
	switch (config->clock_frequency) {
	case 8192000:
		data->clock = AD2S1210_CLOCKIN_8MHZ192;
		break;
	case 10240000:
		data->clock = AD2S1210_CLOCKIN_10MHZ24;
		break;
	default:
		data->clock = AD2S1210_CLOCKIN_UNKNOWN;
		return -EIO;
	}

	/* Set resolution based on configuration */
	if (!config->have_resolution_pins) {
		/* Convert assigned resolution bits to enum */
		switch (config->assigned_resolution_bits) {
		case 10:
			resolution = AD2S1210_RES_10BIT;
			break;
		case 12:
			resolution = AD2S1210_RES_12BIT;
			break;
		case 14:
			resolution = AD2S1210_RES_14BIT;
			break;
		case 16:
			resolution = AD2S1210_RES_16BIT;
			break;
		default:
			LOG_ERR("Invalid assigned resolution bits: %d",
				config->assigned_resolution_bits);
			return -EINVAL;
		}
	} else {
		/* Default to 16 bits when using GPIO control */
		resolution = AD2S1210_RES_16BIT;
	}

	/* Set resolution */
	ret = ad2s1210_set_resolution(dev, resolution);
	if (ret < 0) {
		LOG_ERR("Could not set resolution (%d)", ret);
		return ret;
	}

	/* Enable hysteresis by default */
	ret = ad2s1210_set_hysteresis(dev, true);
	if (ret < 0) {
		LOG_ERR("Could not set hysteresis");
		return ret;
	}
	return 0;
}

/** Macro used to initialize one ad2s1210 driver instance */
#define AD2S1210_INIT(i)                                                                           \
	static struct ad2s1210_data ad2s1210_data_##i;                                             \
                                                                                                   \
	static const struct ad2s1210_config ad2s1210_config_##i = {                                \
		.spi = SPI_DT_SPEC_INST_GET(i, SPI_WORD_SET(8), 0),                                \
		.sample_gpio = GPIO_DT_SPEC_INST_GET(i, sample_gpios),                             \
		.mode_gpios =                                                                      \
			{                                                                          \
				GPIO_DT_SPEC_INST_GET_BY_IDX(i, mode_gpios, 0),                    \
				GPIO_DT_SPEC_INST_GET_BY_IDX(i, mode_gpios, 1),                    \
			},                                                                         \
		.reset_gpio = GPIO_DT_SPEC_INST_GET(i, reset_gpios),                               \
		.resolution_gpios =                                                                \
			{                                                                          \
				GPIO_DT_SPEC_INST_GET_BY_IDX_OR(i, resolution_gpios, 0, {0}),      \
				GPIO_DT_SPEC_INST_GET_BY_IDX_OR(i, resolution_gpios, 1, {0}),      \
			},                                                                         \
		.fault_gpios =                                                                     \
			{                                                                          \
				GPIO_DT_SPEC_INST_GET_BY_IDX_OR(i, fault_gpios, 0, {0}),           \
				GPIO_DT_SPEC_INST_GET_BY_IDX_OR(i, fault_gpios, 1, {0}),           \
			},                                                                         \
		.clock_frequency = DT_INST_PROP(i, clock_frequency),                               \
		.assigned_resolution_bits = DT_INST_PROP(i, assigned_resolution_bits),             \
		.have_resolution_pins = (DT_INST_NODE_HAS_PROP(i, resolution_gpios)),              \
	};                                                                                         \
                                                                                                   \
	SENSOR_DEVICE_DT_INST_DEFINE(i, ad2s1210_init, NULL, &ad2s1210_data_##i,                   \
				     &ad2s1210_config_##i, POST_KERNEL,                            \
				     CONFIG_SENSOR_INIT_PRIORITY, &ad2s1210_api);

DT_INST_FOREACH_STATUS_OKAY(AD2S1210_INIT)
