drivers: regulator: npm2100: Add driver for npm2100 pmic

Add regulator driver for npm2100 pmic.
This pmic has one boost and one ldo regulator.

Signed-off-by: Audun Korneliussen <audun.korneliussen@nordicsemi.no>
This commit is contained in:
Audun Korneliussen 2024-09-20 10:55:23 +02:00 committed by Anas Nashif
commit f4443617d8
7 changed files with 965 additions and 0 deletions

View file

@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_REGULATOR_GPIO regulator_gpio.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_MAX20335 regulator_max20335.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_MAX20335 regulator_max20335.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1100 regulator_npm1100.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1100 regulator_npm1100.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1300 regulator_npm1300.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1300 regulator_npm1300.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM2100 regulator_npm2100.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM6001 regulator_npm6001.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM6001 regulator_npm6001.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_PCA9420 regulator_pca9420.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_PCA9420 regulator_pca9420.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_SHELL regulator_shell.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_SHELL regulator_shell.c)

View file

@ -36,6 +36,7 @@ source "drivers/regulator/Kconfig.gpio"
source "drivers/regulator/Kconfig.max20335" source "drivers/regulator/Kconfig.max20335"
source "drivers/regulator/Kconfig.npm1100" source "drivers/regulator/Kconfig.npm1100"
source "drivers/regulator/Kconfig.npm1300" source "drivers/regulator/Kconfig.npm1300"
source "drivers/regulator/Kconfig.npm2100"
source "drivers/regulator/Kconfig.npm6001" source "drivers/regulator/Kconfig.npm6001"
source "drivers/regulator/Kconfig.pca9420" source "drivers/regulator/Kconfig.pca9420"
source "drivers/regulator/Kconfig.rpi_pico" source "drivers/regulator/Kconfig.rpi_pico"

View file

@ -0,0 +1,29 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config REGULATOR_NPM2100
bool "nPM2100 PMIC regulator driver"
default y
depends on DT_HAS_NORDIC_NPM2100_REGULATOR_ENABLED
select I2C
select MFD
help
Enable the Nordic nPM2100 PMIC regulator driver
if REGULATOR_NPM2100
config REGULATOR_NPM2100_COMMON_INIT_PRIORITY
int "nPM2100 regulator driver init priority (common part)"
default 85
help
Init priority for the Nordic nPM2100 regulator driver (common part).
It must be greater than I2C init priority.
config REGULATOR_NPM2100_INIT_PRIORITY
int "nPM2100 regulator driver init priority"
default 86
help
Init priority for the Nordic nPM2100 regulator driver. It must be
greater than REGULATOR_NPM2100_COMMON_INIT_PRIORITY.
endif

View file

