drivers: regulator: adp5360: initial version

Add a new regulator driver for Analog Devices ADP5360. While it is a MFD
device, only support for BUCK/BUCKBOOST regulators is added in this
patch.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2023-05-22 19:07:23 +02:00 committed by Fabio Baltieri
commit e05df8faf1
8 changed files with 475 additions and 0 deletions

View file

@ -4,6 +4,7 @@
zephyr_library()
zephyr_library_sources(regulator_common.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_ADP5360 regulator_adp5360.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_FAKE regulator_fake.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_FIXED regulator_fixed.c)
zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1100 regulator_npm1100.c)

View file

@ -27,6 +27,7 @@ module = REGULATOR
module-str = regulator
source "subsys/logging/Kconfig.template.log_config"
source "drivers/regulator/Kconfig.adp5360"
source "drivers/regulator/Kconfig.fake"
source "drivers/regulator/Kconfig.fixed"
source "drivers/regulator/Kconfig.npm1100"

View file

@ -0,0 +1,28 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX -License-Identifier: Apache-2.0
config REGULATOR_ADP5360
bool "ADP5360 PMIC regulator driver"
default y
depends on DT_HAS_ADI_ADP5360_REGULATOR_ENABLED
select I2C
help
Enable the Analog Devices ADP5360 PMIC regulator driver
if REGULATOR_ADP5360
config REGULATOR_ADP5360_COMMON_INIT_PRIORITY
int "ADP5360 regulator driver init priority (common part)"
default 75
help
Init priority for the Analog Devices ADP5360 regulator driver (common
part). It must be greater than I2C init priority.
config REGULATOR_ADP5360_INIT_PRIORITY
int "ADP5360 regulator driver init priority"
default 76
help
Init priority for the Analog Devices ADP5360 regulator driver. It must
be greater than REGULATOR_ADP5360_COMMON_INIT_PRIORITY.
endif

View file

