diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt index eec91be800a21..a8761a6b53bc8 100644 --- a/drivers/dac/CMakeLists.txt +++ b/drivers/dac/CMakeLists.txt @@ -25,3 +25,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_AD569X dac_ad569x.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c) zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_GAU dac_mcux_gau.c) zephyr_library_sources_ifdef(CONFIG_DAC_TEST dac_test.c) +zephyr_library_sources_ifdef(CONFIG_DAC_MAX22017 dac_max22017.c) diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig index 26b17f1a31d57..74def82995488 100644 --- a/drivers/dac/Kconfig +++ b/drivers/dac/Kconfig @@ -61,4 +61,6 @@ source "drivers/dac/Kconfig.ad569x" source "drivers/dac/Kconfig.test" +source "drivers/dac/Kconfig.max22017" + endif # DAC diff --git a/drivers/dac/Kconfig.max22017 b/drivers/dac/Kconfig.max22017 new file mode 100644 index 0000000000000..d7a46229f02bb --- /dev/null +++ b/drivers/dac/Kconfig.max22017 @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +config DAC_MAX22017 + bool "Analog Devices MAX22017 DAC" + default y + depends on DT_HAS_ADI_MAX22017_DAC_ENABLED + select MFD + help + Enable the driver for the Analog Devices MAX22017 DAC + +if DAC_MAX22017 + +config DAC_MAX22017_INIT_PRIORITY + int "Init priority" + default 80 + help + Analog Devices MAX22017 DAC device driver initialization priority. + +endif # DAC_MAX22017 diff --git a/drivers/dac/dac_max22017.c b/drivers/dac/dac_max22017.c new file mode 100644 index 0000000000000..3c971e7a8408b --- /dev/null +++ b/drivers/dac/dac_max22017.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#define DT_DRV_COMPAT adi_max22017_dac +LOG_MODULE_REGISTER(dac_max22017, CONFIG_DAC_LOG_LEVEL); + +struct dac_adi_max22017_config { + const struct device *parent; + uint8_t resolution; + uint8_t nchannels; + const struct gpio_dt_spec gpio_ldac; + const struct gpio_dt_spec gpio_busy; + uint8_t latch_mode[MAX22017_MAX_CHANNEL]; + uint8_t polarity_mode[MAX22017_MAX_CHANNEL]; + uint8_t dac_mode[MAX22017_MAX_CHANNEL]; + uint8_t ovc_mode[MAX22017_MAX_CHANNEL]; + uint16_t timeout; +}; + +static int max22017_channel_setup(const struct device *dev, + const struct dac_channel_cfg *channel_cfg) +{ + int ret; + uint16_t ao_cnfg, gen_cnfg; + uint8_t chan = channel_cfg->channel_id; + const struct dac_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = parent->data; + + if (chan > config->nchannels - 1) { + LOG_ERR("Unsupported channel %d", chan); + return -ENOTSUP; + } + + if (channel_cfg->resolution != config->resolution) { + LOG_ERR("Unsupported resolution %d", chan); + return -ENOTSUP; + } + + k_mutex_lock(&data->lock, K_FOREVER); + ret = max22017_reg_read(parent, MAX22017_AO_CNFG_OFF, &ao_cnfg); + if (ret) { + goto fail; + } + + ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_EN, BIT(chan)); + + if (!config->latch_mode[chan]) { + ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_LD_CNFG, BIT(chan)); + } + + if (config->polarity_mode[chan]) { + ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_UNI, BIT(chan)); + } + + if (config->dac_mode[chan]) { + ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_MODE, BIT(chan)); + } + + ret = max22017_reg_write(parent, MAX22017_AO_CNFG_OFF, ao_cnfg); + if (ret) { + goto fail; + } + + ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg); + if (ret) { + goto fail; + } + + if (config->ovc_mode[chan]) { + gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_CNFG, BIT(chan)); + /* Over current shutdown mode */ + if (config->ovc_mode[chan] == 2) { + gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_SHDN_CNFG, BIT(chan)); + } + } + + ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int max22017_write_value(const struct device *dev, uint8_t channel, uint32_t value) +{ + int ret; + uint16_t ao_sta; + const struct dac_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = parent->data; + + if (channel > config->nchannels - 1) { + LOG_ERR("unsupported channel %d", channel); + return ENOTSUP; + } + + if (value >= (1 << config->resolution)) { + LOG_ERR("Value %d out of range", value); + return -EINVAL; + } + + k_mutex_lock(&data->lock, K_FOREVER); + if (config->gpio_busy.port) { + if (gpio_pin_get_dt(&config->gpio_busy)) { + ret = -EBUSY; + goto fail; + } + } else { + ret = max22017_reg_read(parent, MAX22017_AO_STA_OFF, &ao_sta); + if (ret) { + goto fail; + } + if (FIELD_GET(MAX22017_AO_STA_BUSY_STA, ao_sta)) { + ret = -EBUSY; + goto fail; + } + } + + ret = max22017_reg_write(parent, MAX22017_AO_DATA_CHn_OFF(channel), + FIELD_PREP(MAX22017_AO_DATA_CHn_AO_DATA_CH, value)); + if (ret) { + goto fail; + } + + if (config->latch_mode[channel]) { + if (config->gpio_ldac.port) { + gpio_pin_set_dt(&config->gpio_ldac, false); + k_sleep(K_USEC(MAX22017_LDAC_TOGGLE_TIME)); + gpio_pin_set_dt(&config->gpio_ldac, true); + } else { + ret = max22017_reg_write( + parent, MAX22017_AO_CMD_OFF, + FIELD_PREP(MAX22017_AO_CMD_AO_LD_CTRL, BIT(channel))); + } + } +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int max22017_init(const struct device *dev) +{ + int ret; + uint16_t gen_cnfg = 0, gen_int_en = 0; + const struct dac_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = config->parent->data; + + if (!device_is_ready(config->parent)) { + LOG_ERR("parent adi_max22017 MFD device '%s' not ready", config->parent->name); + return -EINVAL; + } + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg); + if (ret) { + goto fail; + } + + ret = max22017_reg_read(parent, MAX22017_GEN_INTEN_OFF, &gen_int_en); + if (ret) { + goto fail; + } + + if (config->timeout) { + gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_EN, 1) | + FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_SEL, (config->timeout / 100) - 1); + gen_int_en |= FIELD_PREP(MAX22017_GEN_INTEN_TMOUT_INTEN, 1); + } + + ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg); + if (ret) { + goto fail; + } + + ret = max22017_reg_write(parent, MAX22017_GEN_INTEN_OFF, gen_int_en); + if (ret) { + goto fail; + } + + if (config->gpio_ldac.port) { + ret = gpio_pin_configure_dt(&config->gpio_ldac, GPIO_OUTPUT_ACTIVE); + if (ret) { + LOG_ERR("failed to initialize GPIO ldac pin"); + goto fail; + } + } + + if (config->gpio_busy.port) { + ret = gpio_pin_configure_dt(&config->gpio_busy, GPIO_INPUT); + if (ret) { + LOG_ERR("failed to initialize GPIO busy pin"); + goto fail; + } + } + +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static const struct dac_driver_api max22017_driver_api = { + .channel_setup = max22017_channel_setup, + .write_value = max22017_write_value, +}; + +#define DAC_MAX22017_DEVICE(id) \ + static const struct dac_adi_max22017_config dac_adi_max22017_config_##id = { \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(id)), \ + .resolution = DT_INST_PROP_OR(id, resolution, 16), \ + .nchannels = DT_INST_PROP_OR(id, num_channels, 2), \ + .gpio_busy = GPIO_DT_SPEC_INST_GET_OR(id, busy_gpios, {0}), \ + .gpio_ldac = GPIO_DT_SPEC_INST_GET_OR(id, ldac_gpios, {0}), \ + .latch_mode = DT_INST_PROP_OR(id, latch_mode, {0}), \ + .polarity_mode = DT_INST_PROP_OR(id, polarity_mode, {0}), \ + .dac_mode = DT_INST_PROP_OR(id, dac_mode, {0}), \ + .ovc_mode = DT_INST_PROP_OR(id, overcurrent_mode, {0}), \ + .timeout = DT_INST_PROP_OR(id, timeout, 0), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(id, max22017_init, NULL, NULL, &dac_adi_max22017_config_##id, \ + POST_KERNEL, CONFIG_DAC_MAX22017_INIT_PRIORITY, \ + &max22017_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(DAC_MAX22017_DEVICE); diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index dcc4b96a54fa8..3e419edc12e72 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -44,6 +44,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_LMP90XXX gpio_lmp90xxx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_LPC11U6X gpio_lpc11u6x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MAX14906 gpio_max14906.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MAX14916 gpio_max14916.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_MAX22017 gpio_max22017.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MAX22190 gpio_max22190.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MAX32 gpio_max32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCHP_MEC5 gpio_mchp_mec5.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ba1ed8a3f64be..9190676281ac7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -134,6 +134,7 @@ source "drivers/gpio/Kconfig.lmp90xxx" source "drivers/gpio/Kconfig.lpc11u6x" source "drivers/gpio/Kconfig.max14906" source "drivers/gpio/Kconfig.max14916" +source "drivers/gpio/Kconfig.max22017" source "drivers/gpio/Kconfig.max22190" source "drivers/gpio/Kconfig.max32" source "drivers/gpio/Kconfig.mchp_mss" diff --git a/drivers/gpio/Kconfig.max22017 b/drivers/gpio/Kconfig.max22017 new file mode 100644 index 0000000000000..49c07edd92b0b --- /dev/null +++ b/drivers/gpio/Kconfig.max22017 @@ -0,0 +1,32 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_MAX22017 + bool "Analog Devices MAX22017 GPIO support" + default y + depends on DT_HAS_ADI_MAX22017_GPIO_ENABLED + select MFD + help + Enable GPIO support for the Analog Devices MAX22017 + +if GPIO_MAX22017 + +config GPIO_MAX22017_INIT_PRIORITY + int "Init priority" + default 81 + help + Analog Devices MAX22017 gpio device driver initialization priority. + +config GPIO_MAX22017_INT_QUIRK + bool "MAX22017 GPIO Interrupt quirk" + help + The GPIO controller will not report any new GPI interrupt as long as its interrupt status + register hasn't been read. + Reading the interrupt status register happens on a falling edge of the INT pin. + There seems to be a condition when the GPIO controller detects an interrupt but it's INT + pin stays high which masks any subsequent interrupts. + To avoid being stuck in that state, fire a timer to periodically check the interrupt status + register. + +endif # GPIO_MAX22017 diff --git a/drivers/gpio/gpio_max22017.c b/drivers/gpio/gpio_max22017.c new file mode 100644 index 0000000000000..cd9a20107dad0 --- /dev/null +++ b/drivers/gpio/gpio_max22017.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DT_DRV_COMPAT adi_max22017_gpio + +#include +LOG_MODULE_REGISTER(gpio_max22017, CONFIG_GPIO_LOG_LEVEL); + +#include + +#include + +struct gpio_adi_max22017_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + const struct device *parent; +}; + +struct gpio_adi_max22017_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; +#ifdef CONFIG_GPIO_MAX22017_INT_QUIRK + struct k_timer int_quirk_timer; +#endif +}; + +#ifdef CONFIG_GPIO_MAX22017_INT_QUIRK +void isr_quirk_handler(struct k_timer *int_quirk_timer) +{ + int ret; + struct max22017_data *data = k_timer_user_data_get(int_quirk_timer); + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = k_work_submit(&data->int_work); + if (ret < 0) { + LOG_WRN("Could not submit int work: %d", ret); + } + + k_mutex_unlock(&data->lock); +} +#endif + +static int adi_max22017_gpio_set_output(const struct device *dev, uint8_t pin, bool initial_value) +{ + int ret; + uint16_t gpio_data, gpio_ctrl; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); + if (ret) { + goto fail; + } + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); + if (ret) { + goto fail; + } + + if (initial_value) { + gpio_data |= FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); + } else { + gpio_data &= ~FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); + } + + gpio_ctrl |= FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)) | + FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_DIR, BIT(pin)); + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); + if (ret) { + goto fail; + } + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); + +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int adi_max22017_gpio_set_input(const struct device *dev, uint8_t pin) +{ + int ret; + uint16_t gpio_ctrl; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); + if (ret) { + goto fail; + } + + gpio_ctrl |= FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)); + gpio_ctrl &= ~FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_DIR, BIT(pin)); + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +int adi_max22017_gpio_deconfigure(const struct device *dev, uint8_t pin) +{ + int ret; + uint16_t gpio_ctrl; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); + if (ret) { + goto fail; + } + + gpio_ctrl &= ~FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)); + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +int adi_max22017_gpio_set_pin_value(const struct device *dev, uint8_t pin, bool value) +{ + int ret; + uint16_t gpio_data; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); + if (ret) { + goto fail; + } + + if (value) { + gpio_data |= FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); + } else { + gpio_data &= ~FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); + } + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +int adi_max22017_gpio_get_pin_value(const struct device *dev, uint8_t pin, bool *value) +{ + int ret; + uint16_t gpio_data; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); + if (ret) { + goto fail; + } + + *value = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data) & BIT(pin); + +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int adi_max22017_gpio_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + int ret; + uint16_t gpio_data, tmp_val; + struct max22017_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); + if (ret) { + goto fail; + } + + tmp_val = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPO_DATA, gpio_data); + tmp_val = (tmp_val & ~mask) | (value & mask); + gpio_data = FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, tmp_val) | + FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPI_DATA, + FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data)); + + ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int gpio_adi_max22017_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_adi_max22017_config *config = dev->config; + int err = -EINVAL; + + if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { + return adi_max22017_gpio_deconfigure(config->parent, pin); + } + + if ((flags & GPIO_SINGLE_ENDED) != 0) { + return -ENOTSUP; + } + + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + return -ENOTSUP; + } + + switch (flags & GPIO_DIR_MASK) { + case GPIO_INPUT: + err = adi_max22017_gpio_set_input(config->parent, pin); + break; + case GPIO_OUTPUT: + err = adi_max22017_gpio_set_output(config->parent, pin, + (flags & GPIO_OUTPUT_INIT_HIGH) != 0); + break; + default: + return -ENOTSUP; + } + return err; +} + +static int gpio_adi_max22017_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + int ret; + uint16_t gpio_int, gen_int_en; + const struct gpio_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = parent->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + if (mode == GPIO_INT_MODE_DISABLED) { + ret = -ENOTSUP; + goto fail; + } + + ret = max22017_reg_read(parent, MAX22017_GEN_GPI_INT_OFF, &gpio_int); + if (ret) { + goto fail; + } + + if (mode & GPIO_INT_EDGE_RISING) { + gpio_int |= FIELD_PREP(MAX22017_GEN_GPI_INT_GPI_POS_EDGE_INT, BIT(pin)); + } + if (mode & GPIO_INT_EDGE_FALLING) { + gpio_int |= FIELD_PREP(MAX22017_GEN_GPI_INT_GPI_NEG_EDGE_INT, BIT(pin)); + } + + ret = max22017_reg_write(parent, MAX22017_GEN_GPI_INT_OFF, gpio_int); + if (ret) { + goto fail; + } + + ret = max22017_reg_read(parent, MAX22017_GEN_INTEN_OFF, &gen_int_en); + if (ret) { + goto fail; + } + + ret = max22017_reg_write(parent, MAX22017_GEN_INTEN_OFF, + gen_int_en | FIELD_PREP(MAX22017_GEN_INTEN_GPI_INTEN, 1)); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int gpio_adi_max22017_port_get_raw(const struct device *dev, gpio_port_value_t *value) +{ + int ret; + const struct gpio_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = parent->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(parent, MAX22017_GEN_GPIO_DATA_OFF, (uint16_t *)value); + if (ret) { + goto fail; + } + + *value = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, *value); + +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int gpio_adi_max22017_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_adi_max22017_config *config = dev->config; + + return adi_max22017_gpio_port_set_masked_raw(config->parent, mask, value); +} + +static int gpio_adi_max22017_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_adi_max22017_config *config = dev->config; + + return adi_max22017_gpio_port_set_masked_raw(config->parent, pins, pins); +} + +static int gpio_adi_max22017_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_adi_max22017_config *config = dev->config; + + return adi_max22017_gpio_port_set_masked_raw(config->parent, pins, 0); +} + +static int gpio_adi_max22017_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) +{ + int ret; + uint16_t gpio_data, tmp_val; + const struct gpio_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + struct max22017_data *data = parent->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(parent, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); + if (ret) { + goto fail; + } + + tmp_val = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPO_DATA, gpio_data); + tmp_val = (tmp_val ^ pins); + gpio_data = FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, tmp_val) | + FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPI_DATA, + FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data)); + + ret = max22017_reg_write(parent, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +static int gpio_adi_max22017_manage_cb(const struct device *dev, struct gpio_callback *callback, + bool set) +{ + int ret; + const struct gpio_adi_max22017_config *config = dev->config; + struct max22017_data *data = config->parent->data; + + k_mutex_lock(&data->lock, K_FOREVER); + ret = gpio_manage_callback(&data->callbacks_gpi, callback, set); + k_mutex_unlock(&data->lock); + + return ret; +} + +static int gpio_adi_max22017_init(const struct device *dev) +{ + const struct gpio_adi_max22017_config *config = dev->config; + const struct device *parent = config->parent; + + if (!device_is_ready(parent)) { + LOG_ERR("parent adi_max22017 MFD device '%s' not ready", config->parent->name); + return -EINVAL; + } + +#ifdef CONFIG_GPIO_MAX22017_INT_QUIRK + struct gpio_adi_max22017_data *data = dev->data; + struct k_timer *t = &data->int_quirk_timer; + + k_timer_init(t, isr_quirk_handler, NULL); + k_timer_user_data_set(t, parent->data); + k_timer_start(t, K_MSEC(25), K_MSEC(25)); +#endif + return 0; +} + +static const struct gpio_driver_api gpio_adi_max22017_api = { + .pin_configure = gpio_adi_max22017_configure, + .port_set_masked_raw = gpio_adi_max22017_port_set_masked_raw, + .port_set_bits_raw = gpio_adi_max22017_port_set_bits_raw, + .port_clear_bits_raw = gpio_adi_max22017_port_clear_bits_raw, + .port_toggle_bits = gpio_adi_max22017_port_toggle_bits, + .port_get_raw = gpio_adi_max22017_port_get_raw, + .pin_interrupt_configure = gpio_adi_max22017_pin_interrupt_configure, + .manage_callback = gpio_adi_max22017_manage_cb, +}; + +#define GPIO_MAX22017_DEVICE(id) \ + static const struct gpio_adi_max22017_config gpio_adi_max22017_##id##_cfg = { \ + .common = \ + { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \ + }, \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(id)), \ + }; \ + static struct gpio_adi_max22017_data gpio_adi_max22017_##id##_data; \ + DEVICE_DT_INST_DEFINE(id, gpio_adi_max22017_init, NULL, &gpio_adi_max22017_##id##_data, \ + &gpio_adi_max22017_##id##_cfg, POST_KERNEL, \ + CONFIG_GPIO_MAX22017_INIT_PRIORITY, &gpio_adi_max22017_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX22017_DEVICE) diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt index 8fe7ac34caeee..5a7f5faee90ac 100644 --- a/drivers/mfd/CMakeLists.txt +++ b/drivers/mfd/CMakeLists.txt @@ -21,3 +21,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801 mfd_ite_it8801.c) zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801_ALTCTRL mfd_it8801_altctrl.c) zephyr_library_sources_ifdef(CONFIG_MFD_AW9523B mfd_aw9523b.c) zephyr_library_sources_ifdef(CONFIG_MFD_DS3231 mfd_ds3231.c) +zephyr_library_sources_ifdef(CONFIG_MFD_MAX22017 mfd_max22017.c) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b11e3dfd4b245..0ccb41f6ba1b6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -25,6 +25,7 @@ source "drivers/mfd/Kconfig.aw9523b" source "drivers/mfd/Kconfig.bd8lb600fs" source "drivers/mfd/Kconfig.ds3231" source "drivers/mfd/Kconfig.max20335" +source "drivers/mfd/Kconfig.max22017" source "drivers/mfd/Kconfig.max31790" source "drivers/mfd/Kconfig.nct38xx" source "drivers/mfd/Kconfig.npm1300" diff --git a/drivers/mfd/Kconfig.max22017 b/drivers/mfd/Kconfig.max22017 new file mode 100644 index 0000000000000..8a28f19614e43 --- /dev/null +++ b/drivers/mfd/Kconfig.max22017 @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +config MFD_MAX22017 + bool "Analog Devices MAX22017 DAC/GPIO chip" + default y + depends on SPI + select CRC + depends on DT_HAS_ADI_MAX22017_ENABLED + help + Enable the driver for the Analog Devices MAX22017 DAC/GPIO chip diff --git a/drivers/mfd/mfd_max22017.c b/drivers/mfd/mfd_max22017.c new file mode 100644 index 0000000000000..a4b7d6fa1557c --- /dev/null +++ b/drivers/mfd/mfd_max22017.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max22017 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mfd_max22017, CONFIG_MFD_LOG_LEVEL); + +#include + +struct max22017_config { + struct spi_dt_spec spi; + const struct gpio_dt_spec gpio_reset; + const struct gpio_dt_spec gpio_int; + bool crc_mode; +}; + +int max22017_reg_read(const struct device *dev, uint8_t addr, uint16_t *value) +{ + int ret; + const struct max22017_config *config = dev->config; + const struct max22017_data *data = dev->data; + uint8_t rxbuffer[4] = {0}; + size_t recv_len = 3; + + *value = 0; + addr = FIELD_PREP(MAX22017_SPI_TRANS_ADDR, addr) | FIELD_PREP(MAX22017_SPI_TRANS_DIR, 1); + + if (data->crc_enabled) { + recv_len += 1; + } + + const struct spi_buf txb[] = { + { + .buf = &addr, + .len = 1, + }, + }; + const struct spi_buf rxb[] = { + { + .buf = rxbuffer, + .len = recv_len, + }, + }; + + struct spi_buf_set tx = { + .buffers = txb, + .count = ARRAY_SIZE(txb), + }; + + struct spi_buf_set rx = { + .buffers = rxb, + .count = ARRAY_SIZE(rxb), + }; + + ret = spi_transceive_dt(&config->spi, &tx, &rx); + + if (ret) { + return ret; + } + + if (!data->crc_enabled) { + goto out_skip_crc; + } + + uint8_t crc_in[] = {addr, rxbuffer[1], rxbuffer[2]}; + + ret = crc8(crc_in, 3, MAX22017_CRC_POLY, 0, true); + + if (ret != rxbuffer[3]) { + LOG_ERR("Reg read: CRC Mismatch calculated / read: %x / %x", ret, rxbuffer[3]); + return -EINVAL; + } + +out_skip_crc: + *value = rxbuffer[1] | rxbuffer[2] << 8; + *value = sys_be16_to_cpu(*value); + + return 0; +} + +int max22017_reg_write(const struct device *dev, uint8_t addr, uint16_t value) +{ + uint8_t crc; + size_t crc_len = 0; + const struct max22017_config *config = dev->config; + const struct max22017_data *data = dev->data; + + addr = FIELD_PREP(MAX22017_SPI_TRANS_ADDR, addr) | FIELD_PREP(MAX22017_SPI_TRANS_DIR, 0); + value = sys_cpu_to_be16(value); + + if (data->crc_enabled) { + uint8_t crc_in[] = {addr, ((uint8_t *)&value)[0], ((uint8_t *)&value)[1]}; + + crc_len = 1; + crc = crc8(crc_in, 3, MAX22017_CRC_POLY, 0, true); + } + + const struct spi_buf buf[] = { + { + .buf = &addr, + .len = 1, + }, + { + .buf = &value, + .len = 2, + }, + { + .buf = &crc, + .len = crc_len, + }, + }; + + struct spi_buf_set tx = { + .buffers = buf, + .count = ARRAY_SIZE(buf), + }; + + return spi_write_dt(&config->spi, &tx); +} + +static int max22017_reset(const struct device *dev) +{ + int ret = 0; + uint16_t ao_sta; + const struct max22017_config *config = dev->config; + + if (config->gpio_reset.port != NULL) { + ret = gpio_pin_set_dt(&config->gpio_reset, 0); + if (ret) { + return ret; + } + k_sleep(K_MSEC(100)); + ret = gpio_pin_set_dt(&config->gpio_reset, 1); + k_sleep(K_MSEC(500)); + } else { + ret = max22017_reg_write(dev, MAX22017_GEN_RST_CTRL_OFF, + FIELD_PREP(MAX22017_GEN_RST_CTRL_GEN_RST, 1)); + if (ret) { + return ret; + } + k_sleep(K_MSEC(100)); + ret = max22017_reg_write(dev, MAX22017_GEN_RST_CTRL_OFF, + FIELD_PREP(MAX22017_GEN_RST_CTRL_GEN_RST, 0)); + if (ret) { + return ret; + } + k_sleep(K_MSEC(500)); + ret = max22017_reg_read(dev, MAX22017_AO_STA_OFF, &ao_sta); + if (ret) { + return ret; + } + if (FIELD_GET(MAX22017_AO_STA_BUSY_STA, ao_sta)) { + ret = -EBUSY; + } + } + return ret; +} + +static void max22017_isr(const struct device *dev, struct gpio_callback *gpio_cb, uint32_t pins) +{ + struct max22017_data *data = CONTAINER_OF(gpio_cb, struct max22017_data, callback_int); + int ret; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = k_work_submit(&data->int_work); + if (ret < 0) { + LOG_WRN("Could not submit int work: %d", ret); + } + + k_mutex_unlock(&data->lock); +} + +static void max22017_int_worker(struct k_work *work) +{ + struct max22017_data *data = CONTAINER_OF(work, struct max22017_data, int_work); + const struct device *dev = data->dev; + int ret; + uint16_t gen_int; + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_read(dev, MAX22017_GEN_INT_OFF, &gen_int); + if (ret) { + LOG_ERR("Unable to read GEN_INT register"); + goto fail; + } + + if (FIELD_GET(MAX22017_GEN_INT_FAIL_INT, gen_int)) { + LOG_ERR("Boot failure"); + } + + ret = FIELD_GET(MAX22017_GEN_INT_CONV_OVF_INT, gen_int); + if (ret) { + LOG_ERR("Conversion failure on channels: %s %s", (ret & BIT(0)) ? "0" : "", + (ret & BIT(1)) ? "1" : ""); + } + + ret = FIELD_GET(MAX22017_GEN_INT_OPENWIRE_DTCT_INT, gen_int); + if (ret) { + LOG_ERR("Openwire detected on channels: %s %s", (ret & BIT(0)) ? "0" : "", + (ret & BIT(1)) ? "1" : ""); + } + + if (FIELD_GET(MAX22017_GEN_INT_HVDD_INT, gen_int)) { + LOG_ERR("HVDD/HVSS voltage difference below 1.5V"); + } + + if (FIELD_GET(MAX22017_GEN_INT_TMOUT_INT, gen_int)) { + LOG_ERR("SPI transaction timeout"); + } + + ret = FIELD_GET(MAX22017_GEN_INT_THSHDN_INT, gen_int); + if (ret) { + LOG_ERR("Thermal shutdown AO channels: %s %s", (ret & BIT(0)) ? "0" : "", + (ret & BIT(1)) ? "1" : ""); + } + + ret = FIELD_GET(MAX22017_GEN_INT_THWRNG_INT, gen_int); + if (ret) { + LOG_ERR("Thermal warning AO channels: %s %s", (ret & BIT(0)) ? "0" : "", + (ret & BIT(1)) ? "1" : ""); + } + + ret = FIELD_GET(MAX22017_GEN_INT_OVC_INT, gen_int); + if (ret) { + LOG_ERR("Over current on channels: %s %s", (ret & BIT(0)) ? "0" : "", + (ret & BIT(1)) ? "1" : ""); + } + + if (FIELD_GET(MAX22017_GEN_INT_CRC_INT, gen_int)) { + LOG_ERR("CRC Error"); + } + + ret = FIELD_GET(MAX22017_GEN_INT_GPI_INT, gen_int); + if (ret) { + LOG_INF("GPI Interrupt: %d", ret); +#ifdef CONFIG_GPIO_MAX22017 + uint16_t gpi_sta, lsb; + + ret = max22017_reg_read(dev, MAX22017_GEN_GPI_INT_STA_OFF, &gpi_sta); + if (ret) { + goto fail; + } + + /* Aggregate both positive and negative edge together */ + gpi_sta = FIELD_GET(MAX22017_GEN_GPI_INT_GPI_NEG_EDGE_INT, gpi_sta) | + FIELD_GET(MAX22017_GEN_GPI_INT_GPI_POS_EDGE_INT, gpi_sta); + while (gpi_sta) { + lsb = LSB_GET(gpi_sta); + gpio_fire_callbacks(&data->callbacks_gpi, dev, lsb); + gpi_sta &= ~lsb; + } +#endif + } +fail: + k_mutex_unlock(&data->lock); +} + +static int max22017_init(const struct device *dev) +{ + const struct max22017_config *config = dev->config; + struct max22017_data *data = dev->data; + uint16_t version; + int ret; + uint16_t gen_cnfg = 0, gen_int_en = 0; + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI spi %s not ready", config->spi.bus->name); + return -ENODEV; + } + + if (config->gpio_reset.port != NULL) { + ret = gpio_pin_configure_dt(&config->gpio_reset, GPIO_OUTPUT_ACTIVE); + if (ret) { + LOG_ERR("failed to initialize GPIO reset pin"); + return ret; + } + } + + ret = max22017_reset(dev); + if (ret) { + LOG_ERR("failed to reset MAX22017"); + return ret; + } + + data->dev = dev; + k_work_init(&data->int_work, max22017_int_worker); + k_mutex_init(&data->lock); + + if (config->gpio_int.port) { + ret = gpio_pin_configure_dt(&config->gpio_int, GPIO_INPUT); + if (ret) { + LOG_ERR("failed to initialize GPIO interrupt pin"); + goto fail; + } + + ret = gpio_pin_interrupt_configure_dt(&config->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); + if (ret) { + LOG_ERR("failed to configure interrupt pin"); + goto fail; + } + + gpio_init_callback(&data->callback_int, max22017_isr, BIT(config->gpio_int.pin)); + ret = gpio_add_callback(config->gpio_int.port, &data->callback_int); + if (ret) { + LOG_ERR("failed to add data ready callback"); + goto fail; + } + } + + k_mutex_lock(&data->lock, K_FOREVER); + + ret = max22017_reg_write(dev, MAX22017_AO_CNFG_OFF, 0); + if (ret) { + return ret; + } + + ret = max22017_reg_write(dev, MAX22017_AO_DATA_CHn_OFF(0), 0); + if (ret) { + goto fail; + } + + ret = max22017_reg_write(dev, MAX22017_AO_DATA_CHn_OFF(1), 0); + if (ret) { + goto fail; + } + + if (config->crc_mode) { + gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_CRC_EN, 1); + gen_int_en |= FIELD_PREP(MAX22017_GEN_INTEN_CRC_INTEN, 1); + } + + ret = max22017_reg_write(dev, MAX22017_GEN_INTEN_OFF, gen_int_en); + if (ret) { + goto fail; + } + + ret = max22017_reg_write(dev, MAX22017_GEN_CNFG_OFF, gen_cnfg); + if (ret) { + goto fail; + } + + if (config->crc_mode) { + data->crc_enabled = true; + } + + ret = max22017_reg_read(dev, MAX22017_GEN_ID_OFF, &version); + if (ret) { + LOG_ERR("Unable to read MAX22017 version over SPI: %d", ret); + goto fail; + } + + LOG_INF("MAX22017 version: 0x%lx 0x%lx", FIELD_GET(MAX22017_GEN_ID_PROD_ID, version), + FIELD_GET(MAX22017_GEN_ID_REV_ID, version)); + +fail: + k_mutex_unlock(&data->lock); + return ret; +} + +#define INST_DT_MAX22017(index) \ + static const struct max22017_config max22017_config_##index = { \ + .spi = SPI_DT_SPEC_INST_GET(index, SPI_OP_MODE_MASTER | SPI_WORD_SET(8U), 0U), \ + .gpio_int = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}), \ + .gpio_reset = GPIO_DT_SPEC_INST_GET_OR(index, rst_gpios, {0}), \ + .crc_mode = DT_INST_PROP_OR(index, crc_mode, 0), \ + }; \ + \ + static struct max22017_data max22017_data_##index; \ + \ + DEVICE_DT_INST_DEFINE(index, max22017_init, NULL, &max22017_data_##index, \ + &max22017_config_##index, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(INST_DT_MAX22017); diff --git a/dts/bindings/dac/adi,max22017.yaml b/dts/bindings/dac/adi,max22017.yaml new file mode 100644 index 0000000000000..3497791c079e9 --- /dev/null +++ b/dts/bindings/dac/adi,max22017.yaml @@ -0,0 +1,74 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +include: dac-controller.yaml + +description: Analog Devices MAX22017 16bit DAC + +properties: + "#io-channel-cells": + const: 2 + + num-channels: + type: int + description: Number of DAC output channels. + default: 2 + + resolution: + type: int + description: DAC resolution. + default: 16 + + busy-gpios: + description: Busy line indicating the DAC is calculating next sample. + type: phandle-array + + ldac-gpios: + description: Load both DAC latches at the same time. + type: phandle-array + + polarity-mode: + description: | + Unipolar/bipolar mode selection for channels. + 0 Indicates bipolar mode and 1 unipolar mode. + The default settings to bipolar here align with the default mode of the device. + default: [0, 0] + type: uint8-array + + dac-mode: + description: | + Voltage/current mode selection for channels. + 0 indicates voltage mode and 1 indicates current mode. + The default settings to voltage mode here align with the default mode of the device. + default: [0, 0] + type: uint8-array + + latch-mode: + description: | + Latch mode selection for channels. + 0 means the channel is not latched, 1 means latched. + The default settings to non latched should be more straightforward to use than the latched + mode. The latch mode can be used eitheir with the ldac-gpios to load both channels at the + same time or if no ldac-gpios property is set, latching will be done per channel with a + register write. + default: [0, 0] + type: uint8-array + + overcurrent-mode: + description: | + Overcurrent mode selection for channels. + 0 for current limiting mode + 1 for short circuit protection auto power up mode + 2 for short circuit protection shutdown mode + The default setting to current limiting mode here aligns with the default mode of the device. + default: [0, 0] + type: uint8-array + + timeout: + description: | + Timeout in ms. + The value should be between 100 and 1600ms in increments of 100ms. + type: int + +compatible: "adi,max22017-dac" diff --git a/dts/bindings/gpio/adi,max22017-gpio.yaml b/dts/bindings/gpio/adi,max22017-gpio.yaml new file mode 100644 index 0000000000000..81a243ea7af22 --- /dev/null +++ b/dts/bindings/gpio/adi,max22017-gpio.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +description: MAX22017 GPIO + +compatible: "adi,max22017-gpio" + +include: gpio-controller.yaml + +properties: + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/mfd/adi,max22017.yaml b/dts/bindings/mfd/adi,max22017.yaml new file mode 100644 index 0000000000000..06bf2d8a63978 --- /dev/null +++ b/dts/bindings/mfd/adi,max22017.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +include: spi-device.yaml + +description: Analog MAX22017 DAC/GPIO chip common properties + +properties: + rst-gpios: + description: Reset pin for max22017. + type: phandle-array + + crc-mode: + description: Enables CRC over SPI. + type: boolean + + int-gpios: + description: Interrupt gpio + type: phandle-array + +compatible: "adi,max22017" diff --git a/include/zephyr/drivers/mfd/max22017.h b/include/zephyr/drivers/mfd/max22017.h new file mode 100644 index 0000000000000..a8f837020ab21 --- /dev/null +++ b/include/zephyr/drivers/mfd/max22017.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_MAX22017_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MFD_MAX22017_H_ + +#include + +#define MAX22017_LDAC_TOGGLE_TIME 200 +#define MAX22017_MAX_CHANNEL 2 +#define MAX22017_CRC_POLY 0x8c /* reversed 0x31 poly for crc8-maxim */ + +#define MAX22017_GEN_ID_OFF 0x00 +#define MAX22017_GEN_ID_PROD_ID GENMASK(15, 8) +#define MAX22017_GEN_ID_REV_ID GENMASK(7, 0) + +#define MAX22017_GEN_SERIAL_MSB_OFF 0x01 +#define MAX22017_GEN_SERIAL_MSB_SERIAL_MSB GENMASK(15, 0) + +#define MAX22017_GEN_SERIAL_LSB_OFF 0x02 +#define MAX22017_GEN_SERIAL_LSB_SERIAL_LSB GENMASK(15, 0) + +#define MAX22017_GEN_CNFG_OFF 0x03 +#define MAX22017_GEN_CNFG_OPENWIRE_DTCT_CNFG GENMASK(15, 14) +#define MAX22017_GEN_CNFG_TMOUT_SEL GENMASK(13, 10) +#define MAX22017_GEN_CNFG_TMOUT_CNFG BIT(9) +#define MAX22017_GEN_CNFG_TMOUT_EN BIT(8) +#define MAX22017_GEN_CNFG_THSHDN_CNFG GENMASK(7, 6) +#define MAX22017_GEN_CNFG_OVC_SHDN_CNFG GENMASK(5, 4) +#define MAX22017_GEN_CNFG_OVC_CNFG GENMASK(3, 2) +#define MAX22017_GEN_CNFG_CRC_EN BIT(1) +#define MAX22017_GEN_CNFG_DACREF_SEL BIT(0) + +#define MAX22017_GEN_GPIO_CTRL_OFF 0x04 +#define MAX22017_GEN_GPIO_CTRL_GPIO_EN GENMASK(13, 8) +#define MAX22017_GEN_GPIO_CTRL_GPIO_DIR GENMASK(5, 0) + +#define MAX22017_GEN_GPIO_DATA_OFF 0x05 +#define MAX22017_GEN_GPIO_DATA_GPO_DATA GENMASK(13, 8) +#define MAX22017_GEN_GPIO_DATA_GPI_DATA GENMASK(5, 0) + +#define MAX22017_GEN_GPI_INT_OFF 0x06 +#define MAX22017_GEN_GPI_INT_GPI_POS_EDGE_INT GENMASK(13, 8) +#define MAX22017_GEN_GPI_INT_GPI_NEG_EDGE_INT GENMASK(5, 0) + +#define MAX22017_GEN_GPI_INT_STA_OFF 0x07 +#define MAX22017_GEN_GPI_INT_STA_GPI_POS_EDGE_INT_STA GENMASK(13, 8) +#define MAX22017_GEN_GPI_INT_STA_GPI_NEG_EDGE_INT_STA GENMASK(5, 0) + +#define MAX22017_GEN_INT_OFF 0x08 +#define MAX22017_GEN_INT_FAIL_INT BIT(15) +#define MAX22017_GEN_INT_CONV_OVF_INT GENMASK(13, 12) +#define MAX22017_GEN_INT_OPENWIRE_DTCT_INT GENMASK(11, 10) +#define MAX22017_GEN_INT_HVDD_INT BIT(9) +#define MAX22017_GEN_INT_TMOUT_INT BIT(8) +#define MAX22017_GEN_INT_THSHDN_INT GENMASK(7, 6) +#define MAX22017_GEN_INT_THWRNG_INT GENMASK(5, 4) +#define MAX22017_GEN_INT_OVC_INT GENMASK(3, 2) +#define MAX22017_GEN_INT_CRC_INT BIT(1) +#define MAX22017_GEN_INT_GPI_INT BIT(0) + +#define MAX22017_GEN_INTEN_OFF 0x09 +#define MAX22017_GEN_INTEN_CONV_OVF_INTEN GENMASK(13, 12) +#define MAX22017_GEN_INTEN_OPENWIRE_DTCT_INTEN GENMASK(11, 10) +#define MAX22017_GEN_INTEN_HVDD_INTEN BIT(9) +#define MAX22017_GEN_INTEN_TMOUT_INTEN BIT(8) +#define MAX22017_GEN_INTEN_THSHDN_INTEN GENMASK(7, 6) +#define MAX22017_GEN_INTEN_THWRNG_INTEN GENMASK(5, 4) +#define MAX22017_GEN_INTEN_OVC_INTEN GENMASK(3, 2) +#define MAX22017_GEN_INTEN_CRC_INTEN BIT(1) +#define MAX22017_GEN_INTEN_GPI_INTEN BIT(0) + +#define MAX22017_GEN_RST_CTRL_OFF 0x0A +#define MAX22017_GEN_RST_CTRL_AO_COEFF_RELOAD GENMASK(15, 14) +#define MAX22017_GEN_RST_CTRL_GEN_RST BIT(9) + +#define MAX22017_AO_CMD_OFF 0x40 +#define MAX22017_AO_CMD_AO_LD_CTRL GENMASK(15, 14) + +#define MAX22017_AO_STA_OFF 0x41 +#define MAX22017_AO_STA_BUSY_STA GENMASK(15, 14) +#define MAX22017_AO_STA_SLEW_STA GENMASK(13, 12) +#define MAX22017_AO_STA_FAIL_STA BIT(0) + +#define MAX22017_AO_CNFG_OFF 0x42 +#define MAX22017_AO_CNFG_AO_LD_CNFG GENMASK(15, 14) +#define MAX22017_AO_CNFG_AO_CM_SENSE GENMASK(13, 12) +#define MAX22017_AO_CNFG_AO_UNI GENMASK(11, 10) +#define MAX22017_AO_CNFG_AO_MODE GENMASK(9, 8) +#define MAX22017_AO_CNFG_AO_OPENWIRE_DTCT_LMT GENMASK(5, 4) +#define MAX22017_AO_CNFG_AI_EN GENMASK(3, 2) +#define MAX22017_AO_CNFG_AO_EN GENMASK(1, 0) + +#define MAX22017_AO_SLEW_RATE_CHn_OFF(n) (0x43 + n) +#define MAX22017_AO_SLEW_RATE_CHn_AO_SR_EN_CHn BIT(5) +#define MAX22017_AO_SLEW_RATE_CHn_AO_SR_SEL_CHn BIT(4) +#define MAX22017_AO_SLEW_RATE_CHn_AO_SR_STEP_SIZE_CHn GENMASK(3, 2) +#define MAX22017_AO_SLEW_RATE_CHn_AO_SR_UPDATE_RATE_CHn GENMASK(1, 0) + +#define MAX22017_AO_DATA_CHn_OFF(n) (0x45 + n) +#define MAX22017_AO_DATA_CHn_AO_DATA_CH GENMASK(15, 0) + +#define MAX22017_AO_OFFSET_CORR_CHn_OFF(n) (0x47 + (2 * n)) +#define MAX22017_AO_OFFSET_CORR_CHn_AO_OFFSET_CH GENMASK(15, 0) + +#define MAX22017_AO_GAIN_CORR_CHn_OFF(n) (0x48 + (2 * n)) +#define MAX22017_AO_GAIN_CORR_CHn_AO_GAIN_CH GENMASK(15, 0) + +#define MAX22017_SPI_TRANS_ADDR GENMASK(7, 1) +#define MAX22017_SPI_TRANS_DIR BIT(0) +#define MAX22017_SPI_TRANS_PAYLOAD GENMASK(15, 0) + +/** + * @defgroup mdf_interface_max22017 MFD MAX22017 interface + * @ingroup mfd_interfaces + * @{ + */ + +/** + * @brief Read register from max22017 + * + * @param dev max22017 mfd device + * @param addr register address to read from + * @param value pointer to buffer for received data + * @retval 0 If successful + * @retval -errno In case of any error (see spi_transceive_dt()) + */ +int max22017_reg_read(const struct device *dev, uint8_t addr, uint16_t *value); + +/** + * @brief Write register to max22017 + * + * @param dev max22017 mfd device + * @param addr register address to write to + * @param value content to write + * @retval 0 If successful + * @retval -errno In case of any error (see spi_write_dt()) + */ +int max22017_reg_write(const struct device *dev, uint8_t addr, uint16_t value); + +/** + * @} + */ + +struct max22017_data { + const struct device *dev; + struct k_mutex lock; + struct k_work int_work; + struct gpio_callback callback_int; + bool crc_enabled; +#ifdef CONFIG_GPIO_MAX22017 + sys_slist_t callbacks_gpi; +#endif +}; + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_MAX22017_H_ */ diff --git a/tests/drivers/build_all/dac/app.overlay b/tests/drivers/build_all/dac/app.overlay index c3c9fec38ed3c..e9f0777190d15 100644 --- a/tests/drivers/build_all/dac/app.overlay +++ b/tests/drivers/build_all/dac/app.overlay @@ -124,6 +124,7 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, + <&test_gpio 0 0>, <&test_gpio 0 0>; test_spi_dac60508: dac60508@0 { @@ -288,6 +289,19 @@ #io-channel-cells = <1>; }; }; + + test_spi_max22017_mfd: max22017_mfd@11 { + compatible = "adi,max22017"; + status = "okay"; + spi-max-frequency = <1000000>; + reg = <0x11>; + + test_spi_max22017_dac0: dac-controller { + compatible = "adi,max22017-dac"; + #io-channel-cells = <2>; + status = "okay"; + }; + }; }; }; }; diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index b91babee9104a..229a6092eae7a 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -440,6 +440,7 @@ &test_gpio 0 0 &test_gpio 0 0 &test_gpio 0 0 + &test_gpio 0 0 &test_gpio 0 0>; test_spi_mcp23s17: mcp23s17@0 { @@ -594,6 +595,21 @@ fault-gpios = <&test_gpio 0 0>; latch-gpios = <&test_gpio 0 0>; }; + + test_spi_max22017: max22017_mfd@b { + compatible = "adi,max22017"; + status = "okay"; + reg = <0x0b>; + spi-max-frequency = <0>; + + max22017_gpio0: max22017_gpio { + compatible = "adi,max22017-gpio"; + status = "okay"; + gpio-controller; + ngpios = <5>; + #gpio-cells = <2>; + }; + }; }; }; };