@ -0,0 +1,791 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nordic_npm2100_regulator
#include <errno.h>
#include <string.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/dt-bindings/regulator/npm2100.h>
#include <zephyr/sys/linear_range.h>
#include <zephyr/sys/util.h>
enum npm2100_sources {
NPM2100_SOURCE_BOOST,
NPM2100_SOURCE_LDOSW,
};
#define BOOST_VOUT 0x22U
#define BOOST_VOUTSEL 0x23U
#define BOOST_OPER 0x24U
#define BOOST_LIMIT 0x26U
#define BOOST_GPIO 0x28U
#define BOOST_PIN 0x29U
#define BOOST_CTRLSET 0x2AU
#define BOOST_CTRLCLR 0x2BU
#define BOOST_IBATLIM 0x2DU
#define BOOST_VBATMINL 0x2FU
#define BOOST_VBATMINH 0x30U
#define BOOST_VOUTMIN 0x31U
#define BOOST_VOUTWRN 0x32U
#define BOOST_VOUTDPS 0x33U
#define BOOST_STATUS0 0x34U
#define BOOST_STATUS1 0x35U
#define BOOST_VSET0 0x36U
#define BOOST_VSET1 0x37U
#define LDOSW_VOUT 0x68U
#define LDOSW_ENABLE 0x69U
#define LDOSW_SEL 0x6AU
#define LDOSW_GPIO 0x6BU
#define LDOSW_STATUS 0x6EU
#define LDOSW_PRGOCP 0x6FU
#define SHIP_TASK_SHIP 0xC0U
#define RESET_ALTCONFIG 0xD6U
#define RESET_WRITESTICKY 0xDBU
#define RESET_STROBESTICKY 0xDCU
#define BOOST_OPER_MODE_MASK 0x07U
#define BOOST_OPER_MODE_AUTO 0x00U
#define BOOST_OPER_MODE_HP 0x01U
#define BOOST_OPER_MODE_LP 0x02U
#define BOOST_OPER_MODE_PASS 0x03U
#define BOOST_OPER_MODE_NOHP 0x04U
#define BOOST_OPER_DPS_MASK 0x18U
#define BOOST_OPER_DPS_DISABLE 0x00U
#define BOOST_OPER_DPS_ALLOW 0x01U
#define BOOST_OPER_DPS_ALLOWLP 0x02U
#define BOOST_OPER_DPSTIMER_MASK 0x60U
#define BOOST_PIN_FORCE_HP 0x00U
#define BOOST_PIN_FORCE_LP 0x01U
#define BOOST_PIN_FORCE_PASS 0x02U
#define BOOST_PIN_FORCE_NOHP 0x03U
#define BOOST_STATUS0_MODE_MASK 0x07U
#define BOOST_STATUS0_MODE_HP 0x00U
#define BOOST_STATUS0_MODE_LP 0x01U
#define BOOST_STATUS0_MODE_ULP 0x02U
#define BOOST_STATUS0_MODE_PT 0x03U
#define BOOST_STATUS0_MODE_DPS 0x04U
#define BOOST_STATUS1_VSET_MASK 0x40U
#define LDOSW_SEL_OPER_MASK 0x06U
#define LDOSW_SEL_OPER_AUTO 0x00U
#define LDOSW_SEL_OPER_ULP 0x02U
#define LDOSW_SEL_OPER_HP 0x04U
#define LDOSW_SEL_OPER_PIN 0x06U
#define LDOSW_GPIO_PIN_MASK 0x07U
#define LDOSW_GPIO_PINACT_MASK 0x18U
#define LDOSW_GPIO_PINACT_HP 0x00U
#define LDOSW_GPIO_PINACT_ULP 0x08U
#define LDOSW_GPIO_PININACT_OFF 0x00U
#define LDOSW_GPIO_PININACT_ULP 0x10U
#define LDOSW_STATUS_LDO 0x01U
#define LDOSW_STATUS_SW 0x02U
#define LDOSW_STATUS_HP 0x04U
#define LDOSW_STATUS_ULP 0x08U
#define LDOSW_STATUS_OCP 0x10U
#define RESET_ALTCONFIG_LDOSW_OFF 0x01U
struct regulator_npm2100_pconfig {
struct i2c_dt_spec i2c;
struct gpio_dt_spec dvs_state_pins[2];
};
struct regulator_npm2100_config {
struct regulator_common_config common;
struct i2c_dt_spec i2c;
uint8_t source;
struct gpio_dt_spec mode_gpios;
bool ldosw_wd_reset;
uint8_t dps_timer;
uint8_t dps_pulse_limit;
};
struct regulator_npm2100_data {
struct regulator_common_data data;
bool ldsw_mode;
};
static const struct linear_range boost_range = LINEAR_RANGE_INIT(1800000, 50000, 0U, 30U);
static const struct linear_range ldosw_range = LINEAR_RANGE_INIT(800000, 50000, 8U, 52U);
static const struct linear_range vset0_range = LINEAR_RANGE_INIT(1800000, 100000, 0U, 6U);
static const struct linear_range vset1_ranges[] = {LINEAR_RANGE_INIT(3000000, 0, 0U, 0U),
LINEAR_RANGE_INIT(2700000, 100000, 1U, 3U),
LINEAR_RANGE_INIT(3100000, 100000, 4U, 6U)};
static const struct linear_range boost_ocp_range = LINEAR_RANGE_INIT(0, 300000, 0U, 1U);
static const struct linear_range ldsw_ocp_ranges[] = {LINEAR_RANGE_INIT(40000, 0, 0U, 0U),
LINEAR_RANGE_INIT(70000, 5000, 1U, 3U),
LINEAR_RANGE_INIT(110000, 0, 4U, 4U)};
static const uint8_t ldo_ocp_lookup[] = {13, 7, 6, 4, 1};
static const struct linear_range ldo_ocp_ranges[] = {LINEAR_RANGE_INIT(25000, 13000, 0U, 1U),
LINEAR_RANGE_INIT(50000, 25000, 2U, 3U),
LINEAR_RANGE_INIT(150000, 0, 4U, 4U)};
static const uint8_t ldsw_ocp_lookup[] = {1, 7, 8, 9, 15};
static unsigned int regulator_npm2100_count_voltages(const struct device *dev)
{
const struct regulator_npm2100_config *config = dev->config;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return linear_range_values_count(&boost_range);
case NPM2100_SOURCE_LDOSW:
return linear_range_values_count(&ldosw_range);
default:
return 0;
}
}
static int regulator_npm2100_list_voltage(const struct device *dev, unsigned int idx,
int32_t *volt_uv)
{
const struct regulator_npm2100_config *config = dev->config;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return linear_range_get_value(&boost_range, idx, volt_uv);
case NPM2100_SOURCE_LDOSW:
return linear_range_get_value(&ldosw_range, idx, volt_uv);
default:
return -EINVAL;
}
}
static int regulator_npm2100_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv)
{
const struct regulator_npm2100_config *config = dev->config;
uint16_t idx;
int ret;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
ret = linear_range_get_win_index(&boost_range, min_uv, max_uv, &idx);
if (ret == -EINVAL) {
return ret;
}
ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_VOUT, idx);
if (ret < 0) {
return ret;
}
/* Enable SW control of boost voltage */
return i2c_reg_write_byte_dt(&config->i2c, BOOST_VOUTSEL, 1U);
case NPM2100_SOURCE_LDOSW:
ret = linear_range_get_win_index(&ldosw_range, min_uv, max_uv, &idx);
if (ret == -EINVAL) {
return ret;
}
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_VOUT, idx);
default:
return -ENODEV;
}
}
static int regulator_npm2100_get_voltage(const struct device *dev, int32_t *volt_uv)
{
const struct regulator_npm2100_config *config = dev->config;
uint8_t idx;
int ret;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_VOUTSEL, &idx);
if (ret < 0) {
return ret;
}
if (idx == 1U) {
/* Voltage is selected by register value */
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_VOUT, &idx);
if (ret < 0) {
return ret;
}
return linear_range_get_value(&boost_range, idx, volt_uv);
}
/* Voltage is selected by VSET pin */
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_STATUS1, &idx);
if (ret < 0) {
return ret;
}
if ((idx & BOOST_STATUS1_VSET_MASK) == 0U) {
/* VSET low, voltage is selected by VSET0 register */
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_VSET0, &idx);
if (ret < 0) {
return ret;
}
return linear_range_get_value(&vset0_range, idx, volt_uv);
}
/* VSET high, voltage is selected by VSET1 register */
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_VSET1, &idx);
if (ret < 0) {
return ret;
}
return linear_range_group_get_value(vset1_ranges, ARRAY_SIZE(vset1_ranges), idx,
volt_uv);
case NPM2100_SOURCE_LDOSW:
ret = i2c_reg_read_byte_dt(&config->i2c, LDOSW_VOUT, &idx);
if (ret < 0) {
return ret;
}
return linear_range_get_value(&ldosw_range, idx, volt_uv);
default:
return -ENODEV;
}
}
static unsigned int regulator_npm2100_count_currents(const struct device *dev)
{
const struct regulator_npm2100_config *config = dev->config;
const struct regulator_npm2100_data *data = dev->data;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return linear_range_values_count(&boost_ocp_range);
case NPM2100_SOURCE_LDOSW:
if (data->ldsw_mode) {
return linear_range_group_values_count(ldsw_ocp_ranges,
ARRAY_SIZE(ldsw_ocp_ranges));
}
return linear_range_group_values_count(ldo_ocp_ranges, ARRAY_SIZE(ldo_ocp_ranges));
default:
return 0;
}
}
static int regulator_npm2100_list_currents(const struct device *dev, unsigned int idx,
int32_t *current_ua)
{
const struct regulator_npm2100_config *config = dev->config;
const struct regulator_npm2100_data *data = dev->data;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return linear_range_get_value(&boost_ocp_range, idx, current_ua);
case NPM2100_SOURCE_LDOSW:
if (data->ldsw_mode) {
return linear_range_group_get_value(
ldsw_ocp_ranges, ARRAY_SIZE(ldsw_ocp_ranges), idx, current_ua);
}
return linear_range_group_get_value(ldo_ocp_ranges, ARRAY_SIZE(ldo_ocp_ranges), idx,
current_ua);
default:
return -EINVAL;
}
}
static int regulator_npm2100_set_current(const struct device *dev, int32_t min_ua, int32_t max_ua)
{
const struct regulator_npm2100_config *config = dev->config;
const struct regulator_npm2100_data *data = dev->data;
uint16_t idx = 0;
uint8_t shift = 0;
int ret;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
ret = linear_range_get_win_index(&boost_ocp_range, min_ua, max_ua, &idx);
if (ret == -EINVAL) {
return ret;
}
if (idx == 1) {
return i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLSET, BIT(3));
}
return i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLCLR, BIT(3));
case NPM2100_SOURCE_LDOSW:
if (data->ldsw_mode) {
ret = linear_range_group_get_win_index(
ldsw_ocp_ranges, ARRAY_SIZE(ldsw_ocp_ranges), min_ua, max_ua, &idx);
idx = ldsw_ocp_lookup[idx];
shift = 4U;
} else {
ret = linear_range_group_get_win_index(
ldo_ocp_ranges, ARRAY_SIZE(ldo_ocp_ranges), min_ua, max_ua, &idx);
idx = ldo_ocp_lookup[idx];
}
if (ret == -EINVAL) {
return ret;
}
return i2c_reg_update_byte_dt(&config->i2c, LDOSW_PRGOCP, BIT_MASK(3) << shift,
idx << shift);
default:
return -ENODEV;
}
}
static int set_boost_mode(const struct device *dev, regulator_mode_t mode)
{
const struct regulator_npm2100_config *config = dev->config;
uint8_t reg;
/* Normal mode */
switch (mode & NPM2100_REG_OPER_MASK) {
case NPM2100_REG_OPER_AUTO:
reg = BOOST_OPER_MODE_AUTO;
break;
case NPM2100_REG_OPER_HP:
reg = BOOST_OPER_MODE_HP;
break;
case NPM2100_REG_OPER_LP:
reg = BOOST_OPER_MODE_LP;
break;
case NPM2100_REG_OPER_PASS:
reg = BOOST_OPER_MODE_PASS;
break;
case NPM2100_REG_OPER_NOHP:
reg = BOOST_OPER_MODE_NOHP;
break;
default:
return -ENOTSUP;
}
/* Configure DPS mode */
if ((mode & NPM2100_REG_DPS_MASK) != 0) {
uint8_t dps_val = (mode & NPM2100_REG_DPS_MASK) == NPM2100_REG_DPS_ALLOW
? BOOST_OPER_DPS_ALLOW
: BOOST_OPER_DPS_ALLOWLP;
reg |= FIELD_PREP(BOOST_OPER_DPS_MASK, dps_val);
}
/* Update mode and dps fields, but not dpstimer */
int ret = i2c_reg_update_byte_dt(&config->i2c, BOOST_OPER,
BOOST_OPER_MODE_MASK | BOOST_OPER_DPS_MASK, reg);
if (ret < 0) {
return ret;
}
/* Forced mode */
switch (mode & NPM2100_REG_FORCE_MASK) {
case 0U:
return 0;
case NPM2100_REG_FORCE_HP:
reg = BOOST_PIN_FORCE_HP;
break;
case NPM2100_REG_FORCE_LP:
reg = BOOST_PIN_FORCE_LP;
break;
case NPM2100_REG_FORCE_PASS:
reg = BOOST_PIN_FORCE_PASS;
break;
case NPM2100_REG_FORCE_NOHP:
reg = BOOST_PIN_FORCE_NOHP;
break;
default:
return -ENOTSUP;
}
/* Forced mode is only valid when gpio is configured */
if (config->mode_gpios.port == NULL) {
return -EINVAL;
}
return i2c_reg_write_byte_dt(&config->i2c, BOOST_PIN, reg);
}
static int get_boost_mode(const struct device *dev, regulator_mode_t *mode)
{
const struct regulator_npm2100_config *config = dev->config;
uint8_t reg;
int ret;
ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_STATUS0, &reg);
if (ret < 0) {
return ret;
}
switch (reg & BOOST_STATUS0_MODE_MASK) {
case BOOST_STATUS0_MODE_HP:
*mode = NPM2100_REG_OPER_HP;
break;
case BOOST_STATUS0_MODE_LP:
*mode = NPM2100_REG_OPER_LP;
break;
case BOOST_STATUS0_MODE_ULP:
*mode = NPM2100_REG_OPER_ULP;
break;
case BOOST_STATUS0_MODE_PT:
*mode = NPM2100_REG_OPER_PASS;
break;
case BOOST_STATUS0_MODE_DPS:
/* STATUS0 indicates whether DPS is enabled, regardless of ALLOW/ALLOWLP setting.
* BOOST_OPER_DPS_ALLOW chosen instead of creating new enum value.
*/
*mode = BOOST_OPER_DPS_ALLOW;
break;
default:
return -ENOTSUP;
}
return 0;
}
static int get_ldosw_mode(const struct device *dev, regulator_mode_t *mode)
{
const struct regulator_npm2100_config *config = dev->config;
uint8_t reg;
int ret;
ret = i2c_reg_read_byte_dt(&config->i2c, LDOSW_STATUS, &reg);
if (ret < 0) {
return ret;
}
*mode = 0U;
if (reg & LDOSW_STATUS_SW) {
*mode |= NPM2100_REG_LDSW_EN;
}
if (reg & LDOSW_STATUS_HP) {
*mode |= NPM2100_REG_OPER_HP;
} else if (reg & LDOSW_STATUS_ULP) {
*mode |= NPM2100_REG_OPER_ULP;
}
return 0;
}
static int set_ldosw_gpio_mode(const struct device *dev, uint8_t inact, uint8_t act, uint8_t ldsw)
{
const struct regulator_npm2100_config *config = dev->config;
int ret;
ret = i2c_reg_update_byte_dt(&config->i2c, LDOSW_GPIO, LDOSW_GPIO_PINACT_MASK, inact | act);
if (ret < 0) {
return ret;
}
/* Set operating mode to pin control */
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_SEL, LDOSW_SEL_OPER_PIN | ldsw);
}
static int set_ldosw_mode(const struct device *dev, regulator_mode_t mode)
{
const struct regulator_npm2100_config *config = dev->config;
struct regulator_npm2100_data *data = dev->data;
uint8_t ldsw = mode & NPM2100_REG_LDSW_EN;
uint8_t oper = mode & NPM2100_REG_OPER_MASK;
uint8_t force = mode & NPM2100_REG_FORCE_MASK;
/* Save load switch state, needed for OCP configuration */
data->ldsw_mode = ldsw != 0;
if (force == 0U) {
/* SW control of mode */
switch (oper) {
case NPM2100_REG_OPER_AUTO:
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_SEL,
LDOSW_SEL_OPER_AUTO | ldsw);
case NPM2100_REG_OPER_ULP:
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_SEL,
LDOSW_SEL_OPER_ULP | ldsw);
case NPM2100_REG_OPER_HP:
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_SEL,
LDOSW_SEL_OPER_HP | ldsw);
default:
return -ENOTSUP;
}
}
/* Forced mode is only valid when gpio is configured */
if (config->mode_gpios.port == NULL) {
return -EINVAL;
}
switch (oper | force) {
case NPM2100_REG_OPER_OFF | NPM2100_REG_FORCE_ULP:
return set_ldosw_gpio_mode(dev, LDOSW_GPIO_PININACT_OFF, LDOSW_GPIO_PINACT_ULP,
ldsw);
case NPM2100_REG_OPER_OFF | NPM2100_REG_FORCE_HP:
return set_ldosw_gpio_mode(dev, LDOSW_GPIO_PININACT_OFF, LDOSW_GPIO_PINACT_HP,
ldsw);
case NPM2100_REG_OPER_ULP | NPM2100_REG_FORCE_HP:
return set_ldosw_gpio_mode(dev, LDOSW_GPIO_PININACT_ULP, LDOSW_GPIO_PINACT_HP,
ldsw);
default:
return -ENOTSUP;
}
}
static int regulator_npm2100_set_mode(const struct device *dev, regulator_mode_t mode)
{
const struct regulator_npm2100_config *config = dev->config;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return set_boost_mode(dev, mode);
case NPM2100_SOURCE_LDOSW:
return set_ldosw_mode(dev, mode);
default:
return -ENOTSUP;
}
}
static int regulator_npm2100_get_mode(const struct device *dev, regulator_mode_t *mode)
{
const struct regulator_npm2100_config *config = dev->config;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return get_boost_mode(dev, mode);
case NPM2100_SOURCE_LDOSW:
return get_ldosw_mode(dev, mode);
default:
return -ENOTSUP;
}
}
static int regulator_npm2100_enable(const struct device *dev)
{
const struct regulator_npm2100_config *config = dev->config;
if (config->source != NPM2100_SOURCE_LDOSW) {
return 0;
}
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_ENABLE, 1U);
}
static int regulator_npm2100_disable(const struct device *dev)
{
const struct regulator_npm2100_config *config = dev->config;
if (config->source != NPM2100_SOURCE_LDOSW) {
return 0;
}
return i2c_reg_write_byte_dt(&config->i2c, LDOSW_ENABLE, 0U);
}
static int init_pin_ctrl(const struct device *dev, const struct gpio_dt_spec *spec)
{
const struct regulator_npm2100_config *config = dev->config;
if (spec->port == NULL) {
return 0;
}
int ret = gpio_pin_configure_dt(spec, GPIO_INPUT);
if (ret != 0) {
return ret;
}
uint8_t pin = spec->pin << 1U;
uint8_t offset = ((spec->dt_flags & GPIO_ACTIVE_LOW) != 0U) ? 0U : 1U;
switch (config->source) {
case NPM2100_SOURCE_BOOST:
return i2c_reg_write_byte_dt(&config->i2c, BOOST_GPIO, pin + offset + 1U);
case NPM2100_SOURCE_LDOSW:
return i2c_reg_update_byte_dt(&config->i2c, LDOSW_GPIO, LDOSW_GPIO_PIN_MASK,
pin + offset);
default:
return -ENODEV;
}
}
static int regulator_npm2100_dvs_state_set(const struct device *dev, regulator_dvs_state_t state)
{
const struct regulator_npm2100_pconfig *pconfig = dev->config;
const struct gpio_dt_spec *spec;
int ret;
for (size_t idx = 0U; idx < 2U; 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;
}
static int regulator_npm2100_ship_mode(const struct device *dev)
{
const struct regulator_npm2100_pconfig *pconfig = dev->config;
/* Ensure shiphold button is enabled so that wakeup will work */
int ret = i2c_reg_write_byte_dt(&pconfig->i2c, RESET_WRITESTICKY, 0);
if (ret < 0) {
return ret;
}
ret = i2c_reg_write_byte_dt(&pconfig->i2c, RESET_STROBESTICKY, 1U);
if (ret < 0) {
return ret;
}
return i2c_reg_write_byte_dt(&pconfig->i2c, SHIP_TASK_SHIP, 1U);
}
static DEVICE_API(regulator_parent, parent_api) = {
.dvs_state_set = regulator_npm2100_dvs_state_set,
.ship_mode = regulator_npm2100_ship_mode,
};
static int regulator_npm2100_common_init(const struct device *dev)
{
const struct regulator_npm2100_pconfig *pconfig = dev->config;
const struct gpio_dt_spec *spec;
int ret;
for (size_t idx = 0U; idx < 2U; 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 regulator_npm2100_init(const struct device *dev)
{
const struct regulator_npm2100_config *config = dev->config;
int ret;
if (!i2c_is_ready_dt(&config->i2c)) {
return -ENODEV;
}
/* Configure GPIO pin control */
ret = init_pin_ctrl(dev, &config->mode_gpios);
if (ret != 0) {
return ret;
}
/* BOOST is always enabled */
if (config->source == NPM2100_SOURCE_BOOST) {
ret = i2c_reg_write_byte_dt(
&config->i2c, BOOST_OPER,
FIELD_PREP(BOOST_OPER_DPSTIMER_MASK, config->dps_timer));
if (ret < 0) {
return ret;
}
ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_LIMIT, config->dps_pulse_limit);
if (ret < 0) {
return ret;
}
return regulator_common_init(dev, true);
}
/* Configure LDOSW behavior during watchdog reset */
if (config->ldosw_wd_reset) {
ret = i2c_reg_write_byte_dt(&config->i2c, RESET_ALTCONFIG,
RESET_ALTCONFIG_LDOSW_OFF);
if (ret < 0) {
return ret;
}
}
/* Get enable state for LDOSW */
uint8_t enabled;
ret = i2c_reg_read_byte_dt(&config->i2c, LDOSW_ENABLE, &enabled);
if (ret < 0) {
return ret;
}
return regulator_common_init(dev, enabled != 0U);
}
static DEVICE_API(regulator, api) = {
.enable = regulator_npm2100_enable,
.disable = regulator_npm2100_disable,
.count_voltages = regulator_npm2100_count_voltages,
.list_voltage = regulator_npm2100_list_voltage,
.set_voltage = regulator_npm2100_set_voltage,
.get_voltage = regulator_npm2100_get_voltage,
.count_current_limits = regulator_npm2100_count_currents,
.list_current_limit = regulator_npm2100_list_currents,
.set_current_limit = regulator_npm2100_set_current,
.set_mode = regulator_npm2100_set_mode,
.get_mode = regulator_npm2100_get_mode};
#define REGULATOR_NPM2100_DEFINE(node_id, id, _source) \
static struct regulator_npm2100_data data_##id; \
\
static const struct regulator_npm2100_config config_##id = { \
.common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \
.i2c = I2C_DT_SPEC_GET(DT_GPARENT(node_id)), \
.source = _source, \
.mode_gpios = GPIO_DT_SPEC_GET_OR(node_id, mode_gpios, {0}), \
.ldosw_wd_reset = DT_PROP(node_id, ldosw_wd_reset), \
.dps_timer = DT_ENUM_IDX_OR(node_id, dps_timer_us, 0), \
.dps_pulse_limit = DT_PROP_OR(node_id, dps_pulse_limit, 0)}; \
BUILD_ASSERT(DT_PROP_OR(node_id, dps_pulse_limit, 0) >= 3 || \
DT_PROP_OR(node_id, dps_pulse_limit, 0) == 0, \
"Invalid dps_pulse_limit value"); \
\
DEVICE_DT_DEFINE(node_id, regulator_npm2100_init, NULL, &data_##id, &config_##id, \
POST_KERNEL, CONFIG_REGULATOR_NPM2100_INIT_PRIORITY, &api);
#define REGULATOR_NPM2100_DEFINE_COND(inst, child, source) \
COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \
(REGULATOR_NPM2100_DEFINE(DT_INST_CHILD(inst, child), child##inst, source)), \
())
#define REGULATOR_NPM2100_DEFINE_ALL(inst) \
static const struct regulator_npm2100_pconfig config_##inst = { \
.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \
.dvs_state_pins = {GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, dvs_gpios, 0, {0}), \
GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, dvs_gpios, 1, {0})}}; \
\
DEVICE_DT_INST_DEFINE(inst, regulator_npm2100_common_init, NULL, NULL, &config_##inst, \
POST_KERNEL, CONFIG_REGULATOR_NPM2100_COMMON_INIT_PRIORITY, \
&parent_api); \
\
REGULATOR_NPM2100_DEFINE_COND(inst, boost, NPM2100_SOURCE_BOOST) \
REGULATOR_NPM2100_DEFINE_COND(inst, ldosw, NPM2100_SOURCE_LDOSW)
DT_INST_FOREACH_STATUS_OKAY(REGULATOR_NPM2100_DEFINE_ALL)