@ -0,0 +1,308 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT adi_adp5360_regulator
#include <errno.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/dt-bindings/regulator/adp5360.h>
#include <zephyr/sys/linear_range.h>
#include <zephyr/sys/util.h>
/* ADP5360 regulator related registers */
#define ADP5360_BUCK_CFG 0x29U
#define ADP5360_BUCK_OUTPUT 0x2AU
#define ADP5360_BUCKBST_CFG 0x2BU
#define ADP5360_BUCKBST_OUTPUT 0x2CU
/* Buck/boost configure register. */
#define ADP5360_BUCK_CFG_SS_MSK GENMASK(7, 6)
#define ADP5360_BUCK_CFG_SS_POS 6U
#define ADP5360_BUCK_CFG_BST_ILIM_MSK GENMASK(5, 3)
#define ADP5360_BUCK_CFG_BST_ILIM_POS 3U
#define ADP5360_BUCK_CFG_BUCK_ILIM_MSK GENMASK(5, 3)
#define ADP5360_BUCK_CFG_BUCK_ILIM_POS 3U
#define ADP5360_BUCK_CFG_BUCK_MODE_MSK BIT(3)
#define ADP5360_BUCK_CFG_BUCK_MODE_POS 3U
#define ADP5360_BUCK_CFG_STP_MSK BIT(2)
#define ADP5360_BUCK_CFG_DISCHG_MSK BIT(1)
#define ADP5360_BUCK_CFG_EN_MSK BIT(0)
/* Buck/boost output voltage setting register. */
#define ADP5360_BUCK_OUTPUT_VOUT_MSK GENMASK(5, 0)
#define ADP5360_BUCK_OUTPUT_VOUT_POS 0U
#define ADP5360_BUCK_OUTPUT_DLY_MSK GENMASK(7, 6)
#define ADP5360_BUCK_OUTPUT_DLY_POS 6U
struct regulator_adp5360_desc {
uint8_t cfg_reg;
uint8_t out_reg;
bool has_modes;
const struct linear_range *ranges;
uint8_t nranges;
};
static const struct linear_range buck_ranges[] = {
LINEAR_RANGE_INIT(600000, 50000U, 0x0U, 0x3FU),
};
static const struct regulator_adp5360_desc buck_desc = {
.cfg_reg = ADP5360_BUCK_CFG,
.out_reg = ADP5360_BUCK_OUTPUT,
.has_modes = true,
.ranges = buck_ranges,
.nranges = ARRAY_SIZE(buck_ranges),
};
static const struct linear_range buckboost_ranges[] = {
LINEAR_RANGE_INIT(1800000, 100000U, 0x0U, 0x0BU),
LINEAR_RANGE_INIT(2950000, 50000U, 0xCU, 0x3FU),
};
static const struct regulator_adp5360_desc buckboost_desc = {
.cfg_reg = ADP5360_BUCKBST_CFG,
.out_reg = ADP5360_BUCKBST_OUTPUT,
.has_modes = false,
.ranges = buckboost_ranges,
.nranges = ARRAY_SIZE(buckboost_ranges),
};
struct regulator_adp5360_config {
struct regulator_common_config common;
struct i2c_dt_spec i2c;
const struct regulator_adp5360_desc *desc;
int8_t dly_idx;
int8_t ss_idx;
int8_t ilim_idx;
bool stp_en;
bool dis_en;
};
struct regulator_adp5360_data {
struct regulator_common_data data;
};
static unsigned int regulator_adp5360_count_voltages(const struct device *dev)
{
const struct regulator_adp5360_config *config = dev->config;
return linear_range_group_values_count(config->desc->ranges, config->desc->nranges);
}
static int regulator_adp5360_list_voltage(const struct device *dev, unsigned int idx,
int32_t *volt_uv)
{
const struct regulator_adp5360_config *config = dev->config;
return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, idx,
volt_uv);
}
static int regulator_adp5360_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv)
{
const struct regulator_adp5360_config *config = dev->config;
uint16_t idx;
int ret;
ret = linear_range_group_get_win_index(config->desc->ranges, config->desc->nranges, min_uv,
max_uv, &idx);
if (ret == -EINVAL) {
return ret;
}
return i2c_reg_update_byte_dt(&config->i2c, config->desc->out_reg,
ADP5360_BUCK_OUTPUT_VOUT_MSK,
(uint8_t)idx << ADP5360_BUCK_OUTPUT_VOUT_POS);
}
static int regulator_adp5360_get_voltage(const struct device *dev, int32_t *volt_uv)
{
const struct regulator_adp5360_config *config = dev->config;
int ret;
uint8_t raw_reg;
ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &raw_reg);
if (ret < 0) {
return ret;
}
raw_reg = (raw_reg & ADP5360_BUCK_OUTPUT_VOUT_MSK) >> ADP5360_BUCK_OUTPUT_VOUT_POS;
return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, raw_reg,
volt_uv);
}
static int regulator_adp5360_set_mode(const struct device *dev, regulator_mode_t mode)
{
const struct regulator_adp5360_config *config = dev->config;
if (!config->desc->has_modes || (mode > ADP5360_MODE_PWM)) {
return -ENOTSUP;
}
return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg,
ADP5360_BUCK_CFG_BUCK_MODE_MSK,
mode << ADP5360_BUCK_CFG_BUCK_MODE_POS);
}
static int regulator_adp5360_get_mode(const struct device *dev, regulator_mode_t *mode)
{
const struct regulator_adp5360_config *config = dev->config;
uint8_t val;
int ret;
if (!config->desc->has_modes) {
return -ENOTSUP;
}
ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val);
if (ret < 0) {
return ret;
}
*mode = (val & ADP5360_BUCK_CFG_BUCK_MODE_MSK) >> ADP5360_BUCK_CFG_BUCK_MODE_POS;
return 0;
}
static int regulator_adp5360_enable(const struct device *dev)
{
const struct regulator_adp5360_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK,
1U);
}
static int regulator_adp5360_disable(const struct device *dev)
{
const struct regulator_adp5360_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK,
0U);
}
static int regulator_adp5360_init(const struct device *dev)
{
const struct regulator_adp5360_config *config = dev->config;
int ret;
uint8_t val, nval, msk;
regulator_common_data_init(dev);
if (!i2c_is_ready_dt(&config->i2c)) {
return -ENODEV;
}
/* apply optional delay */
msk = 0U;
nval = 0U;
ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &val);
if (ret < 0) {
return ret;
}
if (config->dly_idx >= 0) {
msk |= ADP5360_BUCK_OUTPUT_DLY_MSK;
nval |= ((uint8_t)config->dly_idx << ADP5360_BUCK_OUTPUT_DLY_POS) &
ADP5360_BUCK_OUTPUT_DLY_MSK;
}
if (msk != 0U) {
ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->out_reg,
(val & ~msk) | nval);
if (ret < 0) {
return ret;
}
}
/* apply optional initial configuration */
msk = 0U;
nval = 0U;
ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val);
if (ret < 0) {
return ret;
}
if (config->ss_idx >= 0) {
msk |= ADP5360_BUCK_CFG_SS_MSK;
nval |= ((uint8_t)config->ss_idx << ADP5360_BUCK_CFG_SS_POS) &
ADP5360_BUCK_CFG_SS_MSK;
}
if (config->ilim_idx >= 0) {
if (config->desc->has_modes) {
msk |= ADP5360_BUCK_CFG_BUCK_ILIM_MSK;
nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BUCK_ILIM_POS) &
ADP5360_BUCK_CFG_BUCK_ILIM_MSK;
} else {
msk |= ADP5360_BUCK_CFG_BST_ILIM_MSK;
nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BST_ILIM_POS) &
ADP5360_BUCK_CFG_BST_ILIM_MSK;
}
}
if (config->stp_en) {
msk |= ADP5360_BUCK_CFG_STP_MSK;
nval |= ADP5360_BUCK_CFG_STP_MSK;
}
if (config->dis_en) {
msk |= ADP5360_BUCK_CFG_DISCHG_MSK;
nval |= ADP5360_BUCK_CFG_DISCHG_MSK;
}
if (msk != 0U) {
ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->cfg_reg,
(val & ~msk) | nval);
if (ret < 0) {
return ret;
}
}
return regulator_common_init(dev, (val & ADP5360_BUCK_CFG_EN_MSK) != 0U);
}
static const struct regulator_driver_api api = {
.enable = regulator_adp5360_enable,
.disable = regulator_adp5360_disable,
.count_voltages = regulator_adp5360_count_voltages,
.list_voltage = regulator_adp5360_list_voltage,
.set_voltage = regulator_adp5360_set_voltage,
.get_voltage = regulator_adp5360_get_voltage,
.set_mode = regulator_adp5360_set_mode,
.get_mode = regulator_adp5360_get_mode,
};
#define REGULATOR_ADP5360_DEFINE(node_id, id, name) \
static struct regulator_adp5360_data data_##id; \
\
static const struct regulator_adp5360_config config_##id = { \
.common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \
.i2c = I2C_DT_SPEC_GET(DT_GPARENT(node_id)), \
.desc = &name##_desc, \
.dly_idx = DT_ENUM_IDX_OR(node_id, adi_switch_delay_us, -1), \
.ss_idx = DT_ENUM_IDX_OR(node_id, adi_soft_start_ms, -1), \
.ilim_idx = DT_ENUM_IDX_OR(node_id, adi_ilim_milliamp, -1), \
.stp_en = DT_PROP(node_id, adi_enable_stop_pulse), \
.dis_en = DT_PROP(node_id, adi_enable_output_discharge), \
}; \
\
DEVICE_DT_DEFINE(node_id, regulator_adp5360_init, NULL, &data_##id, &config_##id, \
POST_KERNEL, CONFIG_REGULATOR_ADP5360_INIT_PRIORITY, &api);
#define REGULATOR_ADP5360_DEFINE_COND(inst, child) \
COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \
(REGULATOR_ADP5360_DEFINE(DT_INST_CHILD(inst, child), child##inst, child)), \
())
#define REGULATOR_ADP5360_DEFINE_ALL(inst) \
REGULATOR_ADP5360_DEFINE_COND(inst, buck) \
REGULATOR_ADP5360_DEFINE_COND(inst, buckboost)
DT_INST_FOREACH_STATUS_OKAY(REGULATOR_ADP5360_DEFINE_ALL)