From 2e4e9926441a0431c94d6c0aff915d4a314f238c Mon Sep 17 00:00:00 2001 From: Mustafa Abdullah Kus Date: Tue, 15 Aug 2023 17:37:22 +0300 Subject: [PATCH] drivers: adc: add max1125x driver This adds support for the max1125x (max11254, max11254) family of spi adc devices. Signed-off-by: Mustafa Abdullah Kus --- drivers/adc/CMakeLists.txt | 1 + drivers/adc/Kconfig | 2 + drivers/adc/Kconfig.max1125x | 40 + drivers/adc/adc_max1125x.c | 864 ++++++++++++++++++++++ dts/bindings/adc/maxim,max11253.yaml | 5 + dts/bindings/adc/maxim,max11254.yaml | 5 + dts/bindings/adc/maxim,max1125x-base.yaml | 36 + tests/drivers/build_all/adc/app.overlay | 32 + tests/drivers/build_all/adc/testcase.yaml | 1 + 9 files changed, 986 insertions(+) create mode 100644 drivers/adc/Kconfig.max1125x create mode 100644 drivers/adc/adc_max1125x.c create mode 100644 dts/bindings/adc/maxim,max11253.yaml create mode 100644 dts/bindings/adc/maxim,max11254.yaml create mode 100644 dts/bindings/adc/maxim,max1125x-base.yaml diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index f10326d4d19..ee426090d3f 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -44,3 +44,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_SMARTBOND_GPADC adc_smartbond_gpadc.c) zephyr_library_sources_ifdef(CONFIG_ADC_SMARTBOND_SDADC adc_smartbond_sdadc.c) zephyr_library_sources_ifdef(CONFIG_ADC_TLA2021 adc_tla2021.c) zephyr_library_sources_ifdef(CONFIG_ADC_NXP_S32_ADC_SAR adc_nxp_s32_adc_sar.c) +zephyr_library_sources_ifdef(CONFIG_ADC_MAX1125X adc_max1125x.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index a915fa18fc0..eb9e03041a9 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -112,4 +112,6 @@ source "drivers/adc/Kconfig.tla2021" source "drivers/adc/Kconfig.nxp_s32" +source "drivers/adc/Kconfig.max1125x" + endif # ADC diff --git a/drivers/adc/Kconfig.max1125x b/drivers/adc/Kconfig.max1125x new file mode 100644 index 00000000000..c0b29486204 --- /dev/null +++ b/drivers/adc/Kconfig.max1125x @@ -0,0 +1,40 @@ +# MAX1125X ADC configuration options + +# Copyright (c) 2023 Mustafa Abdullah Kus, Sparse Technology +# SPDX-License-Identifier: Apache-2.0 + +config ADC_MAX1125X + bool "MAX1125X driver" + default y + depends on DT_HAS_MAXIM_MAX11254_ENABLED || DT_HAS_MAXIM_MAX11253_ENABLED + select SPI + select ADC_CONFIGURABLE_INPUTS + help + Enable the driver implementation for the MAX1125X + +if ADC_MAX1125X + +config ADC_MAX1125X_INIT_PRIORITY + int "Init priority" + default 80 + help + ADS1X1X ADC device driver initialization priority. + +config ADC_MAX1125X_ASYNC_THREAD_INIT_PRIORITY + int "ADC MAX1125X async thread priority" + default 0 + +config ADC_MAX1125X_ACQUISITION_THREAD_PRIORITY + int "Priority for the ADC data acquisition thread" + default 0 + help + Priority level for the internal ADC data acquisition thread. + +config ADC_MAX1125X_ACQUISITION_THREAD_STACK_SIZE + int "Stack size for the ADC data acquisition thread" + default 400 + help + Size of the stack used for the internal data acquisition + thread. + +endif # ADC_MAX1125X diff --git a/drivers/adc/adc_max1125x.c b/drivers/adc/adc_max1125x.c new file mode 100644 index 00000000000..82a85d2af1c --- /dev/null +++ b/drivers/adc/adc_max1125x.c @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2023 Mustafa Abdullah Kus, Sparse Technology + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +LOG_MODULE_REGISTER(ADC_MAX1125X, CONFIG_ADC_LOG_LEVEL); + +#define MAX1125X_CONFIG_PGA(x) BIT(x) +#define MAX1125X_CONFIG_CHANNEL(x) ((x) << 5) +#define MAX1125X_CONFIG_CHMAP(x) ((x << 2) | BIT(1)) +#define MAX1125X_REG_DATA(x) (MAX1125X_REG_DATA0 + (x << 1)) + +#define MAX1125X_CMD_READ 0xC1 +#define MAX1125X_CMD_WRITE 0xC0 +#define MAX1125X_CMD_CONV 0x80 +#define MAX1125X_CMD_CALIBRATION 0x20 +#define MAX1125X_CMD_SEQUENCER 0x30 + +enum max1125x_mode { + MAX1125X_MODE_POWERDOWN = 0x01, + MAX1125X_MODE_CALIBRATION = 0x02, + MAX1125X_MODE_SEQUENCER = 0x03, +}; + +enum { + MAX1125X_CONFIG_RATE_1_9 = 0x00, + MAX1125X_CONFIG_RATE_3_9 = 0x01, + MAX1125X_CONFIG_RATE_7_8 = 0x02, + MAX1125X_CONFIG_RATE_15_6 = 0x03, + MAX1125X_CONFIG_RATE_31_2 = 0x04, + MAX1125X_CONFIG_RATE_62_5 = 0x05, + MAX1125X_CONFIG_RATE_125 = 0x06, + MAX1125X_CONFIG_RATE_250 = 0x07, + MAX1125X_CONFIG_RATE_500 = 0x08, + MAX1125X_CONFIG_RATE_1000 = 0x09, + MAX1125X_CONFIG_RATE_2000 = 0x0A, + MAX1125X_CONFIG_RATE_4000 = 0x0B, + MAX1125X_CONFIG_RATE_8000 = 0x0C, + MAX1125X_CONFIG_RATE_16000 = 0x0D, + MAX1125X_CONFIG_RATE_32000 = 0x0E, + MAX1125X_CONFIG_RATE_64000 = 0x0F, +}; + +enum max1125x_reg { + MAX1125X_REG_STAT = 0x00, + MAX1125X_REG_CTRL1 = 0x02, + MAX1125X_REG_CTRL2 = 0x04, + MAX1125X_REG_CTRL3 = 0x06, + MAX1125X_REG_GPIO_CTRL = 0x08, + MAX1125X_REG_DELAY = 0x0A, + MAX1125X_REG_CHMAP1 = 0x0C, + MAX1125X_REG_CHMAP0 = 0x0E, + MAX1125X_REG_SEQ = 0x10, + MAX1125X_REG_GPO_DIR = 0x12, + MAX1125X_REG_SOC = 0x14, + MAX1125X_REG_SGC = 0x16, + MAX1125X_REG_SCOC = 0x18, + MAX1125X_REG_SCGC = 0x1A, + MAX1125X_REG_DATA0 = 0x1C, + MAX1125X_REG_DATA1 = 0x1E, + MAX1125X_REG_DATA2 = 0x20, + MAX1125X_REG_DATA3 = 0x22, + MAX1125X_REG_DATA4 = 0x24, + MAX1125X_REG_DATA5 = 0x26, +}; + +enum { + MAX1125X_REG_STAT_LEN = 3, + MAX1125X_REG_CTRL1_LEN = 1, + MAX1125X_REG_CTRL2_LEN = 1, + MAX1125X_REG_CTRL3_LEN = 1, + MAX1125X_REG_GPIO_CTRL_LEN = 1, + MAX1125X_REG_DELAY_LEN = 2, + MAX1125X_REG_CHMAP1_LEN = 3, + MAX1125X_REG_CHMAP0_LEN = 3, + MAX1125X_REG_SEQ_LEN = 1, + MAX1125X_REG_GPO_DIR_LEN = 1, + MAX1125X_REG_SOC_LEN = 3, + MAX1125X_REG_SGC_LEN = 3, + MAX1125X_REG_SCOC_LEN = 3, + MAX1125X_REG_SCGC_LEN = 3, +}; + +enum { + MAX1125X_CTRL1_CAL_SELF = 0, + MAX1125X_CTRL1_CAL_OFFSET = 1, + MAX1125X_CTRL1_CAL_FULLSCALE = 2, +}; + +enum { + MAX1125X_CTRL1_PD_NOP = 0, + MAX1125X_CTRL1_DP_SLEEP = 1, + MAX1125X_CTRL1_DP_STANDBY = 2, + MAX1125X_CTRL1_DP_RESET = 3, +}; + +enum { + MAX1125X_CTRL1_CONTSC = 0, + MAX1125X_CTRL1_SCYCLE = 1, + MAX1125X_CTRL1_FORMAT = 2, + MAX1125X_CTRL1_UBPOLAR = 3, +}; + +enum { + MAX1125X_CTRL2_PGA_GAIN_1 = 0, + MAX1125X_CTRL2_PGA_GAIN_2 = 1, + MAX1125X_CTRL2_PGA_GAIN_4 = 2, + MAX1125X_CTRL2_PGA_GAIN_8 = 3, + MAX1125X_CTRL2_PGA_GAIN_16 = 4, + MAX1125X_CTRL2_PGA_GAIN_32 = 5, + MAX1125X_CTRL2_PGA_GAIN_64 = 6, + MAX1125X_CTRL2_PGA_GAIN_128 = 7, +}; + +enum { + MAX1125X_CTRL2_PGAEN = 3, + MAX1125X_CTRL2_LPMODE = 4, + MAX1125X_CTRL2_LDOEN = 5, + MAX1125X_CTRL2_CSSEN = 6, + MAX1125X_CTRL2_EXTCLK = 7, +}; + +enum { + MAX1125X_CTRL3_NOSCO = 0, + MAX1125X_CTRL3_NOSCG = 1, + MAX1125X_CTRL3_NOSYSO = 2, + MAX1125X_CTRL3_NOSYSG = 3, + MAX1125X_CTRL3_CALREGSEL = 4, + MAX1125X_CTRL3_SYNC_MODE = 5, + MAX1125X_CTRL3_GPO_MODE = 6, +}; + +enum { + MAX1125X_GPIO_CTRL_DIO0 = 0, + MAX1125X_GPIO_CTRL_DIO1 = 1, + MAX1125X_GPIO_CTRL_DIRO = 3, + MAX1125X_GPIO_CTRL_DIR1 = 4, + MAX1125X_GPIO_CTRL_GPIO0_EN = 6, + MAX1125X_GPIO_CTRL_GPIO1_EN = 7, +}; + +enum { + MAX1125X_SEQ_RDYBEN = 0, + MAX1125X_SEQ_MDREN = 1, + MAX1125X_SEQ_GPODREN = 2, + MAX1125X_SEQ_MODE0 = 3, + MAX1125X_SEQ_MODE1 = 4, + MAX1125X_SEQ_MUX0 = 5, + MAX1125X_SEQ_MUX1 = 6, + MAX1125X_SEQ_MUX2 = 7, +}; + +enum { + MAX1125X_GPO_DIR_GPO0 = 0, + MAX1125X_GPO_DIR_GPO1 = 1, +}; + +enum { + MAX1125X_CMD_RATE0 = 0, + MAX1125X_CMD_RATE1 = 1, + MAX1125X_CMD_RATE2 = 2, + MAX1125X_CMD_RATE3 = 3, +}; + +enum { + MAX1125X_CHANNEL_0 = 0x0, + MAX1125X_CHANNEL_1 = 0x1, + MAX1125X_CHANNEL_2 = 0x2, + MAX1125X_CHANNEL_3 = 0x3, + MAX1125X_CHANNEL_4 = 0x4, + MAX1125X_CHANNEL_5 = 0x5, +}; + +enum { + MAX1125X_CMD_MODE0 = 4, + MAX1125X_CMD_MODE1 = 5, +}; + +struct max1125x_gpio_ctrl { + bool gpio0_enable; + bool gpio1_enable; + bool gpio0_direction; + bool gpio1_direction; +}; + +struct max1125x_gpo_ctrl { + bool gpo0_enable; + bool gpo1_enable; +}; + +struct max1125x_config { + struct spi_dt_spec bus; + struct gpio_dt_spec drdy_gpio; + const uint32_t odr_delay[16]; + uint8_t resolution; + bool multiplexer; + bool pga; + bool self_calibration; + struct max1125x_gpio_ctrl gpio; + struct max1125x_gpo_ctrl gpo; +}; + +struct max1125x_data { + const struct device *dev; + struct adc_context ctx; + uint8_t rate; + struct gpio_callback callback_data_ready; + struct k_sem acq_sem; + struct k_sem data_ready_signal; + int32_t *buffer; + int32_t *repeat_buffer; + struct k_thread thread; + bool differential; + + K_THREAD_STACK_MEMBER(stack, CONFIG_ADC_MAX1125X_ACQUISITION_THREAD_STACK_SIZE); +}; + +static void max1125x_data_ready_handler(const struct device *dev, struct gpio_callback *gpio_cb, + uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pins); + + struct max1125x_data *data = + CONTAINER_OF(gpio_cb, struct max1125x_data, callback_data_ready); + + k_sem_give(&data->data_ready_signal); +} + +static int max1125x_read_reg(const struct device *dev, enum max1125x_reg reg_addr, uint8_t *buffer, + size_t reg_size) +{ + int ret; + const struct max1125x_config *config = dev->config; + uint8_t buffer_tx[3]; + uint8_t buffer_rx[ARRAY_SIZE(buffer_tx)]; + const struct spi_buf tx_buf[] = {{ + .buf = buffer_tx, + .len = ARRAY_SIZE(buffer_tx), + }}; + const struct spi_buf rx_buf[] = {{ + .buf = buffer_rx, + .len = ARRAY_SIZE(buffer_rx), + }}; + const struct spi_buf_set tx = { + .buffers = tx_buf, + .count = ARRAY_SIZE(tx_buf), + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = ARRAY_SIZE(rx_buf), + }; + buffer_tx[0] = MAX1125X_CMD_READ | reg_addr; + /* read one register */ + buffer_tx[1] = 0x00; + + ret = spi_transceive_dt(&config->bus, &tx, &rx); + if (ret != 0) { + LOG_ERR("MAX1125X: error writing register 0x%X (%d)", reg_addr, ret); + return ret; + } + *buffer = buffer_rx[1]; + LOG_DBG("read from register 0x%02X value 0x%02X", reg_addr, *buffer); + + return 0; +} + +static int max1125x_write_reg(const struct device *dev, enum max1125x_reg reg_addr, + uint8_t *reg_val, size_t reg_size) +{ + int ret; + const struct max1125x_config *config = dev->config; + uint8_t command = MAX1125X_CMD_WRITE | reg_addr; + + const struct spi_buf spi_buf[2] = {{.buf = &command, .len = sizeof(command)}, + {.buf = reg_val, .len = reg_size}}; + const struct spi_buf_set tx = {.buffers = spi_buf, .count = ARRAY_SIZE(spi_buf)}; + + ret = spi_write_dt(&config->bus, &tx); + if (ret != 0) { + LOG_ERR("MAX1125X: error writing register 0x%X (%d)", reg_addr, ret); + return ret; + } + + return 0; +} + +static int max1125x_send_command(const struct device *dev, enum max1125x_mode mode, uint8_t rate) +{ + int ret; + const struct max1125x_config *config = dev->config; + uint8_t command = MAX1125X_CMD_CONV | mode | rate; + const struct spi_buf spi_buf = {.buf = &command, .len = sizeof(command)}; + const struct spi_buf_set tx = {.buffers = &spi_buf, .count = 1}; + + ret = spi_write_dt(&config->bus, &tx); + if (ret != 0) { + LOG_ERR("MAX1125X: error writing register 0x%X (%d)", rate, ret); + return ret; + } + + return 0; +} + +static int max1125x_start_conversion(const struct device *dev) +{ + const struct max1125x_data *data = dev->data; + + return max1125x_send_command(dev, MAX1125X_CMD_SEQUENCER, data->rate); +} + +static inline int max1125x_acq_time_to_dr(const struct device *dev, uint16_t acq_time) +{ + struct max1125x_data *data = dev->data; + const struct max1125x_config *config = dev->config; + const uint32_t *odr_delay = config->odr_delay; + uint32_t odr_delay_us = 0; + uint16_t acq_value = ADC_ACQ_TIME_VALUE(acq_time); + int odr = -EINVAL; + + if (acq_time != ADC_ACQ_TIME_DEFAULT && ADC_ACQ_TIME_UNIT(acq_time) != ADC_ACQ_TIME_TICKS) { + LOG_ERR("MAX1125X: invalid acq time value (%d)", acq_time); + return -EINVAL; + } + + if (acq_value < MAX1125X_CONFIG_RATE_1_9 || acq_value > MAX1125X_CONFIG_RATE_64000) { + LOG_ERR("MAX1125X: invalid acq value (%d)", acq_value); + return -EINVAL; + } + + odr = acq_value; + odr_delay_us = odr_delay[acq_value]; + + data->rate = odr; + + return odr; +} + +static int max1125x_wait_data_ready(const struct device *dev) +{ + struct max1125x_data *data = dev->data; + + return k_sem_take(&data->data_ready_signal, ADC_CONTEXT_WAIT_FOR_COMPLETION_TIMEOUT); +} + +static int max1125x_read_sample(const struct device *dev) +{ + const struct max1125x_config *config = dev->config; + struct max1125x_data *data = dev->data; + bool is_positive; + uint8_t buffer_tx[(config->resolution / 8) + 1]; + uint8_t buffer_rx[ARRAY_SIZE(buffer_tx)]; + uint8_t current_channel = find_msb_set(data->ctx.sequence.channels) - 1; + int rc; + + const struct spi_buf tx_buf[] = {{ + .buf = buffer_tx, + .len = ARRAY_SIZE(buffer_tx), + }}; + const struct spi_buf rx_buf[] = {{ + .buf = buffer_rx, + .len = ARRAY_SIZE(buffer_rx), + }}; + const struct spi_buf_set tx = { + .buffers = tx_buf, + .count = ARRAY_SIZE(tx_buf), + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = ARRAY_SIZE(rx_buf), + }; + + buffer_tx[0] = MAX1125X_CMD_READ | MAX1125X_REG_DATA(current_channel); + + rc = spi_transceive_dt(&config->bus, &tx, &rx); + if (rc != 0) { + LOG_ERR("spi_transceive failed with error %i", rc); + return rc; + } + + /* The data format while in unipolar mode is always offset binary. + * In offset binary format the most negative value is 0x000000, + * the midscale value is 0x800000 and the most positive value is + * 0xFFFFFF. In bipolar mode if the FORMAT bit = ‘1’ then the + * data format is offset binary. If the FORMAT bit = ‘0’, then + * the data format is two’s complement. In two’s complement the + * negative full-scale value is 0x800000, the midscale is 0x000000 + * and the positive full scale is 0x7FFFFF. Any input exceeding + * the available input range is limited to the minimum or maximum + * data value. + */ + is_positive = buffer_rx[(config->resolution / 8)] >> 7; + if (is_positive) { + *data->buffer++ = sys_get_be24(buffer_rx) - (1 << (config->resolution - 1)); + } else { + *data->buffer++ = sys_get_be24(buffer_rx + 1); + } + + adc_context_on_sampling_done(&data->ctx, dev); + + return rc; +} + +static int max1125x_configure_chmap(const struct device *dev, const uint8_t channel_id) +{ + uint8_t last_order = 0; + uint8_t chmap1_register[3] = {0}; + uint8_t chmap0_register[3] = {0}; + + if (channel_id > 6) { + LOG_ERR("MAX1125X: invalid channel (%u)", channel_id); + return -EINVAL; + } + + max1125x_read_reg(dev, MAX1125X_REG_CHMAP1, chmap1_register, MAX1125X_REG_CHMAP1_LEN); + for (int index = 0; index < 3; index++) { + if ((chmap1_register[index] >> 2) >= last_order) { + last_order = chmap1_register[index] >> 2; + } else { + continue; + } + } + + max1125x_read_reg(dev, MAX1125X_REG_CHMAP0, chmap0_register, MAX1125X_REG_CHMAP0_LEN); + for (int index = 0; index < 3; index++) { + if ((chmap0_register[index] >> 2) >= last_order) { + last_order = chmap0_register[index] >> 2; + } else { + continue; + } + } + + last_order++; + + switch (channel_id) { + case MAX1125X_CHANNEL_0: + chmap0_register[2] = MAX1125X_CONFIG_CHMAP(last_order); + break; + case MAX1125X_CHANNEL_1: + chmap0_register[1] = MAX1125X_CONFIG_CHMAP(last_order); + break; + case MAX1125X_CHANNEL_2: + chmap0_register[0] = MAX1125X_CONFIG_CHMAP(last_order); + break; + case MAX1125X_CHANNEL_3: + chmap1_register[2] = MAX1125X_CONFIG_CHMAP(last_order); + break; + case MAX1125X_CHANNEL_4: + chmap1_register[1] = MAX1125X_CONFIG_CHMAP(last_order); + break; + case MAX1125X_CHANNEL_5: + chmap1_register[0] = MAX1125X_CONFIG_CHMAP(last_order); + break; + default: + break; + } + + if (channel_id > 3) { + /* CHMAP 1 register configuration */ + max1125x_write_reg(dev, MAX1125X_REG_CHMAP1, chmap1_register, + MAX1125X_REG_CHMAP1_LEN); + } else { + /* CHMAP 0 register configuration */ + max1125x_write_reg(dev, MAX1125X_REG_CHMAP0, chmap0_register, + MAX1125X_REG_CHMAP0_LEN); + } + + return 0; +} + +static int max1125x_self_calibration(const struct device *dev) +{ + uint8_t seq_register = 0; + uint8_t ctrl1_register = BIT(MAX1125X_CTRL1_SCYCLE); + + max1125x_write_reg(dev, MAX1125X_REG_SEQ, &seq_register, MAX1125X_REG_SEQ_LEN); + max1125x_write_reg(dev, MAX1125X_REG_CTRL1, &ctrl1_register, MAX1125X_REG_CTRL1_LEN); + max1125x_send_command(dev, MAX1125X_CMD_CALIBRATION, 0x00); + + k_sleep(K_MSEC(200)); + return 0; +} + +static int max1125x_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + const struct max1125x_config *max_config = dev->config; + uint8_t seq_register = 0; + uint8_t ctrl2_register = 0; + uint8_t gpio_reg = 0; + uint8_t gpo_reg = 0; + + /* sequencer register configuration */ + max1125x_read_reg(dev, MAX1125X_REG_SEQ, &seq_register, MAX1125X_REG_SEQ_LEN); + seq_register |= BIT(MAX1125X_SEQ_MDREN); + seq_register |= BIT(MAX1125X_SEQ_MODE0); + max1125x_write_reg(dev, MAX1125X_REG_SEQ, &seq_register, MAX1125X_REG_SEQ_LEN); + + /* configuration multiplexer */ + if (max_config->multiplexer) { + if (!channel_cfg->differential) { + LOG_ERR("6 channel fully supported only supported differential " + "differemtial option %i", + channel_cfg->differential); + return -ENOTSUP; + } + } + + max1125x_acq_time_to_dr(dev, channel_cfg->acquisition_time); + + /* ctrl2 register configuration */ + if (max_config->pga) { + /* programmable gain amplifier support */ + ctrl2_register |= MAX1125X_CONFIG_PGA(MAX1125X_CTRL2_PGAEN); + switch (channel_cfg->gain) { + case ADC_GAIN_1: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_1; + break; + case ADC_GAIN_2: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_2; + break; + case ADC_GAIN_4: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_4; + break; + case ADC_GAIN_8: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_8; + break; + case ADC_GAIN_16: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_16; + break; + case ADC_GAIN_32: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_32; + break; + case ADC_GAIN_64: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_64; + break; + case ADC_GAIN_128: + ctrl2_register |= MAX1125X_CTRL2_PGA_GAIN_128; + break; + default: + LOG_ERR("MAX1125X: unsupported channel gain '%d'", channel_cfg->gain); + return -ENOTSUP; + } + } + + if (channel_cfg->reference == ADC_REF_INTERNAL) { + ctrl2_register |= BIT(MAX1125X_CTRL2_LDOEN); + + } else if (channel_cfg->reference == ADC_REF_EXTERNAL1) { + ctrl2_register &= ~BIT(MAX1125X_CTRL2_LDOEN); + } else { + LOG_ERR("MAX1125X: unsupported channel reference type '%d'", + channel_cfg->reference); + return -ENOTSUP; + } + max1125x_write_reg(dev, MAX1125X_REG_CTRL2, &ctrl2_register, MAX1125X_REG_CTRL2_LEN); + + /* GPIO_CTRL register configuration */ + gpio_reg |= max_config->gpio.gpio0_enable << MAX1125X_GPIO_CTRL_GPIO0_EN; + gpio_reg |= max_config->gpio.gpio1_enable << MAX1125X_GPIO_CTRL_GPIO1_EN; + gpio_reg |= max_config->gpio.gpio0_direction << MAX1125X_GPIO_CTRL_DIRO; + gpio_reg |= max_config->gpio.gpio1_direction << MAX1125X_GPIO_CTRL_DIR1; + max1125x_write_reg(dev, MAX1125X_REG_GPIO_CTRL, &gpio_reg, MAX1125X_REG_GPIO_CTRL_LEN); + + /* GPO_DIR register configuration */ + gpo_reg |= max_config->gpo.gpo0_enable << MAX1125X_GPO_DIR_GPO0; + gpo_reg |= max_config->gpo.gpo1_enable << MAX1125X_GPO_DIR_GPO1; + max1125x_write_reg(dev, MAX1125X_REG_GPO_DIR, &gpo_reg, MAX1125X_REG_GPO_DIR_LEN); + + /* configuration of channel order */ + max1125x_configure_chmap(dev, channel_cfg->channel_id); + + return 0; +} + +static int max1125x_validate_buffer_size(const struct adc_sequence *sequence) +{ + size_t needed = sizeof(uint8_t) * (sequence->resolution / 8); + + if (sequence->options) { + needed *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed) { + return -ENOMEM; + } + + return 0; +} + +static int max1125x_validate_sequence(const struct device *dev, const struct adc_sequence *sequence) +{ + int err; + + if (sequence->oversampling) { + LOG_ERR("MAX1125X: oversampling not supported"); + return -ENOTSUP; + } + + err = max1125x_validate_buffer_size(sequence); + if (err) { + LOG_ERR("MAX1125X: buffer size too small"); + return -ENOTSUP; + } + + return 0; +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) +{ + struct max1125x_data *data = CONTAINER_OF(ctx, struct max1125x_data, ctx); + + if (repeat_sampling) { + data->buffer = data->repeat_buffer; + } +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct max1125x_data *data = CONTAINER_OF(ctx, struct max1125x_data, ctx); + + data->repeat_buffer = data->buffer; + + max1125x_start_conversion(data->dev); + + k_sem_give(&data->acq_sem); +} + +static int max1125x_adc_start_read(const struct device *dev, const struct adc_sequence *sequence) +{ + int rc; + struct max1125x_data *data = dev->data; + + rc = max1125x_validate_sequence(dev, sequence); + if (rc != 0) { + return rc; + } + + data->buffer = sequence->buffer; + + adc_context_start_read(&data->ctx, sequence); + + return adc_context_wait_for_completion(&data->ctx); +} + +static int max1125x_adc_read_async(const struct device *dev, const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + int rc; + struct max1125x_data *data = dev->data; + + adc_context_lock(&data->ctx, async ? true : false, async); + rc = max1125x_adc_start_read(dev, sequence); + adc_context_release(&data->ctx, rc); + + return rc; +} + +static int max1125x_adc_perform_read(const struct device *dev) +{ + struct max1125x_data *data = dev->data; + int rc; + + rc = max1125x_read_sample(dev); + if (rc != 0) { + LOG_ERR("reading sample failed (err %d)", rc); + adc_context_complete(&data->ctx, rc); + return rc; + } + + return rc; +} + +static int max1125x_read(const struct device *dev, const struct adc_sequence *sequence) +{ + return max1125x_adc_read_async(dev, sequence, NULL); +} + +static void max1125x_acquisition_thread(const struct device *dev) +{ + struct max1125x_data *data = dev->data; + int rc; + + while (true) { + k_sem_take(&data->acq_sem, K_FOREVER); + + rc = max1125x_wait_data_ready(dev); + if (rc != 0) { + LOG_ERR("MAX1125X: failed to get ready status (err %d)", rc); + adc_context_complete(&data->ctx, rc); + break; + } + + max1125x_adc_perform_read(dev); + } +} + +static int max1125x_init(const struct device *dev) +{ + int err; + const struct max1125x_config *config = dev->config; + struct max1125x_data *data = dev->data; + + data->dev = dev; + + k_sem_init(&data->acq_sem, 0, 1); + k_sem_init(&data->data_ready_signal, 0, 1); + + if (!spi_is_ready_dt(&config->bus)) { + LOG_ERR("spi bus %s not ready", config->bus.bus->name); + return -ENODEV; + } + + if (config->self_calibration) { + LOG_INF("performing self calibration process"); + max1125x_self_calibration(dev); + } + + err = gpio_pin_configure_dt(&config->drdy_gpio, GPIO_INPUT); + if (err != 0) { + LOG_ERR("failed to initialize GPIO for data ready (err %d)", err); + return err; + } + + err = gpio_pin_interrupt_configure_dt(&config->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (err != 0) { + LOG_ERR("failed to configure data ready interrupt (err %d)", err); + return -EIO; + } + + gpio_init_callback(&data->callback_data_ready, max1125x_data_ready_handler, + BIT(config->drdy_gpio.pin)); + err = gpio_add_callback(config->drdy_gpio.port, &data->callback_data_ready); + if (err != 0) { + LOG_ERR("failed to add data ready callback (err %d)", err); + return -EIO; + } + + const k_tid_t tid = k_thread_create( + &data->thread, data->stack, K_THREAD_STACK_SIZEOF(data->stack), + (k_thread_entry_t)max1125x_acquisition_thread, (void *)dev, NULL, NULL, + CONFIG_ADC_MAX1125X_ACQUISITION_THREAD_PRIORITY, 0, K_NO_WAIT); + k_thread_name_set(tid, "adc_max1125x"); + + adc_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static const struct adc_driver_api max1125x_api = { + .channel_setup = max1125x_channel_setup, + .read = max1125x_read, + .ref_internal = 2048, +#ifdef CONFIG_ADC_ASYNC + .read_async = max1125x_adc_read_async, +#endif +}; + +#define DT_INST_MAX1125X(inst, t) DT_INST(inst, maxim_max##t) + +#define MAX1125X_INIT(t, n, odr_delay_us, res, mux, pgab) \ + static const struct max1125x_config max##t##_cfg_##n = { \ + .bus = SPI_DT_SPEC_GET(DT_INST_MAX1125X(n, t), \ + SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB, \ + 1), \ + .odr_delay = odr_delay_us, \ + .resolution = res, \ + .multiplexer = mux, \ + .pga = pgab, \ + .drdy_gpio = GPIO_DT_SPEC_GET_OR(DT_INST_MAX1125X(n, t), drdy_gpios, {0}), \ + .self_calibration = DT_PROP_OR(DT_INST_MAX1125X(n, t), self_calibration, 0), \ + .gpio.gpio0_enable = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpio0_enable, 1), \ + .gpio.gpio1_enable = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpio1_enable, 0), \ + .gpio.gpio0_direction = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpio0_direction, 0), \ + .gpio.gpio1_direction = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpio1_direction, 0), \ + .gpo.gpo0_enable = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpo1_enable, 0), \ + .gpo.gpo1_enable = DT_PROP_OR(DT_INST_MAX1125X(n, t), gpo1_enable, 0), \ + }; \ + static struct max1125x_data max##t##_data_##n = { \ + ADC_CONTEXT_INIT_LOCK(max##t##_data_##n, ctx), \ + ADC_CONTEXT_INIT_TIMER(max##t##_data_##n, ctx), \ + ADC_CONTEXT_INIT_SYNC(max##t##_data_##n, ctx), \ + }; \ + DEVICE_DT_DEFINE(DT_INST_MAX1125X(n, t), max1125x_init, NULL, &max##t##_data_##n, \ + &max##t##_cfg_##n, POST_KERNEL, CONFIG_ADC_MAX1125X_INIT_PRIORITY, \ + &max1125x_api); + +/* Each data register is a 16-bit read-only register. Any attempt to write + * data to this location will have no effect. The data read from these + * registers is clocked out MSB first. The result is stored in a format + * according to the FORMAT bit in the CTRL1 register. The data format + * while in unipolar mode is always offset binary. In offset binary + * format the most negative value is 0x0000, the midscale value is 0x8000 and + * the most positive value is 0xFFFF. In bipolar mode if the FORMAT + * bit = ‘1’ then the data format is offset binary. If the FORMAT + * bit= ‘0’, then the data format is two’s complement. In two’s + * complement the negative full-scale value is 0x8000, the midscale is 0x0000 + * and the positive full scale is 0x7FFF. Any input exceeding the available + * input range is limited to the minimum or maximum data value. + */ + +#define MAX11253_RESOLUTION 16 + +/* Each data register is a 24-bit read-only register. Any attempt to write + * data to this location will have no effect. The data read from these + * registers is clocked out MSB first. The result is stored in a format + * according to the FORMAT bit in the CTRL1 register. The data format + * while in unipolar mode is always offset binary. In offset binary format + * the most negative value is 0x000000, the midscale value is 0x800000 and + * the most positive value is 0xFFFFFF. In bipolar mode if the FORMAT + * bit = ‘1’ then the data format is offset binary. If the FORMAT bit = ‘0’, + * then the data format is two’s complement. In two’s complement the negative + * full-scale value is 0x800000, the midscale is 0x000000 and the positive + * full scale is 0x7FFFFF. Any input exceeding the available input range is + * limited to the minimum or maximum data value. + */ + +#define MAX11254_RESOLUTION 24 + +/* + * Approximated MAX1125X acquisition times in microseconds. These are + * used for the initial delay when polling for data ready. + * + * {1.9 SPS, 3.9 SPS, 7.8 SPS, 15.6 SPS, 31.2 SPS, 62.5 SPS, 125 SPS, 250 SPS, 500 SPS, + * 1000 SPS, 2000 SPS, 4000 SPS, 8000 SPS, 16000 SPS, 32000 SPS, 64000 SPS} + */ +#define MAX1125X_ODR_DELAY_US \ + { \ + 526315, 256410, 128205, 64102, 32051, 16000, 8000, 4000, 2000, 1000, 500, 250, \ + 125, 62, 31, 15 \ + } + +/* + * MAX11253: 16 bit, 6-channel, programmable gain amplifier, delta-sigma + */ +#define MAX11253_INIT(n) \ + MAX1125X_INIT(11253, n, MAX1125X_ODR_DELAY_US, MAX11253_RESOLUTION, false, true) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT maxim_max11253 +DT_INST_FOREACH_STATUS_OKAY(MAX11253_INIT) + +/* + * MAX1125X: 24 bit, 6-channel, programmable gain amplifier, delta-sigma + */ + +#define MAX11254_INIT(n) \ + MAX1125X_INIT(11254, n, MAX1125X_ODR_DELAY_US, MAX11254_RESOLUTION, false, true) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT maxim_max11254 +DT_INST_FOREACH_STATUS_OKAY(MAX11254_INIT) diff --git a/dts/bindings/adc/maxim,max11253.yaml b/dts/bindings/adc/maxim,max11253.yaml new file mode 100644 index 00000000000..bec8b31b119 --- /dev/null +++ b/dts/bindings/adc/maxim,max11253.yaml @@ -0,0 +1,5 @@ +description: Maxim Integrated MAX11253 SPI ADC + +compatible: "maxim,max11253" + +include: maxim,max1125x-base.yaml diff --git a/dts/bindings/adc/maxim,max11254.yaml b/dts/bindings/adc/maxim,max11254.yaml new file mode 100644 index 00000000000..a98e6f12737 --- /dev/null +++ b/dts/bindings/adc/maxim,max11254.yaml @@ -0,0 +1,5 @@ +description: Maxim Integrated MAX11254 SPI ADC + +compatible: "maxim,max11254" + +include: maxim,max1125x-base.yaml diff --git a/dts/bindings/adc/maxim,max1125x-base.yaml b/dts/bindings/adc/maxim,max1125x-base.yaml new file mode 100644 index 00000000000..0ca6473b8a7 --- /dev/null +++ b/dts/bindings/adc/maxim,max1125x-base.yaml @@ -0,0 +1,36 @@ +# Common fields for Analog Devices MAX1125x + +include: [adc-controller.yaml, spi-device.yaml] + +properties: + "#io-channel-cells": + const: 1 + gpio0-enable: + type: boolean + description: this option for gpio0 enable or disable + gpio1-enable: + type: boolean + description: this option for gpio1 enable or disable + gpio0-direction: + type: boolean + description: this option for gpio0 direction enable for input, disable for output + gpio1-direction: + type: boolean + description: this option for gpio0 direction enable for input, disable for output + gpo0-enable: + type: boolean + description: this option for gpo0 enable or disable + gpo1-enable: + type: boolean + description: this option for gpo1 enable or disable + drdy-gpios: + type: phandle-array + required: true + description: | + gpio for data ready, becomes active when a conversion result is ready + self-calibration: + type: boolean + description: start self calibration during channel setup + +io-channel-cells: + - input diff --git a/tests/drivers/build_all/adc/app.overlay b/tests/drivers/build_all/adc/app.overlay index 9c063197863..8cba0de309a 100644 --- a/tests/drivers/build_all/adc/app.overlay +++ b/tests/drivers/build_all/adc/app.overlay @@ -107,6 +107,8 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, + <&test_gpio 0 0>, + <&test_gpio 0 0>, <&test_gpio 0 0>; test_spi_mcp3204: mcp3204@0 { @@ -187,6 +189,36 @@ #io-channel-cells = <1>; }; + test_spi_max11254: max11254@a { + compatible = "maxim,max11254"; + reg = <0xa>; + spi-max-frequency = <0>; + gpio0-enable; + gpio1-enable; + gpio0-direction; + gpio1-direction; + gpo0-enable; + gpo1-enable; + drdy-gpios = <&test_gpio 0 0>; + self-calibration; + #io-channel-cells = <1>; + }; + + test_spi_max11253: max11253@b { + compatible = "maxim,max11253"; + reg = <0xb>; + spi-max-frequency = <0>; + gpio0-enable; + gpio1-enable; + gpio0-direction; + gpio1-direction; + gpo0-enable; + gpo1-enable; + drdy-gpios = <&test_gpio 0 0>; + self-calibration; + #io-channel-cells = <1>; + }; + test_spi_ads114s08: ads114s08@9 { compatible = "ti,ads114s08"; diff --git a/tests/drivers/build_all/adc/testcase.yaml b/tests/drivers/build_all/adc/testcase.yaml index a069e748f2a..b151e1b3eaf 100644 --- a/tests/drivers/build_all/adc/testcase.yaml +++ b/tests/drivers/build_all/adc/testcase.yaml @@ -15,6 +15,7 @@ tests: - adc_ads1112 - adc_ads114s08 - adc_emul + - adc_max1125x extra_args: "CONFIG_GPIO=y" drivers.adc.cc32xx.build: platform_allow: cc3220sf_launchxl