diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt index c74f798251866..75c5b69b4ba15 100644 --- a/drivers/mfd/CMakeLists.txt +++ b/drivers/mfd/CMakeLists.txt @@ -28,3 +28,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_MCHP_SAM_FLEXCOM mfd_mchp_sam_flexcom.c) zephyr_library_sources_ifdef(CONFIG_MFD_PF1550 mfd_pf1550.c) zephyr_library_sources_ifdef(CONFIG_MFD_PCA9422 mfd_pca9422.c) zephyr_library_sources_ifdef(CONFIG_MFD_MOTOROLA_MC146818 mfd_mc146818.c) +zephyr_library_sources_ifdef(CONFIG_MFD_MAX2221X mfd_max2221x.c) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1a53046ae1477..1a9b4716bc5c4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -27,6 +27,7 @@ source "drivers/mfd/Kconfig.bd8lb600fs" source "drivers/mfd/Kconfig.ds3231" source "drivers/mfd/Kconfig.max20335" source "drivers/mfd/Kconfig.max22017" +source "drivers/mfd/Kconfig.max2221x" source "drivers/mfd/Kconfig.max31790" source "drivers/mfd/Kconfig.maxq10xx" source "drivers/mfd/Kconfig.mchp_sam" diff --git a/drivers/mfd/Kconfig.max2221x b/drivers/mfd/Kconfig.max2221x new file mode 100644 index 0000000000000..f59dcbc99a27a --- /dev/null +++ b/drivers/mfd/Kconfig.max2221x @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +config MFD_MAX2221X + bool "Analog Devices MAX2221X multi-function device driver" + default y + depends on SPI + depends on DT_HAS_ADI_MAX2221X_ENABLED + help + Enable the driver for the Analog Devices MAX2221X multi-function device driver diff --git a/drivers/mfd/mfd_max2221x.c b/drivers/mfd/mfd_max2221x.c new file mode 100644 index 0000000000000..32b6e05bb2713 --- /dev/null +++ b/drivers/mfd/mfd_max2221x.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max2221x + +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(MFD_MAX2221X, CONFIG_MFD_LOG_LEVEL); + +struct mfd_max2221x_config { + struct spi_dt_spec spi; +}; + +int max2221x_reg_read(const struct device *dev, uint8_t addr, uint16_t *value) +{ + int ret; + const struct mfd_max2221x_config *config = dev->config; + uint8_t rxbuffer[3] = {0}; + size_t rx_len = 3; + + *value = 0; + addr = FIELD_PREP(MAX2221X_SPI_TRANS_ADDR, addr) | FIELD_PREP(MAX2221X_SPI_TRANS_DIR, 0); + + const struct spi_buf txb[] = { + { + .buf = &addr, + .len = 1, + }, + }; + + const struct spi_buf rxb[] = { + { + .buf = rxbuffer, + .len = rx_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; + } + + memset(rxbuffer, 0, sizeof(rxbuffer)); + + ret = spi_transceive_dt(&config->spi, &tx, &rx); + if (ret) { + return ret; + } + + *value = sys_be16_to_cpu(*(uint16_t *)&rxbuffer[1]); + return 0; +} + +int max2221x_reg_write(const struct device *dev, uint8_t addr, uint16_t value) +{ + const struct mfd_max2221x_config *config = dev->config; + + addr = FIELD_PREP(MAX2221X_SPI_TRANS_ADDR, addr) | FIELD_PREP(MAX2221X_SPI_TRANS_DIR, 1); + value = sys_cpu_to_be16(value); + + const struct spi_buf buf[] = { + { + .buf = &addr, + .len = 1, + }, + { + .buf = &value, + .len = 2, + }, + }; + + struct spi_buf_set tx = { + .buffers = buf, + .count = ARRAY_SIZE(buf), + }; + + return spi_write_dt(&config->spi, &tx); +} + +int max2221x_reg_update(const struct device *dev, uint8_t addr, uint16_t mask, uint16_t val) +{ + uint16_t tmp; + int ret; + + ret = max2221x_reg_read(dev, addr, &tmp); + if (ret) { + return ret; + } + + tmp = (tmp & ~mask) | FIELD_PREP(mask, val); + + return max2221x_reg_write(dev, addr, tmp); +} + +static int max2221x_init(const struct device *dev) +{ + const struct mfd_max2221x_config *config = dev->config; + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI device %s not ready", config->spi.bus->name); + return -ENODEV; + } + + return 0; +} + +#define MAX2221X_DEFINE(inst) \ + static const struct mfd_max2221x_config mfd_max2221x_config_##inst = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 0), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, max2221x_init, NULL, NULL, &mfd_max2221x_config_##inst, \ + POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(MAX2221X_DEFINE) diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt index b1bdeedfc5a5e..1cbf57f3d62f0 100644 --- a/drivers/misc/CMakeLists.txt +++ b/drivers/misc/CMakeLists.txt @@ -15,5 +15,6 @@ add_subdirectory_ifdef(CONFIG_RENESAS_RX_DTC renesas_rx_dtc) add_subdirectory_ifdef(CONFIG_RENESAS_RX_EXTERNAL_INTERRUPT renesas_rx_external_interrupt) add_subdirectory_ifdef(CONFIG_NXP_RTXXX_DSP_CTRL nxp_rtxxx_dsp_ctrl) add_subdirectory_ifdef(CONFIG_STM32N6_AXISRAM stm32n6_axisram) +add_subdirectory_ifdef(CONFIG_MISC_MAX2221X max2221x) add_subdirectory(interconn) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 27865549052f6..53b7bd73bd22e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -22,5 +22,6 @@ source "drivers/misc/renesas_rx_external_interrupt/Kconfig" source "drivers/misc/nxp_rtxxx_dsp_ctrl/Kconfig" source "drivers/misc/stm32n6_axisram/Kconfig" source "drivers/misc/nxp_inputmux/Kconfig" +source "drivers/misc/max2221x/Kconfig" endmenu diff --git a/drivers/misc/max2221x/CMakeLists.txt b/drivers/misc/max2221x/CMakeLists.txt new file mode 100644 index 0000000000000..cf3f768ac8034 --- /dev/null +++ b/drivers/misc/max2221x/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(max2221x.c) diff --git a/drivers/misc/max2221x/Kconfig b/drivers/misc/max2221x/Kconfig new file mode 100644 index 0000000000000..b00077de38e73 --- /dev/null +++ b/drivers/misc/max2221x/Kconfig @@ -0,0 +1,24 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +config MISC_MAX2221X + bool "Analog Devices MAX22216/MAX22217 Misc driver" + default y + depends on DT_HAS_ADI_MAX2221X_MISC_ENABLED + select MFD + help + Enable driver for MAX2221X MISC. + +if MISC_MAX2221X + +config MISC_MAX2221X_INIT_PRIORITY + int "Init priority" + default 90 + help + Analog Devices MAX22216/MAX22217 Misc driver initialization priority. + +module = MISC_MAX2221X +module-str = misc_max2221x +source "subsys/logging/Kconfig.template.log_config" + +endif # MISC_MAX2221X diff --git a/drivers/misc/max2221x/max2221x.c b/drivers/misc/max2221x/max2221x.c new file mode 100644 index 0000000000000..530c1a5ecd464 --- /dev/null +++ b/drivers/misc/max2221x/max2221x.c @@ -0,0 +1,1151 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max2221x_misc + +#include +#include +#include +#include + +struct misc_max2221x_data { + uint16_t on_time_us[MAX2221X_NUM_CHANNELS]; + uint16_t off_time_us[MAX2221X_NUM_CHANNELS]; + bool stop_state[MAX2221X_NUM_CHANNELS]; + uint16_t repetitions[MAX2221X_NUM_CHANNELS]; +}; + +struct misc_max2221x_config { + const struct device *parent; +}; + +LOG_MODULE_REGISTER(max2221x, CONFIG_MISC_MAX2221X_LOG_LEVEL); + +int max2221x_set_master_chop_freq(const struct device *dev, enum max2221x_master_chop_freq freq) +{ + const struct misc_max2221x_config *config = dev->config; + + if (freq >= MAX2221X_FREQ_INVALID) { + LOG_ERR("Invalid master chopping frequency"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CTRL, MAX2221X_F_PWM_M_MASK, + freq); +} + +int max2221x_get_master_chop_freq(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_GLOBAL_CTRL, ®); + if (ret) { + return ret; + } + + switch (FIELD_GET(MAX2221X_F_PWM_M_MASK, reg)) { + case MAX2221X_FREQ_100KHZ: + return 100000; + case MAX2221X_FREQ_80KHZ: + return 80000; + case MAX2221X_FREQ_60KHZ: + return 60000; + case MAX2221X_FREQ_50KHZ: + return 50000; + case MAX2221X_FREQ_40KHZ: + return 40000; + case MAX2221X_FREQ_30KHZ: + return 30000; + case MAX2221X_FREQ_25KHZ: + return 25000; + case MAX2221X_FREQ_20KHZ: + return 20000; + case MAX2221X_FREQ_15KHZ: + return 15000; + case MAX2221X_FREQ_10KHZ: + return 10000; + case MAX2221X_FREQ_7500HZ: + return 7500; + case MAX2221X_FREQ_5000HZ: + return 5000; + case MAX2221X_FREQ_2500HZ: + return 2500; + default: + LOG_ERR("Unknown master chopping frequency"); + return -EINVAL; + } +} + +int max2221x_get_channel_freq(const struct device *dev, uint8_t channel) +{ + int ret, master_chop_freq; + uint16_t reg, master_freq_divisor; + const struct misc_max2221x_config *config = dev->config; + + master_chop_freq = max2221x_get_master_chop_freq(dev); + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + LOG_ERR("Failed to read register for channel: %u", channel); + return ret; + } + + master_freq_divisor = FIELD_GET(MAX2221X_F_PWM_MASK, reg); + + switch (master_freq_divisor) { + case MAX2221X_FREQ_M: + return master_chop_freq; + case MAX2221X_FREQ_M_2: + return master_chop_freq / 2; + case MAX2221X_FREQ_M_4: + return master_chop_freq / 4; + case MAX2221X_FREQ_M_8: + return master_chop_freq / 8; + default: + LOG_ERR("Unknown channel frequency"); + return -EINVAL; + } +} + +int max2221x_enable_part(const struct device *dev) +{ + const struct misc_max2221x_config *config = dev->config; + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CFG, MAX2221X_ACTIVE_MASK, + 1); +} + +int max2221x_disable_part(const struct device *dev) +{ + const struct misc_max2221x_config *config = dev->config; + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CFG, MAX2221X_ACTIVE_MASK, + 0); +} + +int max2221x_enable_channel(const struct device *dev, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CTRL, + MAX2221X_CNTL0_MASK << channel, 1); +} + +int max2221x_disable_channel(const struct device *dev, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (!dev) { + printk("Error: Device pointer is NULL\n"); + return -EINVAL; + } + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CTRL, + MAX2221X_CNTL0_MASK << channel, 0); +} + +int max2221x_mask_fault_pin(const struct device *dev, enum max2221x_fault_pin_masks mask) +{ + const struct misc_max2221x_config *config = dev->config; + + if (mask >= MAX2221X_FAULT_PIN_INVALID) { + LOG_ERR("Invalid fault pin mask"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CFG, + MAX2221X_M_UVM_MASK << mask, 1); +} + +int max2221x_unmask_fault_pin(const struct device *dev, enum max2221x_fault_pin_masks mask) +{ + const struct misc_max2221x_config *config = dev->config; + + if (mask >= MAX2221X_FAULT_PIN_INVALID) { + LOG_ERR("Invalid fault pin mask"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CFG, + MAX2221X_M_UVM_MASK << mask, 0); +} + +int max2221x_set_vdr_mode(const struct device *dev, enum max2221x_vdr_mode mode) +{ + const struct misc_max2221x_config *config = dev->config; + + if (mode >= MAX2221X_VDR_MODE_INVALID) { + LOG_ERR("Invalid VDR mode"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_GLOBAL_CFG, + MAX2221X_VDRNVDRDUTY_MASK, mode); +} + +int max2221x_get_vdr_mode(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_GLOBAL_CFG, ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_VDRNVDRDUTY_MASK, reg); +} + +int max2221x_read_status(const struct device *dev, uint16_t *status) +{ + int ret; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_GLOBAL_CFG, status); + if (ret) { + return ret; + } + + if (*status & MAX2221X_STATUS_STT3_MASK) { + LOG_DBG("Channel 3: I_AC < I_AC_THLD"); + } else { + LOG_DBG("Channel 3: I_AC > I_AC_THLD"); + } + + if (*status & MAX2221X_STATUS_STT2_MASK) { + LOG_DBG("Channel 2: I_AC < I_AC_THLD"); + } else { + LOG_DBG("Channel 2: I_AC > I_AC_THLD"); + } + + if (*status & MAX2221X_STATUS_STT1_MASK) { + LOG_DBG("Channel 1: I_AC < I_AC_THLD"); + } else { + LOG_DBG("Channel 1: I_AC > I_AC_THLD"); + } + + if (*status & MAX2221X_STATUS_STT0_MASK) { + LOG_DBG("Channel 0: I_AC < I_AC_THLD"); + } else { + LOG_DBG("Channel 0: I_AC > I_AC_THLD"); + } + + if (*status & MAX2221X_STATUS_MIN_T_ON_MASK) { + LOG_DBG("MIN_T_ON not compliant"); + } + + if (*status & MAX2221X_STATUS_RES_MASK) { + LOG_DBG("Measured resistance not compliant"); + } + + if (*status & MAX2221X_STATUS_IND_MASK) { + LOG_DBG("Measured inductance not compliant"); + } + + if (*status & MAX2221X_STATUS_OVT_MASK) { + LOG_DBG("Over-temperature detected"); + } + + if (*status & MAX2221X_STATUS_OCP_MASK) { + LOG_DBG("Over-current detected"); + } + + if (*status & MAX2221X_STATUS_OLF_MASK) { + LOG_DBG("Open-loop detected"); + } + + if (*status & MAX2221X_STATUS_HHF_MASK) { + LOG_DBG("Hit current not reached"); + } + + if (*status & MAX2221X_STATUS_DPM_MASK) { + LOG_DBG("Plunger did not move"); + } + + if (*status & MAX2221X_STATUS_COMER_MASK) { + LOG_DBG("Communication error detected"); + } + + if (*status & MAX2221X_STATUS_UVM_MASK) { + LOG_DBG("Under-voltage detected"); + } + + return 0; +} + +int max2221x_read_vm_monitor(const struct device *dev, uint16_t *vm_monitor) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_VM_MONITOR, ®); + if (ret) { + return ret; + } + + *vm_monitor = FIELD_GET(MAX2221X_VM_MONITOR_MASK, reg); + + return 0; +} + +int max2221x_set_dc_h2l(const struct device *dev, uint16_t dc_hdl) +{ + const struct misc_max2221x_config *config = dev->config; + + return max2221x_reg_write(config->parent, MAX2221X_REG_DC_H2L, dc_hdl); +} + +int max2221x_get_dc_h2l(const struct device *dev, uint16_t *dc_hdl) +{ + const struct misc_max2221x_config *config = dev->config; + + return max2221x_reg_read(config->parent, MAX2221X_REG_DC_H2L, dc_hdl); +} + +int max2221x_set_vm_upper_threshold(const struct device *dev, enum max2221x_vm_threshold threshold) +{ + const struct misc_max2221x_config *config = dev->config; + + if (threshold >= MAX2221X_VM_THRESHOLD_INVALID) { + LOG_ERR("Invalid upper threshold"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_VM_THRESHOLD, + MAX2221X_VM_THLD_UP_MASK, threshold); +} + +int max2221x_get_vm_upper_threshold(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_VM_THRESHOLD, ®); + if (ret) { + return ret; + } + + switch (FIELD_GET(MAX2221X_VM_THLD_UP_MASK, reg)) { + case MAX2221X_VM_THRESHOLD_DISABLED: + return 0; + case MAX2221X_VM_THRESHOLD_4500MV: + return 4500; + case MAX2221X_VM_THRESHOLD_6500MV: + return 6500; + case MAX2221X_VM_THRESHOLD_8500MV: + return 8500; + case MAX2221X_VM_THRESHOLD_10500MV: + return 10500; + case MAX2221X_VM_THRESHOLD_12500MV: + return 12500; + case MAX2221X_VM_THRESHOLD_14500MV: + return 14500; + case MAX2221X_VM_THRESHOLD_16500MV: + return 16500; + case MAX2221X_VM_THRESHOLD_18500MV: + return 18500; + case MAX2221X_VM_THRESHOLD_20500MV: + return 20500; + case MAX2221X_VM_THRESHOLD_22500MV: + return 22500; + case MAX2221X_VM_THRESHOLD_24500MV: + return 24500; + case MAX2221X_VM_THRESHOLD_26500MV: + return 26500; + case MAX2221X_VM_THRESHOLD_28500MV: + return 28500; + case MAX2221X_VM_THRESHOLD_30500MV: + return 30500; + case MAX2221X_VM_THRESHOLD_32500MV: + return 32500; + default: + LOG_ERR("Unknown VM upper threshold"); + return -EINVAL; + } +} + +int max2221x_set_vm_lower_threshold(const struct device *dev, enum max2221x_vm_threshold threshold) +{ + const struct misc_max2221x_config *config = dev->config; + + if (threshold >= MAX2221X_VM_THRESHOLD_INVALID) { + LOG_ERR("Invalid lower threshold"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_VM_THRESHOLD, + MAX2221X_VM_THLD_DOWN_MASK, threshold); +} + +int max2221x_get_vm_lower_threshold(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_VM_THRESHOLD, ®); + if (ret) { + return ret; + } + + switch (FIELD_GET(MAX2221X_VM_THLD_DOWN_MASK, reg)) { + case MAX2221X_VM_THRESHOLD_DISABLED: + return 0; + case MAX2221X_VM_THRESHOLD_4500MV: + return 4500; + case MAX2221X_VM_THRESHOLD_6500MV: + return 6500; + case MAX2221X_VM_THRESHOLD_8500MV: + return 8500; + case MAX2221X_VM_THRESHOLD_10500MV: + return 10500; + case MAX2221X_VM_THRESHOLD_12500MV: + return 12500; + case MAX2221X_VM_THRESHOLD_14500MV: + return 14500; + case MAX2221X_VM_THRESHOLD_16500MV: + return 16500; + case MAX2221X_VM_THRESHOLD_18500MV: + return 18500; + case MAX2221X_VM_THRESHOLD_20500MV: + return 20500; + case MAX2221X_VM_THRESHOLD_22500MV: + return 22500; + case MAX2221X_VM_THRESHOLD_24500MV: + return 24500; + case MAX2221X_VM_THRESHOLD_26500MV: + return 26500; + case MAX2221X_VM_THRESHOLD_28500MV: + return 28500; + case MAX2221X_VM_THRESHOLD_30500MV: + return 30500; + case MAX2221X_VM_THRESHOLD_32500MV: + return 32500; + default: + LOG_ERR("Unknown VM lower threshold"); + return -EINVAL; + } +} + +int max2221x_read_dc_l2h(const struct device *dev, uint16_t *dc_l2h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_read(config->parent, MAX2221X_REG_CFG_DC_L2H(channel), dc_l2h); +} + +int max2221x_write_dc_l2h(const struct device *dev, uint16_t dc_l2h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_write(config->parent, MAX2221X_REG_CFG_DC_L2H(channel), dc_l2h); +} + +int max2221x_read_dc_h(const struct device *dev, uint16_t *dc_h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_read(config->parent, MAX2221X_REG_CFG_DC_H(channel), dc_h); +} + +int max2221x_write_dc_h(const struct device *dev, uint16_t dc_h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_write(config->parent, MAX2221X_REG_CFG_DC_H(channel), dc_h); +} + +int max2221x_read_dc_l(const struct device *dev, uint16_t *dc_l, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_read(config->parent, MAX2221X_REG_CFG_DC_L(channel), dc_l); +} + +int max2221x_write_dc_l(const struct device *dev, uint16_t dc_l, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_write(config->parent, MAX2221X_REG_CFG_DC_L(channel), dc_l); +} + +int max2221x_read_time_l2h(const struct device *dev, uint16_t *time_l2h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_read(config->parent, MAX2221X_REG_CFG_TIME_L2H(channel), time_l2h); +} + +int max2221x_write_time_l2h(const struct device *dev, uint16_t time_l2h, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_write(config->parent, MAX2221X_REG_CFG_TIME_L2H(channel), time_l2h); +} + +int max2221x_set_ctrl_mode(const struct device *dev, enum max2221x_ctrl_mode mode, uint8_t channel) +{ + const struct misc_max2221x_config *config = dev->config; + + if (mode >= MAX2221X_CTRL_MODE_INVALID) { + switch (channel) { + case 0: + LOG_ERR("Ch 0: Invalid control mode"); + break; + case 1: + LOG_ERR("Ch 1: Invalid control mode"); + break; + case 2: + LOG_ERR("Ch 2: Invalid control mode"); + break; + case 3: + LOG_ERR("Ch 3: Invalid control mode"); + break; + default: + LOG_ERR("Invalid channel"); + break; + } + + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_CTRL_MODE_MASK, mode); +} + +int max2221x_get_ctrl_mode(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL0(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_CTRL_MODE_MASK, reg); +} + +int max2221x_enable_ramps(const struct device *dev, uint8_t channel, uint8_t ramp_mask) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (ramp_mask & MAX2221X_RAMP_DOWN_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RDWE_MASK, 1); + } + + if (ramp_mask & MAX2221X_RAMP_MID_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RMDE_MASK, 1); + } + + if (ramp_mask & MAX2221X_RAMP_UP_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RUPE_MASK, 1); + } + + return 0; +} + +int max2221x_disable_ramps(const struct device *dev, uint8_t channel, uint8_t ramp_mask) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (ramp_mask & MAX2221X_RAMP_DOWN_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RDWE_MASK, 0); + } + + if (ramp_mask & MAX2221X_RAMP_MID_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RMDE_MASK, 0); + } + + if (ramp_mask & MAX2221X_RAMP_UP_MASK) { + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RUPE_MASK, 0); + } + + return 0; +} + +int max2221x_set_ramp_slew_rate(const struct device *dev, uint8_t channel, uint8_t ramp_slew_rate) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL0(channel), + MAX2221X_RAMP_MASK, ramp_slew_rate); +} + +int max2221x_get_ramp_slew_rate(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL0(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_RAMP_MASK, reg); +} + +int max2221x_set_channel_chop_freq_div(const struct device *dev, uint8_t channel, + enum max2221x_ch_freq_div freq_div) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (freq_div >= MAX2221X_CH_FREQ_DIV_INVALID) { + LOG_ERR("Invalid chopping frequency divider"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL1(channel), + MAX2221X_F_PWM_MASK, freq_div); +} + +int max2221x_get_channel_chop_freq_div(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_F_PWM_MASK, reg); +} + +int max2221x_set_slew_rate(const struct device *dev, uint8_t channel, + enum max2221x_slew_rate slew_rate) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (slew_rate >= MAX2221X_SLEW_RATE_INVALID) { + LOG_ERR("Invalid slew rate"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL1(channel), + MAX2221X_SLEW_RATE_MASK, slew_rate); +} + +int max2221x_get_slew_rate(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_SLEW_RATE_MASK, reg); +} + +int max2221x_set_gain(const struct device *dev, uint8_t channel, enum max2221x_gain gain) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (gain >= MAX2221X_GAIN_INVALID) { + LOG_ERR("Invalid gain"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL1(channel), + MAX2221X_GAIN_MASK, gain); +} + +int max2221x_get_gain(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_GAIN_MASK, reg); +} + +int max2221x_set_snsf(const struct device *dev, uint8_t channel, enum max2221x_snsf snsf) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if (snsf >= MAX2221X_SNSF_INVALID) { + LOG_ERR("Invalid SNSF"); + return -EINVAL; + } + + return max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL1(channel), + MAX2221X_SNSF_MASK, snsf); +} + +int max2221x_get_snsf(const struct device *dev, uint8_t channel) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + return ret; + } + + return FIELD_GET(MAX2221X_SNSF_MASK, reg); +} + +int max2221x_read_pwm_dutycycle(const struct device *dev, uint8_t channel, uint16_t *duty_cycle) +{ + const struct misc_max2221x_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_reg_read(config->parent, MAX2221X_REG_PWM_DUTY(channel), duty_cycle); +} + +int max2221x_read_fault0(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_FAULT0, ®); + if (ret) { + return ret; + } + + if (reg & MAX2221X_FAULT_DPM3_MASK) { + LOG_DBG("Channel 3: Plunger did not move"); + } + + if (reg & MAX2221X_FAULT_DPM2_MASK) { + LOG_DBG("Channel 2: Plunger did not move"); + } + + if (reg & MAX2221X_FAULT_DPM1_MASK) { + LOG_DBG("Channel 1: Plunger did not move"); + } + + if (reg & MAX2221X_FAULT_DPM0_MASK) { + LOG_DBG("Channel 0: Plunger did not move"); + } + + if (reg & MAX2221X_FAULT_OLF3_MASK) { + LOG_DBG("Channel 3: Open-loop detected"); + } + + if (reg & MAX2221X_FAULT_OLF2_MASK) { + LOG_DBG("Channel 2: Open-loop detected"); + } + + if (reg & MAX2221X_FAULT_OLF1_MASK) { + LOG_DBG("Channel 1: Open-loop detected"); + } + + if (reg & MAX2221X_FAULT_OLF0_MASK) { + LOG_DBG("Channel 0: Open-loop detected"); + } + + if (reg & MAX2221X_FAULT_HHF3_MASK) { + LOG_DBG("Channel 3: Hit current not reached"); + } + + if (reg & MAX2221X_FAULT_HHF2_MASK) { + LOG_DBG("Channel 2: Hit current not reached"); + } + + if (reg & MAX2221X_FAULT_HHF1_MASK) { + LOG_DBG("Channel 1: Hit current not reached"); + } + + if (reg & MAX2221X_FAULT_HHF0_MASK) { + LOG_DBG("Channel 0: Hit current not reached"); + } + + if (reg & MAX2221X_FAULT_OCP3_MASK) { + LOG_DBG("Channel 3: Over-current detected"); + } + + if (reg & MAX2221X_FAULT_OCP2_MASK) { + LOG_DBG("Channel 2: Over-current detected"); + } + + if (reg & MAX2221X_FAULT_OCP1_MASK) { + LOG_DBG("Channel 1: Over-current detected"); + } + + if (reg & MAX2221X_FAULT_OCP0_MASK) { + LOG_DBG("Channel 0: Over-current detected"); + } + + return 0; +} + +int max2221x_read_fault1(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct misc_max2221x_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_FAULT1, ®); + if (ret) { + return ret; + } + + if (reg & MAX2221X_FAULT_RES3_MASK) { + LOG_DBG("Channel 3: Measured resistance not compliant"); + } + + if (reg & MAX2221X_FAULT_RES2_MASK) { + LOG_DBG("Channel 2: Measured resistance not compliant"); + } + + if (reg & MAX2221X_FAULT_RES1_MASK) { + LOG_DBG("Channel 1: Measured resistance not compliant"); + } + + if (reg & MAX2221X_FAULT_IND3_MASK) { + LOG_DBG("Channel 3: Measured inductance not compliant"); + } + + if (reg & MAX2221X_FAULT_OVT_MASK) { + LOG_DBG("Over-temperature detected"); + } + + if (reg & MAX2221X_FAULT_COMER_MASK) { + LOG_DBG("Communication error detected"); + } + + if (reg & MAX2221X_FAULT_UVM_MASK) { + LOG_DBG("Under-voltage detected"); + } + + if (reg & MAX2221X_FAULT_IND3_MASK) { + LOG_DBG("Channel 3: Measured inductance not compliant"); + } + + if (reg & MAX2221X_FAULT_IND2_MASK) { + LOG_DBG("Channel 2: Measured inductance not compliant"); + } + + if (reg & MAX2221X_FAULT_IND1_MASK) { + LOG_DBG("Channel 1: Measured inductance not compliant"); + } + + if (reg & MAX2221X_FAULT_IND0_MASK) { + LOG_DBG("Channel 0: Measured inductance not compliant"); + } + + return 0; +} + +int max2221x_set_on_time(const struct device *dev, uint8_t channel, uint16_t on_time_us) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + data->on_time_us[channel] = on_time_us; + + return 0; +} + +int max2221x_get_on_time(const struct device *dev, uint8_t channel) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return data->on_time_us[channel]; +} + +int max2221x_set_off_time(const struct device *dev, uint8_t channel, uint16_t off_time_us) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + data->off_time_us[channel] = off_time_us; + + return 0; +} + +int max2221x_get_off_time(const struct device *dev, uint8_t channel) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return data->off_time_us[channel]; +} + +int max2221x_set_stop_state(const struct device *dev, uint8_t channel, bool stop_state) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + data->stop_state[channel] = stop_state; + + return 0; +} + +int max2221x_get_stop_state(const struct device *dev, uint8_t channel) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return data->stop_state[channel]; +} + +int max2221x_set_repetitions(const struct device *dev, uint8_t channel, uint16_t repetitions) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + data->repetitions[channel] = repetitions; + + return 0; +} + +int max2221x_get_repetitions(const struct device *dev, uint8_t channel) +{ + struct misc_max2221x_data *data = dev->data; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return data->repetitions[channel]; +} + +int max2221x_start_rapid_fire(const struct device *dev, uint8_t channel) +{ + uint16_t mask; + int ret, i; + struct misc_max2221x_data *data = dev->data; + + mask = MAX2221X_CNTL0_MASK << channel; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + for (i = 0; i < data->repetitions[channel]; i++) { + ret = max2221x_enable_channel(dev, channel); + if (ret) { + return ret; + } + + k_usleep(data->on_time_us[channel]); + + ret = max2221x_disable_channel(dev, channel); + if (ret) { + return ret; + } + + k_usleep(data->off_time_us[channel]); + } + + if (!data->stop_state[channel]) { + return max2221x_enable_channel(dev, channel); + } + + return 0; +} + +int max2221x_stop_rapid_fire(const struct device *dev, uint8_t channel) +{ + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + return max2221x_disable_channel(dev, channel); +} + +static int misc_max2221x_init(const struct device *dev) +{ + const struct misc_max2221x_config *config = dev->config; + + LOG_DBG("Initialize MAX2221X Misc instance %s", dev->name); + + if (!device_is_ready(config->parent)) { + LOG_ERR("Parent device '%s' not ready", config->parent->name); + return -EINVAL; + } + + LOG_DBG("MAX2221X Misc Initialized"); + + return 0; +} + +#define MISC_MAX2221X_DEFINE(inst) \ + static const struct misc_max2221x_config misc_max2221x_config_##inst = { \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, misc_max2221x_init, NULL, NULL, &misc_max2221x_config_##inst, \ + POST_KERNEL, CONFIG_MISC_MAX2221X_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(MISC_MAX2221X_DEFINE); diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index 3497f51688ec4..4ec35bcb060b8 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -69,3 +69,4 @@ zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c) zephyr_library_sources_ifdef(CONFIG_PWM_CAPTURE pwm_capture.c) zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c) zephyr_library_sources_ifdef(CONFIG_PWM_REALTEK_RTS5912 pwm_realtek_rts5912.c) +zephyr_library_sources_ifdef(CONFIG_PWM_MAX2221X pwm_max2221x.c) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3b9911b2de94e..31528a186d58e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -142,4 +142,6 @@ source "drivers/pwm/Kconfig.wch" source "drivers/pwm/Kconfig.ambiq_timer" +source "drivers/pwm/Kconfig.max2221x" + endif # PWM diff --git a/drivers/pwm/Kconfig.max2221x b/drivers/pwm/Kconfig.max2221x new file mode 100644 index 0000000000000..efe4b0000e2d6 --- /dev/null +++ b/drivers/pwm/Kconfig.max2221x @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PWM_MAX2221X + bool "Analog Devices MAX2221X multi-function device driver" + default y + depends on DT_HAS_ADI_MAX2221X_PWM_ENABLED + depends on MFD + help + Enable driver for MAX2221x PWM + +config PWM_MAX2221X_INIT_PRIORITY + int "Init priority" + default 80 + depends on PWM_MAX2221X + help + Analog Devices MAX22216/MAX22217 PWM driver initialization priority. diff --git a/drivers/pwm/pwm_max2221x.c b/drivers/pwm/pwm_max2221x.c new file mode 100644 index 0000000000000..8c7afcaeb3bc4 --- /dev/null +++ b/drivers/pwm/pwm_max2221x.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max2221x_pwm + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(pwm_max2221x, CONFIG_PWM_LOG_LEVEL); + +struct max2221x_pwm_config { + const struct device *parent; +}; + +int max2221x_get_master_chop_freq(const struct device *dev) +{ + int ret; + uint16_t reg; + const struct max2221x_pwm_config *config = dev->config; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_GLOBAL_CTRL, ®); + if (ret) { + LOG_ERR("Failed to read global control register"); + return ret; + } + + switch (FIELD_GET(MAX2221X_F_PWM_M_MASK, reg)) { + case MAX2221X_FREQ_100KHZ: + return 100000; + case MAX2221X_FREQ_80KHZ: + return 80000; + case MAX2221X_FREQ_60KHZ: + return 60000; + case MAX2221X_FREQ_50KHZ: + return 50000; + case MAX2221X_FREQ_40KHZ: + return 40000; + case MAX2221X_FREQ_30KHZ: + return 30000; + case MAX2221X_FREQ_25KHZ: + return 25000; + case MAX2221X_FREQ_20KHZ: + return 20000; + case MAX2221X_FREQ_15KHZ: + return 15000; + case MAX2221X_FREQ_10KHZ: + return 10000; + case MAX2221X_FREQ_7500HZ: + return 7500; + case MAX2221X_FREQ_5000HZ: + return 5000; + case MAX2221X_FREQ_2500HZ: + return 2500; + default: + LOG_ERR("Unknown master chopping frequency"); + return -EINVAL; + } +} + +int max2221x_get_channel_freq(const struct device *dev, uint32_t channel, uint32_t *channel_freq) +{ + int ret; + uint16_t reg, master_freq_divisor; + uint32_t master_freq; + const struct max2221x_pwm_config *config = dev->config; + + if (channel_freq == NULL) { + LOG_ERR("channel_freq pointer must not be NULL"); + return -EINVAL; + } + + ret = max2221x_get_master_chop_freq(dev); + if (ret < 0) { + LOG_ERR("Failed to get master chop frequency"); + return ret; + } + master_freq = (uint32_t)ret; + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL1(channel), ®); + if (ret) { + LOG_ERR("Failed to read register for channel: %u", channel); + return ret; + } + + master_freq_divisor = FIELD_GET(MAX2221X_F_PWM_MASK, reg); + + switch (master_freq_divisor) { + case MAX2221X_FREQ_M: + *channel_freq = master_freq; + break; + case MAX2221X_FREQ_M_2: + *channel_freq = master_freq / 2; + break; + case MAX2221X_FREQ_M_4: + *channel_freq = master_freq / 4; + break; + case MAX2221X_FREQ_M_8: + *channel_freq = master_freq / 8; + break; + default: + LOG_ERR("Unknown channel frequency"); + return -EINVAL; + } + + return 0; +} + +int max2221x_calculate_duty_cycle(uint32_t pulse, uint32_t period, uint16_t *duty_cycle) +{ + if (period == 0) { + LOG_ERR("Period must be > 0"); + return -EINVAL; + } + + if (pulse > period) { + LOG_ERR("Pulse width cannot be greater than period"); + return -EINVAL; + } + + *duty_cycle = (uint16_t)(((uint64_t)pulse * UINT16_MAX) / period); + + return 0; +} + +int max2221x_calculate_master_freq_divisor(uint32_t master_freq, uint32_t period, int *freq_divisor) +{ + int min_difference; + uint32_t user_freq_hz; + const int divisors[] = {1, 2, 4, 8}; + + if (master_freq == 0) { + LOG_ERR("Invalid input: Master frequency must be > 0"); + return -EINVAL; + } + + if (period == 0) { + LOG_ERR("Invalid input: Pulse width must be > 0"); + return -EINVAL; + } + + user_freq_hz = 1000000 / period; + *freq_divisor = divisors[0]; + min_difference = abs((int)(user_freq_hz - (master_freq / divisors[0]))); + + for (int i = 1; i < ARRAY_SIZE(divisors); i++) { + int current_freq = master_freq / divisors[i]; + int difference = abs((int)(user_freq_hz - current_freq)); + + if (difference < min_difference) { + min_difference = difference; + *freq_divisor = divisors[i]; + } + } + + return 0; +} + +int max2221x_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles) +{ + int ret; + uint32_t channel_freq; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel: %u", channel); + return -EINVAL; + } + + ret = max2221x_get_channel_freq(dev, channel, &channel_freq); + if (ret < 0) { + LOG_ERR("Failed to get channel frequency for channel: %u", channel); + return ret; + } + + *cycles = (uint64_t)channel_freq; + + return 0; +} + +int max2221x_set_cycles(const struct device *dev, uint32_t channel, uint32_t period, uint32_t pulse, + pwm_flags_t flags) +{ + int ret, channel_freq_divisor; + uint16_t global_cfg, vdrnvdrduty, cfg_ctrl0, ctrl_mode; + uint16_t duty_cycle, channel_freq_reg_value; + uint32_t master_freq, channel_freq, valid_period; + uint32_t min_period, max_period; + const struct max2221x_pwm_config *config = dev->config; + + if (channel >= MAX2221X_NUM_CHANNELS) { + LOG_ERR("Invalid channel number: %i", channel); + return -EINVAL; + } + + if (period == 0) { + LOG_ERR("Period must be greater than 0"); + return -EINVAL; + } + + if (pulse > period) { + LOG_ERR("Pulse width cannot be greater than period"); + return -EINVAL; + } + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_GLOBAL_CFG, &global_cfg); + if (ret) { + LOG_ERR("Failed to read global configuration register"); + return ret; + } + + vdrnvdrduty = FIELD_GET(MAX2221X_VDRNVDRDUTY_MASK, global_cfg); + + ret = max2221x_reg_read(config->parent, MAX2221X_REG_CFG_CTRL0(channel), &cfg_ctrl0); + if (ret) { + LOG_ERR("Failed to read control mode register"); + return ret; + } + + ctrl_mode = FIELD_GET(MAX2221X_CTRL_MODE_MASK, cfg_ctrl0); + + ret = max2221x_get_master_chop_freq(dev); + if (ret < 0) { + LOG_ERR("Failed to get master chop frequency"); + return ret; + } + master_freq = (uint32_t)ret; + + min_period = 1000000 / master_freq; + max_period = min_period * 8; + + if (period < min_period || period > max_period) { + LOG_ERR("Period must be between %d and %d microseconds for " + "frequency %d Hz", + min_period, max_period, master_freq); + return -EINVAL; + } + + ret = max2221x_calculate_master_freq_divisor(master_freq, period, &channel_freq_divisor); + + if (ret < 0) { + LOG_ERR("Failed to calculate channel frequency divisor"); + return ret; + } + + channel_freq = master_freq / (uint32_t)channel_freq_divisor; + valid_period = (1.0 / channel_freq) * 1000000; + + switch (channel_freq_divisor) { + case 1: + channel_freq_reg_value = MAX2221X_FREQ_M; + break; + case 2: + channel_freq_reg_value = MAX2221X_FREQ_M_2; + break; + case 4: + channel_freq_reg_value = MAX2221X_FREQ_M_4; + break; + case 8: + channel_freq_reg_value = MAX2221X_FREQ_M_8; + break; + default: + LOG_ERR("Invalid channel frequency divisor: %d", channel_freq_divisor); + return -EINVAL; + } + + ret = max2221x_calculate_duty_cycle(pulse, valid_period, &duty_cycle); + if (ret < 0) { + LOG_ERR("Failed to calculate duty cycle"); + return ret; + } + + if (vdrnvdrduty == 0) { + ret = max2221x_reg_update(config->parent, MAX2221X_REG_CFG_CTRL1(channel), + MAX2221X_F_PWM_MASK, channel_freq_reg_value); + if (ret) { + LOG_ERR("Failed to write channel frequency for channel %d", channel); + return ret; + } + + if (ctrl_mode == 0 || ctrl_mode == 2) { + ret = max2221x_reg_write(config->parent, MAX2221X_REG_CFG_DC_H(channel), + duty_cycle); + if (ret) { + LOG_ERR("Failed to write DC_H for channel %d", channel); + return ret; + } + } else { + LOG_ERR("Cannot set duty cycle in control mode %d for channel %d", + ctrl_mode, channel); + } + } + + return 0; +} +static DEVICE_API(pwm, max2221x_pwm_api) = { + .set_cycles = max2221x_set_cycles, + .get_cycles_per_sec = max2221x_get_cycles_per_sec, +}; + +static int max2221x_pwm_init(const struct device *dev) +{ + const struct max2221x_pwm_config *config = dev->config; + + LOG_DBG("Initialize MAX2221X PWM instance %s", dev->name); + + if (!device_is_ready(config->parent)) { + LOG_ERR("Parent device '%s' not ready", config->parent->name); + return -ENODEV; + } + + return 0; +} + +#define PWM_MAX2221X_DEFINE(inst) \ + static const struct max2221x_pwm_config max2221x_pwm_config_##inst = { \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, max2221x_pwm_init, NULL, NULL, &max2221x_pwm_config_##inst, \ + POST_KERNEL, CONFIG_PWM_MAX2221X_INIT_PRIORITY, &max2221x_pwm_api); + +DT_INST_FOREACH_STATUS_OKAY(PWM_MAX2221X_DEFINE); diff --git a/dts/bindings/mfd/adi,max2221x.yaml b/dts/bindings/mfd/adi,max2221x.yaml new file mode 100644 index 0000000000000..1b57c9b409dd6 --- /dev/null +++ b/dts/bindings/mfd/adi,max2221x.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +include: spi-device.yaml + +description: Analog Devices MAX2221X multi-function device driver + +compatible: "adi,max2221x" diff --git a/dts/bindings/misc/adi,max2221x-misc.yaml b/dts/bindings/misc/adi,max2221x-misc.yaml new file mode 100644 index 0000000000000..5a77c05c69594 --- /dev/null +++ b/dts/bindings/misc/adi,max2221x-misc.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +include: base.yaml + +description: Analog Devices MAX22216/MAX22217 MISC + +compatible: "adi,max2221x-misc" diff --git a/dts/bindings/pwm/adi,max2221x-pwm.yaml b/dts/bindings/pwm/adi,max2221x-pwm.yaml new file mode 100644 index 0000000000000..512e119f9a67f --- /dev/null +++ b/dts/bindings/pwm/adi,max2221x-pwm.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Analog Devices MAX22216/MAX22217 PWM + +compatible: "adi,max2221x-pwm" + +include: [pwm-controller.yaml, base.yaml] + +properties: + "#pwm-cells": + const: 3 + +pwm-cells: + - channel + - period + - flags diff --git a/include/zephyr/drivers/mfd/max2221x.h b/include/zephyr/drivers/mfd/max2221x.h new file mode 100644 index 0000000000000..08797fe9eeb7b --- /dev/null +++ b/include/zephyr/drivers/mfd/max2221x.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_MAX2221X_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MFD_MAX2221X_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define MAX2221X_SPI_TRANS_ADDR GENMASK(6, 0) +#define MAX2221X_SPI_TRANS_DIR BIT(7) + +#define MAX2221X_REG_GLOBAL_CTRL 0x00 +#define MAX2221X_REG_GLOBAL_CFG 0x01 +#define MAX2221X_REG_STATUS 0x02 +#define MAX2221X_REG_DC_H2L 0x03 +#define MAX2221X_REG_VM_MONITOR 0x05 +#define MAX2221X_REG_VM_THRESHOLD 0x06 +#define MAX2221X_REG_CFG_DC_L2H(x) (0x09 + ((x) * 0xE)) +#define MAX2221X_REG_CFG_DC_H(x) (0x0A + ((x) * 0xE)) +#define MAX2221X_REG_CFG_DC_L(x) (0x0B + ((x) * 0xE)) +#define MAX2221X_REG_CFG_TIME_L2H(x) (0x0C + ((x) * 0xE)) +#define MAX2221X_REG_CFG_CTRL0(x) (0x0D + ((x) * 0xE)) +#define MAX2221X_REG_CFG_CTRL1(x) (0x0E + ((x) * 0xE)) +#define MAX2221X_REG_PWM_DUTY(x) (0x49 + ((x) * 0xE)) +#define MAX2221X_REG_FAULT0 0x65 +#define MAX2221X_REG_FAULT1 0x66 + +#define MAX2221X_F_PWM_M_MASK GENMASK(7, 4) +#define MAX2221X_CNTL0_MASK BIT(0) +#define MAX2221X_CNTL1_MASK BIT(1) +#define MAX2221X_CNTL2_MASK BIT(2) +#define MAX2221X_CNTL3_MASK BIT(3) + +#define MAX2221X_ACTIVE_MASK BIT(15) +#define MAX2221X_M_UVM_MASK BIT(8) +#define MAX2221X_VDRNVDRDUTY_MASK BIT(4) + +#define MAX2221X_STATUS_STT3_MASK BIT(14) +#define MAX2221X_STATUS_STT2_MASK BIT(13) +#define MAX2221X_STATUS_STT1_MASK BIT(12) +#define MAX2221X_STATUS_STT0_MASK BIT(11) +#define MAX2221X_STATUS_MIN_T_ON_MASK BIT(10) +#define MAX2221X_STATUS_RES_MASK BIT(9) +#define MAX2221X_STATUS_IND_MASK BIT(8) +#define MAX2221X_STATUS_OVT_MASK BIT(7) +#define MAX2221X_STATUS_OCP_MASK BIT(6) +#define MAX2221X_STATUS_OLF_MASK BIT(5) +#define MAX2221X_STATUS_HHF_MASK BIT(4) +#define MAX2221X_STATUS_DPM_MASK BIT(3) +#define MAX2221X_STATUS_COMER_MASK BIT(2) +#define MAX2221X_STATUS_UVM_MASK BIT(1) + +#define MAX2221X_VM_MONITOR_MASK GENMASK(12, 0) + +#define MAX2221X_VM_THLD_UP_MASK GENMASK(7, 4) +#define MAX2221X_VM_THLD_DOWN_MASK GENMASK(3, 0) + +#define MAX2221X_CTRL_MODE_MASK GENMASK(15, 14) +#define MAX2221X_RDWE_MASK BIT(10) +#define MAX2221X_RMDE_MASK BIT(9) +#define MAX2221X_RUPE_MASK BIT(8) +#define MAX2221X_RAMP_MASK GENMASK(7, 0) + +#define MAX2221X_F_PWM_MASK GENMASK(9, 8) +#define MAX2221X_SLEW_RATE_MASK GENMASK(5, 4) +#define MAX2221X_GAIN_MASK GENMASK(3, 2) +#define MAX2221X_SNSF_MASK GENMASK(1, 0) + +#define MAX2221X_FAULT_DPM3_MASK BIT(15) +#define MAX2221X_FAULT_DPM2_MASK BIT(14) +#define MAX2221X_FAULT_DPM1_MASK BIT(13) +#define MAX2221X_FAULT_DPM0_MASK BIT(12) +#define MAX2221X_FAULT_OLF3_MASK BIT(11) +#define MAX2221X_FAULT_OLF2_MASK BIT(10) +#define MAX2221X_FAULT_OLF1_MASK BIT(9) +#define MAX2221X_FAULT_OLF0_MASK BIT(8) +#define MAX2221X_FAULT_HHF3_MASK BIT(7) +#define MAX2221X_FAULT_HHF2_MASK BIT(6) +#define MAX2221X_FAULT_HHF1_MASK BIT(5) +#define MAX2221X_FAULT_HHF0_MASK BIT(4) +#define MAX2221X_FAULT_OCP3_MASK BIT(3) +#define MAX2221X_FAULT_OCP2_MASK BIT(2) +#define MAX2221X_FAULT_OCP1_MASK BIT(1) +#define MAX2221X_FAULT_OCP0_MASK BIT(0) + +#define MAX2221X_FAULT_RES3_MASK BIT(10) +#define MAX2221X_FAULT_RES2_MASK BIT(9) +#define MAX2221X_FAULT_RES1_MASK BIT(8) +#define MAX2221X_FAULT_RES0_MASK BIT(7) +#define MAX2221X_FAULT_OVT_MASK BIT(6) +#define MAX2221X_FAULT_COMER_MASK BIT(5) +#define MAX2221X_FAULT_UVM_MASK BIT(4) +#define MAX2221X_FAULT_IND3_MASK BIT(3) +#define MAX2221X_FAULT_IND2_MASK BIT(2) +#define MAX2221X_FAULT_IND1_MASK BIT(1) +#define MAX2221X_FAULT_IND0_MASK BIT(0) + +#define MAX2221X_NUM_CHANNELS 4 + +/** + * @brief Read register from max2221x + * + * @param dev max2221x 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 max2221x_reg_read(const struct device *dev, uint8_t addr, uint16_t *value); + +/** + * @brief Write register to max2221x + * + * @param dev max2221x 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 max2221x_reg_write(const struct device *dev, uint8_t addr, uint16_t value); + +/** + * @brief Update register in max2221x + * + * @param dev max2221x mfd device + * @param addr register address to update + * @param mask mask to apply to the register + * @param val value to write to the register + * @retval 0 If successful + * @retval -errno In case of any error (see spi_transceive_dt()) + */ +int max2221x_reg_update(const struct device *dev, uint8_t addr, uint16_t mask, uint16_t val); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_MAX2221X_H_ */ diff --git a/include/zephyr/drivers/misc/max2221x/max2221x.h b/include/zephyr/drivers/misc/max2221x/max2221x.h new file mode 100644 index 0000000000000..42828fc546057 --- /dev/null +++ b/include/zephyr/drivers/misc/max2221x/max2221x.h @@ -0,0 +1,942 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MISC_MAX2221X_MAX2221X_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MISC_MAX2221X_MAX2221X_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum max2221x_master_chop_freq { + MAX2221X_FREQ_100KHZ = 0, + MAX2221X_FREQ_80KHZ, + MAX2221X_FREQ_60KHZ, + MAX2221X_FREQ_50KHZ, + MAX2221X_FREQ_40KHZ, + MAX2221X_FREQ_30KHZ, + MAX2221X_FREQ_25KHZ, + MAX2221X_FREQ_20KHZ, + MAX2221X_FREQ_15KHZ, + MAX2221X_FREQ_10KHZ, + MAX2221X_FREQ_7500HZ, + MAX2221X_FREQ_5000HZ, + MAX2221X_FREQ_2500HZ, + + MAX2221X_FREQ_INVALID, +}; + +enum max2221x_individual_chop_freq { + MAX2221X_FREQ_M = 0, + MAX2221X_FREQ_M_2, + MAX2221X_FREQ_M_4, + MAX2221X_FREQ_M_8, + + MAX2221X_FREQ_M_INVALID, +}; + +enum max2221x_fault_pin_masks { + /* Under Voltage Monitor fault */ + MAX2221X_FAULT_PIN_UVM = 0, + /* Communication Error fault */ + MAX2221X_FAULT_PIN_COMF, + /* DPM Error fault */ + MAX2221X_FAULT_PIN_DPM, + /* Hit current not reached error fault */ + MAX2221X_FAULT_PIN_HHF, + /* Open-Load Fault */ + MAX2221X_FAULT_PIN_OLF, + /* Over-Current Protection fault */ + MAX2221X_FAULT_PIN_OCP, + /* Over-Temperature Protection fault */ + MAX2221X_FAULT_PIN_OVT, + + MAX2221X_FAULT_PIN_INVALID, +}; + +enum max2221x_vdr_mode { + /* Normal Voltage mode */ + MAX2221X_VDR_MODE_NORMAL = 0, + /* Duty Cycle mode */ + MAX2221X_VDR_MODE_DUTY, + + MAX2221X_VDR_MODE_INVALID, +}; + +enum max2221x_vm_threshold { + MAX2221X_VM_THRESHOLD_DISABLED = 0, + MAX2221X_VM_THRESHOLD_4500MV, + MAX2221X_VM_THRESHOLD_6500MV, + MAX2221X_VM_THRESHOLD_8500MV, + MAX2221X_VM_THRESHOLD_10500MV, + MAX2221X_VM_THRESHOLD_12500MV, + MAX2221X_VM_THRESHOLD_14500MV, + MAX2221X_VM_THRESHOLD_16500MV, + MAX2221X_VM_THRESHOLD_18500MV, + MAX2221X_VM_THRESHOLD_20500MV, + MAX2221X_VM_THRESHOLD_22500MV, + MAX2221X_VM_THRESHOLD_24500MV, + MAX2221X_VM_THRESHOLD_26500MV, + MAX2221X_VM_THRESHOLD_28500MV, + MAX2221X_VM_THRESHOLD_30500MV, + MAX2221X_VM_THRESHOLD_32500MV, + + MAX2221X_VM_THRESHOLD_INVALID, +}; + +enum max2221x_ctrl_mode { + MAX2221X_CTRL_MODE_VOLT = 0, + MAX2221X_CTRL_MODE_CDR, + MAX2221X_CTRL_MODE_LIMITER_VOLT, + MAX2221X_CTRL_MODE_VOLT_CDR, + + MAX2221X_CTRL_MODE_INVALID, +}; + +enum max2221x_ch_freq_div { + MAX2221X_CH_FREQ_DIV_1 = 0, + MAX2221X_CH_FREQ_DIV_2, + MAX2221X_CH_FREQ_DIV_4, + MAX2221X_CH_FREQ_DIV_8, + + MAX2221X_CH_FREQ_DIV_INVALID, +}; + +enum max2221x_slew_rate { + MAX2221X_SLEW_RATE_FAST = 0, + MAX2221X_SLEW_RATE_400V_PER_US, + MAX2221X_SLEW_RATE_200V_PER_US, + MAX2221X_SLEW_RATE_100V_PER_US, + + MAX2221X_SLEW_RATE_INVALID, +}; + +enum max2221x_gain { + MAX2221X_SCALE_1 = 0, + MAX2221X_SCALE_2, + MAX2221X_SCALE_3, + MAX2221X_SCALE_4, + + MAX2221X_GAIN_INVALID, +}; + +enum max2221x_snsf { + MAX2221X_SNSF_FULL_SCALE = 0, + MAX2221X_SNSF_2_3, + MAX2221X_SNSF_1_3, + + MAX2221X_SNSF_INVALID, +}; + +/** + * @name MAX2221x Ramp Enable/Disable Masks + * @{ + */ +#define MAX2221X_RAMP_DOWN_MASK BIT(2) +#define MAX2221X_RAMP_MID_MASK BIT(1) +#define MAX2221X_RAMP_UP_MASK BIT(0) +/** @} */ + +/** + * @brief Set the master chopping frequency for the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set master chop frequency to 50kHz + * int ret = max2221x_set_master_chop_freq(dev, MASTER_CHOP_FREQ_50KHZ); + * if (ret) + * printk("Failed to set master chop frequency: %d\n", ret); + * else + * printk("Master chop frequency set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param freq The desired master chopping frequency to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_master_chop_freq(const struct device *dev, enum max2221x_master_chop_freq freq); + +/** + * @brief Get the master chop frequency of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return The master chop frequency in Hz on success, or a negative error code on failure. + */ +int max2221x_get_master_chop_freq(const struct device *dev); + +/** + * @brief Get the channel frequency of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to read. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_get_channel_freq(const struct device *dev, uint8_t channel); + +/** + * @brief Enable the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_enable_part(const struct device *dev); + +/** + * @brief Disable the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_disable_part(const struct device *dev); + +/** + * @brief Enable a channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to enable. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_enable_channel(const struct device *dev, uint8_t channel); + +/** + * @brief Disable a channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to disable. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_disable_channel(const struct device *dev, uint8_t channel); + +/** + * @brief Mask detection of a fault condition on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Mask fault pin for overcurrent condition + * int ret = max2221x_mask_fault_pin(dev, MAX2221X_FAULT_PIN_OCP); + * if (ret) + * printk("Failed to mask fault pin: %d\n", ret); + * else + * printk("Fault pin masked successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param mask The fault condition to mask. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_mask_fault_pin(const struct device *dev, enum max2221x_fault_pin_masks mask); + +/** + * @brief Unmask detection of a fault condition on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Unmask fault pin for overcurrent condition + * int ret = max2221x_unmask_fault_pin(dev, MAX2221X_FAULT_PIN_OCP); + * if (ret) + * printk("Failed to unmask fault pin: %d\n", ret); + * else + * printk("Fault pin unmasked successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param mask The fault condition to unmask. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_unmask_fault_pin(const struct device *dev, enum max2221x_fault_pin_masks mask); + +/** + * @brief Set the VDR mode of the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set VDR mode to VDRDUTY + * int ret = max2221x_set_vdr_mode(dev, MAX2221X_VDR_MODE_DUTY); + * if (ret) + * printk("Failed to set VDR mode: %d\n", ret); + * else + * printk("VDR mode set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param mode The VDR mode to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_vdr_mode(const struct device *dev, enum max2221x_vdr_mode mode); + +/** + * @brief Get the VDR mode of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return The VDR mode on success, or a negative error code on failure. + */ +int max2221x_get_vdr_mode(const struct device *dev); + +/** + * @brief Read the status of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param status Pointer to the variable to store the status. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_status(const struct device *dev, uint16_t *status); + +/** + * @brief Read the voltage monitor of the MAX2221x device. + * + * VM Measurement is calculated as: + * + * VM = KVM x vm_monitor + * where KVM = 9.73 mV + * + * @param dev Pointer to the device structure for the driver instance. + * @param vm_monitor Pointer to the variable to store the voltage monitor. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_vm_monitor(const struct device *dev, uint16_t *vm_monitor); + +/** + * @brief Set the demagnetization voltage of the MAX2221x device. + * + * If mode is MAX2221X_VDR_MODE_DUTY, dc_hdl represents a voltage. The voltage value is + * calculated as VOUT (V) = KVDR x 36 x dc_hdl. + * If mode is MAX2221X_VDR_MODE_NORMAL, dc_hdl represents a duty cycle. The voltage value is + * calculated as VOUT (V) = KVDR x VM x dc_hdl. + * Here, KVDR = 30.518 uV. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_hdl The desired demagnetization voltage to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_dc_h2l(const struct device *dev, uint16_t dc_hdl); + +/** + * @brief Get the demagnetization voltage of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_hdl Pointer to the variable to store the demagnetization voltage. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_get_dc_h2l(const struct device *dev, uint16_t *dc_hdl); + +/** + * @brief Set the upper threshold of the supply voltage monitor of the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set upper threshold to 3.3V + * int ret = max2221x_set_vm_upper_threshold(dev, MAX2221X_VM_THRESHOLD_3_3V); + * if (ret) + * printk("Failed to set upper threshold: %d\n", ret); + * else + * printk("Upper threshold set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param threshold The desired upper threshold to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_vm_upper_threshold(const struct device *dev, enum max2221x_vm_threshold threshold); + +/** + * @brief Get the upper threshold of the supply voltage monitor of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return The upper threshold in millivolts on success, or a negative error code on failure. + */ +int max2221x_get_vm_upper_threshold(const struct device *dev); + +/** + * @brief Set the lower threshold of the supply voltage monitor of the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set lower threshold to 2.5V + * int ret = max2221x_set_vm_lower_threshold(dev, MAX2221X_VM_THRESHOLD_2_5V); + * if (ret) + * printk("Failed to set lower threshold: %d\n", ret); + * else + * printk("Lower threshold set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param threshold The desired lower threshold to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_vm_lower_threshold(const struct device *dev, enum max2221x_vm_threshold threshold); + +/** + * @brief Get the lower threshold of the supply voltage monitor of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return The lower threshold in millivolts on success, or a negative error code on failure. + */ +int max2221x_get_vm_lower_threshold(const struct device *dev); + +/** + * @brief Read the DC_L2H value of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_l2h Pointer to the variable to store the DC_L2H value. + * @param channel The channel to read the DC_L2H value from. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_dc_l2h(const struct device *dev, uint16_t *dc_l2h, uint8_t channel); + +/** + * @brief Write the DC_L2H value of channel on the MAX2221x device. + * + * Sets the DC_L2H level: + * VDR: VOUT (V) = KVDR x 36 x dc_l2h + * VDRDUTY: VOUT (V) = KVDR x VM x dc_l2h + * CDR: IOUT (mA) = KCDR x GAIN x SNSF x dc_l2h + * where KVDR = 30.518 uV, KCDR = 1.017 mA (MAX22216) or 0.339 mA (MAX22217). + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_l2h The desired DC_L2H value to write. + * @param channel The channel to write the DC_L2H value to. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_write_dc_l2h(const struct device *dev, uint16_t dc_l2h, uint8_t channel); + +/** + * @brief Read the DC_H value of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_h Pointer to the variable to store the DC_H value. + * @param channel The channel to read the DC_H value from. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_dc_h(const struct device *dev, uint16_t *dc_h, uint8_t channel); + +/** + * @brief Write the DC_H value of channel on the MAX2221x device. + * + * Sets the DC_H level: + * VDR: VOUT (V) = KVDR x 36 x dc_h + * VDRDUTY: VOUT (V) = KVDR x VM x dc_h + * CDR: IOUT (mA) = KCDR x GAIN x SNSF x dc_h + * where KVDR = 30.518 uV, KCDR = 1.017 mA (MAX22216) or 0.339 mA (MAX22217). + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_h The desired DC_H value to write. + * @param channel The channel to write the DC_H value to. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_write_dc_h(const struct device *dev, uint16_t dc_h, uint8_t channel); + +/** + * @brief Read the DC_L value of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_l Pointer to the variable to store the DC_L value. + * @param channel The channel to read the DC_L value from. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_dc_l(const struct device *dev, uint16_t *dc_l, uint8_t channel); + +/** + * @brief Write the DC_L value of channel on the MAX2221x device. + * + * Sets the DC_L level: + * VDR: VOUT (V) = KVDR x 36 x dc_l + * VDRDUTY: VOUT (V) = KVDR x VM x dc_l + * CDR: IOUT (mA) = KCDR x GAIN x SNSF x dc_l + * where KVDR = 30.518 uV, KCDR = 1.017 mA (MAX22216) or 0.339 mA (MAX22217). + * + * @param dev Pointer to the device structure for the driver instance. + * @param dc_l The desired DC_L value to write. + * @param channel The channel to write the DC_L value to. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_write_dc_l(const struct device *dev, uint16_t dc_l, uint8_t channel); + +/** + * @brief Read the time L2H value of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param time_l2h Pointer to the variable to store the time L2H value. + * @param channel The channel to read the time L2H value from. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_time_l2h(const struct device *dev, uint16_t *time_l2h, uint8_t channel); + +/** + * @brief Write the time L2H value of channel on the MAX2221x device. + * + * Sets the time L2H level: + * TIME_L2H (s) = time_l2h / channel chopping frequency + * + * @param dev Pointer to the device structure for the driver instance. + * @param time_l2h The desired time L2H value to write. + * @param channel The channel to write the time L2H value to. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_write_time_l2h(const struct device *dev, uint16_t time_l2h, uint8_t channel); + +/** + * @brief Set the control mode of channel on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set control mode for DC motor drive for channel 1 + * int ret = max2221x_set_ctrl_mode(dev, MAX2221X_CTRL_MODE_LIMITER_VOLT, 1); + * if (ret) + * printk("Failed to set control mode: %d\n", ret); + * else + * printk("Control mode set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param mode The desired control mode to set. + * @param channel The channel to set the control mode for. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_ctrl_mode(const struct device *dev, enum max2221x_ctrl_mode mode, uint8_t channel); + +/** + * @brief Get the control mode of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the control mode for. + * + * @return The control mode on success, or a negative error code on failure. + */ +int max2221x_get_ctrl_mode(const struct device *dev, uint8_t channel); + +/** + * @brief Enable a ramp on channel of the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Enable ramp up and ramp down on channel + * uint8_t ramp_mask = MAX2221X_RAMP_UP_MASK | MAX2221X_RAMP_DOWN_MASK; + * + * int ret = max2221x_enable_ramps(dev, channel, ramp_mask); + * if (ret) + * printk("Failed to enable ramp on channel %d: %d\n", channel, ret); + * else + * printk("Ramp enabled on channel %d\n", channel); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to enable the ramp for. + * @param ramp_mask The ramp mask to enable. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_enable_ramps(const struct device *dev, uint8_t channel, uint8_t ramp_mask); + +/** + * @brief Disable a ramp on channel of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to disable the ramp for. + * @param ramp_mask The ramp mask to disable. + * + * Example usage: + * @code + * #include + * + * // Disable ramp up and ramp down on channel + * uint8_t ramp_mask = MAX2221X_RAMP_UP_MASK | MAX2221X_RAMP_DOWN_MASK; + * + * int ret = max2221x_disable_ramps(dev, channel, ramp_mask); + * if (ret) + * printk("Failed to disable ramp on channel %d: %d\n", channel, ret); + * else + * printk("Ramp disabled on channel %d\n", channel); + * @endcode + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_disable_ramps(const struct device *dev, uint8_t channel, uint8_t ramp_mask); + +/** + * @brief Set the ramp slew rate of channel on the MAX2221x device. + * + * Sets the Ramp Slew Rate. + * VDRDUTY: + * Ramp Slew Rate (V/ms) = KVDR x VM x (ramp_slew_rate + 1) x F_PWM (kHz) + * VDR: + * Ramp Slew Rate (V/ms) = KVDR x 36 x (ramp_slew_rate + 1) x F_PWM (kHz) + * CDR: + * Ramp Slew Rate (mA/ms) = KCDR x GAIN x SNSF x (ramp_slew_rate + 1) x F_PWM (kHz) + * + * where KVDR = 30.518 uV, KCDR = 1.017 mA (MAX22216) or 0.339 mA (MAX22217), F_PWM is the channel + * chopping frequency. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the ramp slew rate for. + * @param ramp_slew_rate The desired ramp slew rate to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_ramp_slew_rate(const struct device *dev, uint8_t channel, uint8_t ramp_slew_rate); + +/** + * @brief Get the ramp slew rate of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the ramp slew rate for. + * + * @return The ramp slew rate on success, or a negative error code on failure. + */ +int max2221x_get_ramp_slew_rate(const struct device *dev, uint8_t channel); + +/** + * @brief Set the chopping frequency divider of channel on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set channel chopping frequency divider to 1 + * int ret = max2221x_set_channel_chop_freq_div(dev, channel, MAX2221X_CH_FREQ_DIV_1); + * if (ret) + * printk("Failed to set channel chopping frequency divider: %d\n", ret); + * else + * printk("Channel chopping frequency divider set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the chopping frequency divider for. + * @param freq_div The desired chopping frequency divider to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_channel_chop_freq_div(const struct device *dev, uint8_t channel, + enum max2221x_ch_freq_div freq_div); + +/** + * @brief Get the chopping frequency divider of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the chopping frequency divider for. + * + * @return The chopping frequency divider on success, or a negative error code on failure. + */ +int max2221x_get_channel_chop_freq_div(const struct device *dev, uint8_t channel); + +/** + * @brief Set the slew rate of channel on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set slew rate to 400V/us for channel 1 + * int ret = max2221x_set_slew_rate(dev, 1, MAX2221X_SLEW_RATE_400V_PER_US); + * if (ret) + * printk("Failed to set slew rate: %d\n", ret); + * else + * printk("Slew rate set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the slew rate for. + * @param slew_rate The desired slew rate to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_slew_rate(const struct device *dev, uint8_t channel, + enum max2221x_slew_rate slew_rate); + +/** + * @brief Get the slew rate of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the slew rate for. + * + * @return The slew rate on success, or a negative error code on failure. + */ +int max2221x_get_slew_rate(const struct device *dev, uint8_t channel); + +/** + * @brief Set the digital gain for the CDR of channel on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set gain to 1 for channel 1 + * int ret = max2221x_set_gain(dev, 1, MAX2221X_SCALE_1); + * if (ret) + * printk("Failed to set gain: %d\n", ret); + * else + * printk("Gain set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the gain for. + * @param gain The desired gain to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_gain(const struct device *dev, uint8_t channel, enum max2221x_gain gain); + +/** + * @brief Get the digital gain for the CDR of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the gain for. + * + * @return The gain on success, or a negative error code on failure. + */ +int max2221x_get_gain(const struct device *dev, uint8_t channel); + +/** + * @brief Set the sense scaling factor of channel on the MAX2221x device. + * + * Example usage: + * @code + * #include + * + * // Set sense scaling factor 2/3 for channel 1 + * int ret = max2221x_set_sense_scaling_factor(dev, 1, MAX2221X_SNSF_2_3); + * if (ret) + * printk("Failed to set sense scaling factor: %d\n", ret); + * else + * printk("Sense scaling factor set successfully\n"); + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the sense scaling factor for. + * @param snsf The desired sense scaling factor to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_snsf(const struct device *dev, uint8_t channel, enum max2221x_snsf snsf); + +/** + * @brief Get the sense scaling factor of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the sense scaling factor for. + * + * @return The sense scaling factor on success, or a negative error code on failure. + */ +int max2221x_get_snsf(const struct device *dev, uint8_t channel); + +/** + * @brief Read the PWM duty cycle of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to read the PWM duty cycle from. + * @param duty_cycle Pointer to the variable to store the PWM duty cycle. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_pwm_dutycycle(const struct device *dev, uint8_t channel, uint16_t *duty_cycle); + +/** + * @brief Read the fault status from FAULT0 register of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_fault0(const struct device *dev); + +/** + * @brief Read the fault status from FAULT1 register of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_read_fault1(const struct device *dev); + +/** + * @brief Set the on time of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the on time for. + * @param on_time_us The desired on time to set in microseconds. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_on_time(const struct device *dev, uint8_t channel, uint16_t on_time_us); + +/** + * @brief Get the on time of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the on time for. + * + * @return The on time in microseconds on success, or a negative error code on failure. + */ +int max2221x_get_on_time(const struct device *dev, uint8_t channel); + +/** + * @brief Set the off time of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the off time for. + * @param off_time_us The desired off time to set in microseconds. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_off_time(const struct device *dev, uint8_t channel, uint16_t off_time_us); + +/** + * @brief Get the off time of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the off time for. + * + * @return The off time in microseconds on success, or a negative error code on failure. + */ +int max2221x_get_off_time(const struct device *dev, uint8_t channel); + +/** + * @brief Set the stop state of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the stop state for. + * @param stop_state The desired stop state to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_stop_state(const struct device *dev, uint8_t channel, bool stop_state); + +/** + * @brief Get the stop state of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the stop state for. + * + * @return The stop state on success, or a negative error code on failure. + */ +int max2221x_get_stop_state(const struct device *dev, uint8_t channel); + +/** + * @brief Set the repetitions of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the repetitions for. + * @param repetitions The desired repetitions to set. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_repetitions(const struct device *dev, uint8_t channel, uint16_t repetitions); + +/** + * @brief Get the repetitions of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the repetitions for. + * + * @return The repetitions on success, or a negative error code on failure. + */ +int max2221x_get_repetitions(const struct device *dev, uint8_t channel); + +/** + * @brief Start the rapid fire mode of channel on the MAX2221x device. + * + * @note Usually, set on time, off time, repetitions, and stop state before starting rapid fire + * mode. + * + * Example usage: + * @code + * #include + * + * // Set rapid fire parameters and start rapid fire mode + * int ret; + * ret = max2221x_set_on_time(dev, channel, 1000); // Set on time to 1000 us + * if (ret) { + * printk("Failed to set on time: %d\n", ret); + * return ret; + * } + * ret = max2221x_set_off_time(dev, channel, 1000); // Set off time to 1000 us + * if (ret) { + * printk("Failed to set off time: %d\n", ret); + * return ret; + * } + * ret = max2221x_set_stop_state(dev, channel, true); // Set stop state + * if (ret) { + * printk("Failed to set stop state: %d\n", ret); + * return ret; + * } + * ret = max2221x_set_repetitions(dev, channel, 10); // Set repetitions to 10 + * if (ret) { + * printk("Failed to set repetitions: %d\n", ret); + * return ret; + * } + * ret = max2221x_start_rapid_fire(dev, channel); // Start rapid fire mode + * if (ret) { + * printk("Failed to start rapid fire mode: %d\n", ret); + * } else { + * printk("Rapid fire mode started successfully\n"); + * } + * @endcode + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to start the rapid fire mode for. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_start_rapid_fire(const struct device *dev, uint8_t channel); + +/** + * @brief Stop the rapid fire mode of channel on the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to stop the rapid fire mode for. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_stop_rapid_fire(const struct device *dev, uint8_t channel); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MISC_MAX2221X_MAX2221X_H_ */ diff --git a/include/zephyr/drivers/pwm/max2221x.h b/include/zephyr/drivers/pwm/max2221x.h new file mode 100644 index 0000000000000..760f6f00dba37 --- /dev/null +++ b/include/zephyr/drivers/pwm/max2221x.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_PWM_MAX2221X_H_ +#define ZEPHYR_INCLUDE_DRIVERS_PWM_MAX2221X_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +enum max2221x_master_chop_freq { + MAX2221X_FREQ_100KHZ = 0, + MAX2221X_FREQ_80KHZ, + MAX2221X_FREQ_60KHZ, + MAX2221X_FREQ_50KHZ, + MAX2221X_FREQ_40KHZ, + MAX2221X_FREQ_30KHZ, + MAX2221X_FREQ_25KHZ, + MAX2221X_FREQ_20KHZ, + MAX2221X_FREQ_15KHZ, + MAX2221X_FREQ_10KHZ, + MAX2221X_FREQ_7500HZ, + MAX2221X_FREQ_5000HZ, + MAX2221X_FREQ_2500HZ, + + MAX2221X_FREQ_INVALID, +}; + +enum max2221x_individual_chop_freq { + MAX2221X_FREQ_M = 0, + MAX2221X_FREQ_M_2, + MAX2221X_FREQ_M_4, + MAX2221X_FREQ_M_8, + + MAX2221X_FREQ_M_INVALID, +}; + +/** + * @brief Get the master chop frequency of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return The master chop frequency in Hz on success, or a negative error code on failure. + */ +int max2221x_get_master_chop_freq(const struct device *dev); + +/** + * @brief Get the individual channel frequency of the MAX2221x device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to read. + * @param channel_freq Pointer to the variable to store the individual channel frequency. + * @return The individual channel frequency in Hz on success, or a negative error code on failure. + */ +int max2221x_get_channel_freq(const struct device *dev, uint32_t channel, uint32_t *channel_freq); + +/** + * @brief Calculate the duty cycle. + * + * @param period Period (in microseconds) set to the PWM. HW specific. + * @param pulse Pulse width (in microseconds) set to the PWM. HW specific. + * @param duty_cycle Pointer to the variable to store the duty cycle. + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_calculate_duty_cycle(uint32_t pulse, uint32_t period, uint16_t *duty_cycle); + +/** + * @brief Calculate the master frequency divisor used for calculating individual frequency. + * + * @param master_freq Master frequency (in Hz) set to the PWM. HW specific. + * @param period Period (in microseconds) set to the PWM. HW specific. + * @param freq_divisor Master frequency divisor set per channel. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_calculate_master_freq_divisor(uint32_t master_freq, uint32_t period, + int *freq_divisor); + +/** + * @brief Get the cycles per second for the pwm channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to get the cycles. + * @param cycles Pointer to the variable to store the cycles. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles); + +/** + * @brief Set the duty cycle for the pwm channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The channel to set the cycles. + * @param period Period (in microseconds) set to the PWM. HW specific. + * @param pulse Pulse width (in microseconds) set to the PWM. HW specific. + * @param flags Flags for pin configuration. + * + * @return 0 on success, or a negative error code on failure. + */ +int max2221x_set_cycles(const struct device *dev, uint32_t channel, uint32_t period, uint32_t pulse, + pwm_flags_t flags); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_PWM_MAX2221X_H_ */ diff --git a/tests/drivers/build_all/pwm/max2221x.overlay b/tests/drivers/build_all/pwm/max2221x.overlay new file mode 100644 index 0000000000000..70221d182a2a7 --- /dev/null +++ b/tests/drivers/build_all/pwm/max2221x.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&spi2 { + status = "okay"; + clock-frequency = <2000000>; + + max2221x_mfd: max2221x@0 { + compatible = "adi,max2221x"; + reg = <0x0>; + spi-max-frequency = ; + status = "okay"; + + adi_max2221x_pwm: pwm-device { + compatible = "adi,max2221x-pwm"; + status = "okay"; + #pwm-cells = <3>; + }; + }; +}; diff --git a/tests/drivers/build_all/pwm/testcase.yaml b/tests/drivers/build_all/pwm/testcase.yaml index 34156300b5da2..09c5818d78597 100644 --- a/tests/drivers/build_all/pwm/testcase.yaml +++ b/tests/drivers/build_all/pwm/testcase.yaml @@ -59,3 +59,5 @@ tests: extra_args: DTC_OVERLAY_FILE=max31790.overlay drivers.pwm.rts5912.build: platform_allow: rts5912_evb + drivers.pwm.max2221x.build: + platform_allow: nucleo_l476rg