View file

@ -0,0 +1,81 @@
# Copyright (c), 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
description: |
Nordic nPM2100 PMIC
The PMIC has one boost converter and one LDO/LDSW.
The regulators need to be defined as child nodes,
strictly following the BOOST, LDOSW node names.
For example:
pmic@74 {
reg = <0x74>;
...
regulators {
compatible = "nordic,npm2100-regulator";
BOOST {
/* all properties for BOOST */
};
LDOSW {
/* all properties for LDOSW */
};
};
};
compatible: "nordic,npm2100-regulator"
include: base.yaml
properties:
dvs-gpios:
type: phandle-array
description: |
List of SOC GPIOs connected to PMIC GPIOs.
Set_dvs_mode will drive these pins as follows:
DVS mode 1 will enable the first pin
DVS mode 2 will enable the second pin
The effect of the mode change is defined by the mode-gpios
fields for each of the regulator blocks.
child-binding:
include:
- name: regulator.yaml
property-allowlist:
- regulator-always-on
- regulator-boot-on
- regulator-min-microvolt
- regulator-max-microvolt
- regulator-init-microvolt
- regulator-allowed-modes
- regulator-initial-mode
- regulator-min-microamp
- regulator-max-microamp
- regulator-init-microamp
properties:
mode-gpios:
type: phandle-array
description: |
Regulator mode controlled by specified regulator GPIO pin.
ldosw-wd-reset:
type: boolean
description: |
LDOSW turned off by watchdog reset.
dps-timer-us:
type: int
description: |
Interval between DPS refresh cycles in microseconds.
enum:
- 100
- 200
- 400
- 800
dps-pulse-limit:
type: int
description: |
DPS coil pulse limit per refresh cycle.

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NPM2100_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NPM2100_H_
/**
* @defgroup regulator_npm2100 NPM2100 Devicetree helpers.
* @ingroup regulator_interface
* @{
*/
/**
* @name NPM2100 Regulator modes
* @{
*/
/* Load switch selection, applies to LDOSW only */
#define NPM2100_REG_LDSW_EN 0x01U
/* DPS modes applies to BOOST only */
#define NPM2100_REG_DPS_MASK 0x03U
#define NPM2100_REG_DPS_ALLOW 0x01U
#define NPM2100_REG_DPS_ALLOWLP 0x02U
/* Operating mode */
#define NPM2100_REG_OPER_MASK 0x1CU
#define NPM2100_REG_OPER_AUTO 0x00U
#define NPM2100_REG_OPER_HP 0x04U
#define NPM2100_REG_OPER_LP 0x08U
#define NPM2100_REG_OPER_ULP 0x0CU
#define NPM2100_REG_OPER_PASS 0x10U
#define NPM2100_REG_OPER_NOHP 0x14U
#define NPM2100_REG_OPER_OFF 0x18U
/* Forced mode when GPIO active */
#define NPM2100_REG_FORCE_MASK 0xE0U
#define NPM2100_REG_FORCE_HP 0x20U
#define NPM2100_REG_FORCE_LP 0x40U
#define NPM2100_REG_FORCE_ULP 0x60U
#define NPM2100_REG_FORCE_PASS 0x80U
#define NPM2100_REG_FORCE_NOHP 0xA0U
/** @} */
/** @} */
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NPM2100_H_*/

View file

@ -107,3 +107,15 @@ mpm54304@7 {
BUCK3 {}; BUCK3 {};
BUCK4 {}; BUCK4 {};
}; };
npm2100@8 {
compatible = "nordic,npm2100";
reg = <0x8>;
regulators {
compatible = "nordic,npm2100-regulator";
BOOST {};
LDOSW {};
};
};