|  | /* | 
|  | * Copyright (c) 2023 Nordic Semiconductor ASA | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/regulator.h> | 
|  | #include <zephyr/drivers/mfd/npm13xx.h> | 
|  | #include <zephyr/dt-bindings/regulator/npm13xx.h> | 
|  | #include <zephyr/sys/linear_range.h> | 
|  | #include <zephyr/sys/util.h> | 
|  |  | 
|  | /* nPM13xx voltage sources */ | 
|  | enum npm13xx_sources { | 
|  | NPM13XX_SOURCE_BUCK1, | 
|  | NPM13XX_SOURCE_BUCK2, | 
|  | NPM13XX_SOURCE_LDO1, | 
|  | NPM13XX_SOURCE_LDO2, | 
|  | }; | 
|  |  | 
|  | /* nPM13xx gpio control channels */ | 
|  | enum npm13xx_gpio_type { | 
|  | NPM13XX_GPIO_TYPE_ENABLE, | 
|  | NPM13XX_GPIO_TYPE_RETENTION, | 
|  | NPM13XX_GPIO_TYPE_PWM | 
|  | }; | 
|  |  | 
|  | /* nPM13xx regulator base addresses */ | 
|  | #define BUCK_BASE 0x04U | 
|  | #define LDSW_BASE 0x08U | 
|  | #define SHIP_BASE 0x0BU | 
|  |  | 
|  | /* nPM13xx regulator register offsets */ | 
|  | #define BUCK_OFFSET_EN_SET    0x00U | 
|  | #define BUCK_OFFSET_EN_CLR    0x01U | 
|  | #define BUCK_OFFSET_PWM_SET   0x04U | 
|  | #define BUCK_OFFSET_PWM_CLR   0x05U | 
|  | #define BUCK_OFFSET_VOUT_NORM 0x08U | 
|  | #define BUCK_OFFSET_VOUT_RET  0x09U | 
|  | #define BUCK_OFFSET_EN_CTRL   0x0CU | 
|  | #define BUCK_OFFSET_VRET_CTRL 0x0DU | 
|  | #define BUCK_OFFSET_PWM_CTRL  0x0EU | 
|  | #define BUCK_OFFSET_SW_CTRL   0x0FU | 
|  | #define BUCK_OFFSET_VOUT_STAT 0x10U | 
|  | #define BUCK_OFFSET_CTRL0     0x15U | 
|  | #define BUCK_OFFSET_STATUS    0x34U | 
|  |  | 
|  | /* nPM13xx ldsw register offsets */ | 
|  | #define LDSW_OFFSET_EN_SET  0x00U | 
|  | #define LDSW_OFFSET_EN_CLR  0x01U | 
|  | #define LDSW_OFFSET_STATUS  0x04U | 
|  | #define LDSW_OFFSET_GPISEL  0x05U | 
|  | #define LDSW_OFFSET_CONFIG  0x07U | 
|  | #define LDSW_OFFSET_LDOSEL  0x08U | 
|  | #define LDSW_OFFSET_VOUTSEL 0x0CU | 
|  |  | 
|  | /* nPM13xx ship register offsets */ | 
|  | #define SHIP_OFFSET_SHIP 0x02U | 
|  |  | 
|  | #define BUCK1_ON_MASK          0x04U | 
|  | #define BUCK2_ON_MASK          0x40U | 
|  | #define BUCK1_EN_PULLDOWN_MASK BIT(2) | 
|  | #define BUCK2_EN_PULLDOWN_MASK BIT(3) | 
|  |  | 
|  | #define LDSW1_ON_MASK 0x03U | 
|  | #define LDSW2_ON_MASK 0x0CU | 
|  |  | 
|  | #define LDSW1_SOFTSTART_MASK        0x0CU | 
|  | #define LDSW1_SOFTSTART_SHIFT       2U | 
|  | #define LDSW1_ACTIVE_DISCHARGE_MASK BIT(6) | 
|  | #define LDSW2_SOFTSTART_MASK        0x30U | 
|  | #define LDSW2_SOFTSTART_SHIFT       4U | 
|  | #define LDSW2_ACTIVE_DISCHARGE_MASK BIT(7) | 
|  |  | 
|  | #define NPM13XX_GPIO_UNUSED UINT8_MAX | 
|  |  | 
|  | struct npm13xx_gpio_info { | 
|  | uint8_t pin; | 
|  | bool invert; | 
|  | }; | 
|  |  | 
|  | struct regulator_npm13xx_pconfig { | 
|  | const struct device *mfd; | 
|  | struct gpio_dt_spec dvs_state_pins[5]; | 
|  | }; | 
|  |  | 
|  | struct regulator_npm13xx_config { | 
|  | struct regulator_common_config common; | 
|  | const struct device *mfd; | 
|  | uint8_t source; | 
|  | int32_t retention_uv; | 
|  | struct npm13xx_gpio_info enable_gpios; | 
|  | struct npm13xx_gpio_info retention_gpios; | 
|  | struct npm13xx_gpio_info pwm_gpios; | 
|  | uint8_t soft_start; | 
|  | bool active_discharge; | 
|  | bool ldo_disable_workaround; | 
|  | }; | 
|  |  | 
|  | struct regulator_npm13xx_data { | 
|  | struct regulator_common_data data; | 
|  | }; | 
|  |  | 
|  | /* Linear range for output voltage, common for all bucks and LDOs on this device */ | 
|  | static const struct linear_range buckldo_range = LINEAR_RANGE_INIT(1000000, 100000, 0U, 23U); | 
|  |  | 
|  | unsigned int regulator_npm13xx_count_voltages(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return linear_range_values_count(&buckldo_range); | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_list_voltage(const struct device *dev, unsigned int idx, int32_t *volt_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return linear_range_get_value(&buckldo_range, idx, volt_uv); | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int retention_set_voltage(const struct device *dev, int32_t retention_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint16_t idx; | 
|  | uint8_t chan; | 
|  | int ret; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | chan = 0U; | 
|  | break; | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | chan = 1U; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | ret = linear_range_get_win_index(&buckldo_range, retention_uv, retention_uv, &idx); | 
|  |  | 
|  | if (ret == -EINVAL) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_VOUT_RET + (chan * 2U), | 
|  | idx); | 
|  | } | 
|  |  | 
|  | static int buck_get_voltage_index(const struct device *dev, uint8_t chan, uint8_t *idx) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t sel; | 
|  | int ret; | 
|  |  | 
|  | ret = mfd_npm13xx_reg_read(config->mfd, BUCK_BASE, BUCK_OFFSET_SW_CTRL, &sel); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if ((sel >> chan) & 1U) { | 
|  | /* SW control */ | 
|  | return mfd_npm13xx_reg_read(config->mfd, BUCK_BASE, | 
|  | BUCK_OFFSET_VOUT_NORM + (chan * 2U), idx); | 
|  | } | 
|  |  | 
|  | /* VSET pin control */ | 
|  | return mfd_npm13xx_reg_read(config->mfd, BUCK_BASE, BUCK_OFFSET_VOUT_STAT + chan, idx); | 
|  | } | 
|  |  | 
|  | static int buck_set_voltage(const struct device *dev, uint8_t chan, int32_t min_uv, int32_t max_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t mask; | 
|  | uint8_t curr_idx; | 
|  | uint16_t idx; | 
|  | int ret; | 
|  |  | 
|  | ret = linear_range_get_win_index(&buckldo_range, min_uv, max_uv, &idx); | 
|  |  | 
|  | if (ret == -EINVAL) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Get current setting, and return if current and new index match */ | 
|  | ret = buck_get_voltage_index(dev, chan, &curr_idx); | 
|  |  | 
|  | if ((ret < 0) || (idx == curr_idx)) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_VOUT_NORM + (chan * 2U), | 
|  | idx); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Enable SW control of buck output */ | 
|  | mask = BIT(chan); | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_SW_CTRL, mask, mask); | 
|  | } | 
|  |  | 
|  | static int ldo_set_voltage(const struct device *dev, uint8_t chan, int32_t min_uv, int32_t max_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint16_t idx; | 
|  | int ret; | 
|  |  | 
|  | ret = linear_range_get_win_index(&buckldo_range, min_uv, max_uv, &idx); | 
|  |  | 
|  | if (ret == -EINVAL) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_VOUTSEL + chan, idx); | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return buck_set_voltage(dev, 0, min_uv, max_uv); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return buck_set_voltage(dev, 1, min_uv, max_uv); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return ldo_set_voltage(dev, 0, min_uv, max_uv); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return ldo_set_voltage(dev, 1, min_uv, max_uv); | 
|  | default: | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int buck_get_voltage(const struct device *dev, uint8_t chan, int32_t *volt_uv) | 
|  | { | 
|  | uint8_t idx; | 
|  | int ret; | 
|  |  | 
|  | ret = buck_get_voltage_index(dev, chan, &idx); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return linear_range_get_value(&buckldo_range, idx, volt_uv); | 
|  | } | 
|  |  | 
|  | static int ldo_get_voltage(const struct device *dev, uint8_t chan, int32_t *volt_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t idx; | 
|  | int ret; | 
|  |  | 
|  | ret = mfd_npm13xx_reg_read(config->mfd, LDSW_BASE, LDSW_OFFSET_VOUTSEL + chan, &idx); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return linear_range_get_value(&buckldo_range, idx, volt_uv); | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_get_voltage(const struct device *dev, int32_t *volt_uv) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return buck_get_voltage(dev, 0, volt_uv); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return buck_get_voltage(dev, 1, volt_uv); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return ldo_get_voltage(dev, 0, volt_uv); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return ldo_get_voltage(dev, 1, volt_uv); | 
|  | default: | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int set_buck_mode(const struct device *dev, uint8_t chan, regulator_mode_t mode) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t pfm_mask = BIT(chan); | 
|  | uint8_t pfm_data; | 
|  | uint8_t pwm_reg; | 
|  | int ret; | 
|  |  | 
|  | switch (mode) { | 
|  | case NPM13XX_BUCK_MODE_PWM: | 
|  | pfm_data = 0U; | 
|  | pwm_reg = BUCK_OFFSET_PWM_SET; | 
|  | break; | 
|  | case NPM13XX_BUCK_MODE_AUTO: | 
|  | pfm_data = 0U; | 
|  | pwm_reg = BUCK_OFFSET_PWM_CLR; | 
|  | break; | 
|  | case NPM13XX_BUCK_MODE_PFM: | 
|  | pfm_data = pfm_mask; | 
|  | pwm_reg = BUCK_OFFSET_PWM_CLR; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | ret = mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_CTRL0, pfm_data, pfm_mask); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, pwm_reg + (chan * 2U), 1U); | 
|  | } | 
|  |  | 
|  | static int set_ldsw_mode(const struct device *dev, uint8_t chan, regulator_mode_t mode) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (mode) { | 
|  | case NPM13XX_LDSW_MODE_LDO: | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_LDOSEL + chan, 1U); | 
|  | case NPM13XX_LDSW_MODE_LDSW: | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_LDOSEL + chan, 0U); | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_set_mode(const struct device *dev, regulator_mode_t mode) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return set_buck_mode(dev, 0, mode); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return set_buck_mode(dev, 1, mode); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return set_ldsw_mode(dev, 0, mode); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return set_ldsw_mode(dev, 1, mode); | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_enable(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | int ret; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_EN_SET, 1U); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_EN_SET + 2U, 1U); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | ret = mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_EN_SET, 1U); | 
|  | break; | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | ret = mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_EN_SET + 2U, 1U); | 
|  | break; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (!config->ldo_disable_workaround) { | 
|  | uint8_t unused; | 
|  |  | 
|  | k_msleep(2); | 
|  | return mfd_npm13xx_reg_read(config->mfd, LDSW_BASE, LDSW_OFFSET_STATUS, &unused); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_disable(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_EN_CLR, 1U); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return mfd_npm13xx_reg_write(config->mfd, BUCK_BASE, BUCK_OFFSET_EN_CLR + 2U, 1U); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_EN_CLR, 1U); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_EN_CLR + 2U, 1U); | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int regulator_npm13xx_set_buck_pin_ctrl(const struct device *dev, uint8_t chan, | 
|  | const struct npm13xx_gpio_info *pin_info, | 
|  | enum npm13xx_gpio_type type) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t inv = pin_info->invert ? 1 : 0; | 
|  | uint8_t ctrl; | 
|  | uint8_t mask; | 
|  |  | 
|  | switch (chan) { | 
|  | case 0: | 
|  | /* Invert control in bit 6, pin control in bits 2-0 */ | 
|  | ctrl = (inv << 6U) | (pin_info->pin + 1U); | 
|  | mask = BIT(6U) | BIT_MASK(3U); | 
|  | break; | 
|  | case 1: | 
|  | /* Invert control in bit 7, pin control in bits 5-3 */ | 
|  | ctrl = (inv << 7U) | ((pin_info->pin + 1U) << 3U); | 
|  | mask = BIT(7U) | (BIT_MASK(3U) << 3U); | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (type) { | 
|  | case NPM13XX_GPIO_TYPE_ENABLE: | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_EN_CTRL, ctrl, | 
|  | mask); | 
|  | case NPM13XX_GPIO_TYPE_PWM: | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_PWM_CTRL, ctrl, | 
|  | mask); | 
|  | case NPM13XX_GPIO_TYPE_RETENTION: | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_VRET_CTRL, ctrl, | 
|  | mask); | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int regulator_npm13xx_set_ldsw_pin_ctrl(const struct device *dev, uint8_t chan, | 
|  | const struct npm13xx_gpio_info *pin_info, | 
|  | enum npm13xx_gpio_type type) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t inv = pin_info->invert ? 1 : 0; | 
|  | uint8_t ctrl; | 
|  |  | 
|  | if (type != NPM13XX_GPIO_TYPE_ENABLE) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | ctrl = (pin_info->pin + 1U) | (inv << 3U); | 
|  |  | 
|  | return mfd_npm13xx_reg_write(config->mfd, LDSW_BASE, LDSW_OFFSET_GPISEL + chan, ctrl); | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_set_pin_ctrl(const struct device *dev, const struct npm13xx_gpio_info *info, | 
|  | enum npm13xx_gpio_type type) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | if (info->pin == NPM13XX_GPIO_UNUSED) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return regulator_npm13xx_set_buck_pin_ctrl(dev, 0, info, type); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return regulator_npm13xx_set_buck_pin_ctrl(dev, 1, info, type); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return regulator_npm13xx_set_ldsw_pin_ctrl(dev, 0, info, type); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return regulator_npm13xx_set_ldsw_pin_ctrl(dev, 1, info, type); | 
|  | default: | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_dvs_state_set(const struct device *dev, regulator_dvs_state_t state) | 
|  | { | 
|  | const struct regulator_npm13xx_pconfig *pconfig = dev->config; | 
|  | const struct gpio_dt_spec *spec; | 
|  | int ret; | 
|  |  | 
|  | for (size_t idx = 0U; idx < 5U; idx++) { | 
|  | spec = &pconfig->dvs_state_pins[idx]; | 
|  |  | 
|  | if (spec->port != NULL) { | 
|  | ret = gpio_pin_set_dt(spec, ((state >> idx) & 1U) != 0U); | 
|  |  | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_ship_mode(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_pconfig *pconfig = dev->config; | 
|  |  | 
|  | return mfd_npm13xx_reg_write(pconfig->mfd, SHIP_BASE, SHIP_OFFSET_SHIP, 1U); | 
|  | } | 
|  |  | 
|  | static DEVICE_API(regulator_parent, parent_api) = { | 
|  | .dvs_state_set = regulator_npm13xx_dvs_state_set, | 
|  | .ship_mode = regulator_npm13xx_ship_mode, | 
|  | }; | 
|  |  | 
|  | int regulator_npm13xx_common_init(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_pconfig *pconfig = dev->config; | 
|  | const struct gpio_dt_spec *spec; | 
|  | int ret; | 
|  |  | 
|  | for (size_t idx = 0U; idx < 5U; idx++) { | 
|  | spec = &pconfig->dvs_state_pins[idx]; | 
|  |  | 
|  | if (spec->port != NULL) { | 
|  | if (!gpio_is_ready_dt(spec)) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | ret = gpio_pin_configure_dt(spec, GPIO_OUTPUT); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int get_enabled_reg(const struct device *dev, uint8_t base, uint8_t offset, uint8_t mask, | 
|  | bool *enabled) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | uint8_t data; | 
|  |  | 
|  | int ret = mfd_npm13xx_reg_read(config->mfd, base, offset, &data); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | *enabled = (data & mask) != 0U; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int get_enabled(const struct device *dev, bool *enabled) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return get_enabled_reg(dev, BUCK_BASE, BUCK_OFFSET_STATUS, BUCK1_ON_MASK, enabled); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return get_enabled_reg(dev, BUCK_BASE, BUCK_OFFSET_STATUS, BUCK2_ON_MASK, enabled); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return get_enabled_reg(dev, LDSW_BASE, LDSW_OFFSET_STATUS, LDSW1_ON_MASK, enabled); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return get_enabled_reg(dev, LDSW_BASE, LDSW_OFFSET_STATUS, LDSW2_ON_MASK, enabled); | 
|  | default: | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int soft_start_set(const struct device *dev, uint8_t soft_start) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return mfd_npm13xx_reg_update(config->mfd, LDSW_BASE, LDSW_OFFSET_CONFIG, | 
|  | soft_start << LDSW1_SOFTSTART_SHIFT, | 
|  | LDSW1_SOFTSTART_MASK); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return mfd_npm13xx_reg_update(config->mfd, LDSW_BASE, LDSW_OFFSET_CONFIG, | 
|  | soft_start << LDSW2_SOFTSTART_SHIFT, | 
|  | LDSW2_SOFTSTART_MASK); | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int active_discharge_set(const struct device *dev, bool enabled) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  |  | 
|  | switch (config->source) { | 
|  | case NPM13XX_SOURCE_BUCK1: | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_CTRL0, | 
|  | enabled ? BUCK1_EN_PULLDOWN_MASK : 0, | 
|  | BUCK1_EN_PULLDOWN_MASK); | 
|  | case NPM13XX_SOURCE_BUCK2: | 
|  | return mfd_npm13xx_reg_update(config->mfd, BUCK_BASE, BUCK_OFFSET_CTRL0, | 
|  | enabled ? BUCK2_EN_PULLDOWN_MASK : 0, | 
|  | BUCK2_EN_PULLDOWN_MASK); | 
|  | case NPM13XX_SOURCE_LDO1: | 
|  | return mfd_npm13xx_reg_update(config->mfd, LDSW_BASE, LDSW_OFFSET_CONFIG, | 
|  | enabled ? LDSW1_ACTIVE_DISCHARGE_MASK : 0, | 
|  | LDSW1_ACTIVE_DISCHARGE_MASK); | 
|  | case NPM13XX_SOURCE_LDO2: | 
|  | return mfd_npm13xx_reg_update(config->mfd, LDSW_BASE, LDSW_OFFSET_CONFIG, | 
|  | enabled ? LDSW2_ACTIVE_DISCHARGE_MASK : 0, | 
|  | LDSW2_ACTIVE_DISCHARGE_MASK); | 
|  | default: | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | int regulator_npm13xx_init(const struct device *dev) | 
|  | { | 
|  | const struct regulator_npm13xx_config *config = dev->config; | 
|  | bool enabled; | 
|  | int ret = 0; | 
|  |  | 
|  | if (!device_is_ready(config->mfd)) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | ret = get_enabled(dev, &enabled); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = regulator_common_init(dev, enabled); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Configure retention voltage */ | 
|  | if (config->retention_uv != 0) { | 
|  | ret = retention_set_voltage(dev, config->retention_uv); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Configure soft start */ | 
|  | if (config->soft_start != UINT8_MAX) { | 
|  | ret = soft_start_set(dev, config->soft_start); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Configure active discharge */ | 
|  | ret = active_discharge_set(dev, config->active_discharge); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Configure GPIO pin control */ | 
|  | ret = regulator_npm13xx_set_pin_ctrl(dev, &config->enable_gpios, NPM13XX_GPIO_TYPE_ENABLE); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = regulator_npm13xx_set_pin_ctrl(dev, &config->retention_gpios, | 
|  | NPM13XX_GPIO_TYPE_RETENTION); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = regulator_npm13xx_set_pin_ctrl(dev, &config->pwm_gpios, NPM13XX_GPIO_TYPE_PWM); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static DEVICE_API(regulator, api) = { | 
|  | .enable = regulator_npm13xx_enable, | 
|  | .disable = regulator_npm13xx_disable, | 
|  | .count_voltages = regulator_npm13xx_count_voltages, | 
|  | .list_voltage = regulator_npm13xx_list_voltage, | 
|  | .set_voltage = regulator_npm13xx_set_voltage, | 
|  | .get_voltage = regulator_npm13xx_get_voltage, | 
|  | .set_mode = regulator_npm13xx_set_mode, | 
|  | }; | 
|  |  | 
|  | #define GPIO_CONFIG_DEFINE(node_id, prop)                                                          \ | 
|  | COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop),                                               \ | 
|  | ({DT_PROP_BY_IDX(node_id, prop, 0),                                            \ | 
|  | !!(DT_PROP_BY_IDX(node_id, prop, 1) & GPIO_ACTIVE_LOW)}),                    \ | 
|  | ({NPM13XX_GPIO_UNUSED, false})) | 
|  |  | 
|  | #define REGULATOR_NPM13XX_DEFINE(partno, node_id, id, _source)                                     \ | 
|  | BUILD_ASSERT(DT_PROP_LEN_OR(node_id, enable_gpio_config, 2) == 2);                         \ | 
|  | BUILD_ASSERT(DT_PROP_LEN_OR(node_id, retention_gpio_config, 2) == 2);                      \ | 
|  | BUILD_ASSERT(DT_PROP_LEN_OR(node_id, pwm_gpio_config, 2) == 2);                            \ | 
|  | static struct regulator_npm13xx_data regulator_##partno##_data_##id;                       \ | 
|  | \ | 
|  | static const struct regulator_npm13xx_config regulator_##partno##_config_##id = {          \ | 
|  | .common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id),                                \ | 
|  | .mfd = DEVICE_DT_GET(DT_GPARENT(node_id)),                                         \ | 
|  | .source = _source,                                                                 \ | 
|  | .retention_uv = DT_PROP_OR(node_id, retention_microvolt, 0),                       \ | 
|  | .soft_start = DT_ENUM_IDX_OR(node_id, soft_start_microamp, UINT8_MAX),             \ | 
|  | .enable_gpios = GPIO_CONFIG_DEFINE(node_id, enable_gpio_config),                   \ | 
|  | .retention_gpios = GPIO_CONFIG_DEFINE(node_id, retention_gpio_config),             \ | 
|  | .pwm_gpios = GPIO_CONFIG_DEFINE(node_id, pwm_gpio_config),                         \ | 
|  | .active_discharge = DT_PROP(node_id, active_discharge),                            \ | 
|  | .ldo_disable_workaround = DT_PROP(node_id, nordic_anomaly38_disable_workaround)};  \ | 
|  | \ | 
|  | DEVICE_DT_DEFINE(node_id, regulator_npm13xx_init, NULL, ®ulator_##partno##_data_##id,   \ | 
|  | ®ulator_##partno##_config_##id, POST_KERNEL,                           \ | 
|  | CONFIG_REGULATOR_NPM13XX_INIT_PRIORITY, &api); | 
|  |  | 
|  | #define REGULATOR_NPM13XX_DEFINE_COND(partno, n, child, source)                                    \ | 
|  | COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(n, child)),                                       \ | 
|  | (REGULATOR_NPM13XX_DEFINE(partno, DT_INST_CHILD(n, child), child##n, source)), \ | 
|  | ()) | 
|  |  | 
|  | #define REGULATOR_NPM13XX_DEFINE_ALL(partno, n)                                                    \ | 
|  | static const struct regulator_npm13xx_pconfig regulator_##partno##_config##n = {           \ | 
|  | .mfd = DEVICE_DT_GET(DT_INST_PARENT(n)),                                           \ | 
|  | .dvs_state_pins = {GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, dvs_gpios, 0, {0}),          \ | 
|  | GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, dvs_gpios, 1, {0}),          \ | 
|  | GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, dvs_gpios, 2, {0}),          \ | 
|  | GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, dvs_gpios, 3, {0}),          \ | 
|  | GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, dvs_gpios, 4, {0})}};        \ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(n, regulator_npm13xx_common_init, NULL, NULL,                        \ | 
|  | ®ulator_##partno##_config##n, POST_KERNEL,                        \ | 
|  | CONFIG_REGULATOR_NPM13XX_COMMON_INIT_PRIORITY,                       \ | 
|  | &parent_api);                                                        \ | 
|  | \ | 
|  | REGULATOR_NPM13XX_DEFINE_COND(partno, n, buck1, NPM13XX_SOURCE_BUCK1)                      \ | 
|  | REGULATOR_NPM13XX_DEFINE_COND(partno, n, buck2, NPM13XX_SOURCE_BUCK2)                      \ | 
|  | REGULATOR_NPM13XX_DEFINE_COND(partno, n, ldo1, NPM13XX_SOURCE_LDO1)                        \ | 
|  | REGULATOR_NPM13XX_DEFINE_COND(partno, n, ldo2, NPM13XX_SOURCE_LDO2) | 
|  |  | 
|  | #define DT_DRV_COMPAT nordic_npm1300_regulator | 
|  | #define REGULATOR_NPM1300_DEFINE_ALL(n) REGULATOR_NPM13XX_DEFINE_ALL(npm1300, n) | 
|  | DT_INST_FOREACH_STATUS_OKAY(REGULATOR_NPM1300_DEFINE_ALL) | 
|  |  | 
|  | #undef DT_DRV_COMPAT | 
|  | #define DT_DRV_COMPAT nordic_npm1304_regulator | 
|  | #define REGULATOR_NPM1304_DEFINE_ALL(n) REGULATOR_NPM13XX_DEFINE_ALL(npm1304, n) | 
|  | DT_INST_FOREACH_STATUS_OKAY(REGULATOR_NPM1304_DEFINE_ALL) |