46bbe052d3
This add regulator driver for Smartbond DA1469X SOC. Driver can control VDD, V14, V18, V18P, V30 rails, full voltage range supported by SOC is covered. For VDD, V14, V18, V18P DCDC can be configured. Special VDD_CLAMP (always on) and VDD_SLEPP are added to allow configuration of VDD in sleep modes. Signed-off-by: Jerzy Kasenberg <jerzy.kasenberg@codecoup.pl>
416 lines
14 KiB
C
416 lines
14 KiB
C
/*
|
|
* Copyright 2023 Renesas Electronics Corporation
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_smartbond_regulator
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <zephyr/drivers/regulator.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/sys/linear_range.h>
|
|
#include <DA1469xAB.h>
|
|
|
|
LOG_MODULE_REGISTER(regulator_da1469x, CONFIG_REGULATOR_LOG_LEVEL);
|
|
|
|
#define DCDC_REQUESTED (DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_HV_Msk |\
|
|
DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_LV_Msk)
|
|
|
|
#define DA1469X_LDO_3V0_MODE_VBAT BIT(8)
|
|
#define DA1469X_LDO_3V0_MODE_VBUS BIT(9)
|
|
|
|
static const struct linear_range curren_ranges[] = {
|
|
LINEAR_RANGE_INIT(30000, 30000, 0, 31),
|
|
};
|
|
|
|
static const struct linear_range vdd_clamp_ranges[] = {
|
|
LINEAR_RANGE_INIT(706000, 0, 15, 15),
|
|
LINEAR_RANGE_INIT(798000, 0, 14, 14),
|
|
LINEAR_RANGE_INIT(828000, 0, 13, 13),
|
|
LINEAR_RANGE_INIT(861000, 0, 11, 11),
|
|
LINEAR_RANGE_INIT(862000, 0, 12, 12),
|
|
LINEAR_RANGE_INIT(889000, 0, 10, 10),
|
|
LINEAR_RANGE_INIT(918000, 0, 9, 9),
|
|
LINEAR_RANGE_INIT(946000, 0, 3, 3),
|
|
LINEAR_RANGE_INIT(952000, 0, 8, 8),
|
|
LINEAR_RANGE_INIT(978000, 0, 2, 2),
|
|
LINEAR_RANGE_INIT(1005000, 0, 1, 1),
|
|
LINEAR_RANGE_INIT(1030000, 0, 7, 7),
|
|
LINEAR_RANGE_INIT(1037000, 0, 0, 0),
|
|
LINEAR_RANGE_INIT(1058000, 0, 6, 6),
|
|
LINEAR_RANGE_INIT(1089000, 0, 5, 5),
|
|
LINEAR_RANGE_INIT(1120000, 0, 4, 4),
|
|
};
|
|
|
|
static const struct linear_range vdd_ranges[] = {
|
|
LINEAR_RANGE_INIT(900000, 100000, 0, 3),
|
|
};
|
|
|
|
static const struct linear_range vdd_sleep_ranges[] = {
|
|
LINEAR_RANGE_INIT(750000, 50000, 0, 3),
|
|
};
|
|
|
|
static const struct linear_range v14_ranges[] = {
|
|
LINEAR_RANGE_INIT(1200000, 50000, 0, 7),
|
|
};
|
|
|
|
static const struct linear_range v30_ranges[] = {
|
|
LINEAR_RANGE_INIT(3000000, 300000, 0, 1),
|
|
};
|
|
|
|
static const struct linear_range v18_ranges[] = {
|
|
LINEAR_RANGE_INIT(1200000, 600000, 0, 1),
|
|
};
|
|
|
|
static const struct linear_range v18p_ranges[] = {
|
|
LINEAR_RANGE_INIT(1800000, 0, 0, 0),
|
|
};
|
|
|
|
enum da1469x_rail {
|
|
VDD_CLAMP,
|
|
VDD_SLEEP,
|
|
VDD,
|
|
V14,
|
|
V18,
|
|
V18P,
|
|
V30,
|
|
};
|
|
|
|
struct regulator_da1469x_desc {
|
|
const struct linear_range *voltage_ranges;
|
|
const struct linear_range *current_ranges;
|
|
uint8_t voltage_range_count;
|
|
/* Bit from POWER_CTRL_REG that can be used for enabling rail */
|
|
uint32_t enable_mask;
|
|
uint32_t voltage_idx_mask;
|
|
volatile uint32_t *dcdc_register;
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc vdd_desc = {
|
|
.voltage_ranges = vdd_ranges,
|
|
.current_ranges = curren_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(vdd_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_CORE_ENABLE_Msk,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk,
|
|
.dcdc_register = &DCDC->DCDC_VDD_REG,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc vdd_sleep_desc = {
|
|
.voltage_ranges = vdd_sleep_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(vdd_sleep_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_CORE_RET_ENABLE_SLEEP_Msk,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_SLEEP_LEVEL_Msk,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc vdd_clamp_desc = {
|
|
.voltage_ranges = vdd_clamp_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(vdd_clamp_ranges),
|
|
.enable_mask = 0,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_CLAMP_LEVEL_Msk,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc v14_desc = {
|
|
.voltage_ranges = v14_ranges,
|
|
.current_ranges = curren_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(v14_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V14_LEVEL_Msk,
|
|
.dcdc_register = &DCDC->DCDC_V14_REG,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc v18_desc = {
|
|
.voltage_ranges = v18_ranges,
|
|
.current_ranges = curren_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(v18_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_1V8_ENABLE_Msk |
|
|
CRG_TOP_POWER_CTRL_REG_LDO_1V8_RET_ENABLE_SLEEP_Msk,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V18_LEVEL_Msk,
|
|
.dcdc_register = &DCDC->DCDC_V18_REG,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc v18p_desc = {
|
|
.voltage_ranges = v18p_ranges,
|
|
.current_ranges = curren_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(v18p_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_1V8P_ENABLE_Msk |
|
|
CRG_TOP_POWER_CTRL_REG_LDO_1V8P_RET_ENABLE_SLEEP_Msk,
|
|
.voltage_idx_mask = 0,
|
|
.dcdc_register = &DCDC->DCDC_V18P_REG,
|
|
};
|
|
|
|
static const struct regulator_da1469x_desc v30_desc = {
|
|
.voltage_ranges = v30_ranges,
|
|
.voltage_range_count = ARRAY_SIZE(v30_ranges),
|
|
.enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_3V0_RET_ENABLE_SLEEP_Msk |
|
|
CRG_TOP_POWER_CTRL_REG_LDO_3V0_MODE_Msk,
|
|
.voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V30_LEVEL_Msk,
|
|
};
|
|
|
|
#define DA1469X_LDO_VDD_CLAMP_RET 0
|
|
#define DA1469X_LDO_VDD_SLEEP_RET 0
|
|
#define DA1469X_LDO_VDD_RET CRG_TOP_POWER_CTRL_REG_LDO_CORE_RET_ENABLE_SLEEP_Msk
|
|
#define DA1469X_LDO_V14_RET 0
|
|
#define DA1469X_LDO_V18_RET CRG_TOP_POWER_CTRL_REG_LDO_1V8_RET_ENABLE_SLEEP_Msk
|
|
#define DA1469X_LDO_V18P_RET CRG_TOP_POWER_CTRL_REG_LDO_1V8P_RET_ENABLE_SLEEP_Msk
|
|
#define DA1469X_LDO_V30_RET CRG_TOP_POWER_CTRL_REG_LDO_3V0_RET_ENABLE_SLEEP_Msk
|
|
|
|
struct regulator_da1469x_config {
|
|
struct regulator_common_config common;
|
|
enum da1469x_rail rail;
|
|
const struct regulator_da1469x_desc *desc;
|
|
uint32_t power_bits;
|
|
uint32_t dcdc_bits;
|
|
};
|
|
|
|
struct regulator_da1469x_data {
|
|
struct regulator_common_data common;
|
|
};
|
|
|
|
static int regulator_da1469x_enable(const struct device *dev)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
uint32_t reg_val;
|
|
|
|
if (config->desc->enable_mask & config->power_bits) {
|
|
reg_val = CRG_TOP->POWER_CTRL_REG & ~(config->desc->enable_mask);
|
|
reg_val |= config->power_bits & config->desc->enable_mask;
|
|
CRG_TOP->POWER_CTRL_REG |= reg_val;
|
|
}
|
|
|
|
if (config->desc->dcdc_register) {
|
|
reg_val = *config->desc->dcdc_register &
|
|
~(DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk |
|
|
DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk);
|
|
reg_val |= config->dcdc_bits;
|
|
*config->desc->dcdc_register = reg_val;
|
|
}
|
|
|
|
/*
|
|
* Enable DCDC if:
|
|
* 1. it was not already enabled, and
|
|
* 2. VBAT is above minimal value
|
|
* 3. Just turned on rail requested DCDC
|
|
*/
|
|
if (((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) == 0) &&
|
|
(CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_COMP_VBAT_HIGH_Msk) &&
|
|
config->dcdc_bits & DCDC_REQUESTED) {
|
|
DCDC->DCDC_CTRL1_REG |= DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int regulator_da1469x_disable(const struct device *dev)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
uint32_t reg_val;
|
|
|
|
if (config->desc->enable_mask & config->power_bits) {
|
|
CRG_TOP->POWER_CTRL_REG &= ~(config->desc->enable_mask &
|
|
config->power_bits);
|
|
}
|
|
if (config->desc->dcdc_register) {
|
|
reg_val = *config->desc->dcdc_register &
|
|
~(DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk |
|
|
DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk);
|
|
*config->desc->dcdc_register = reg_val;
|
|
}
|
|
|
|
/* Turn off DCDC if it's no longer requested by any rail */
|
|
if ((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) &&
|
|
(DCDC->DCDC_VDD_REG & DCDC_REQUESTED) == 0 &&
|
|
(DCDC->DCDC_V14_REG & DCDC_REQUESTED) == 0 &&
|
|
(DCDC->DCDC_V18_REG & DCDC_REQUESTED) == 0 &&
|
|
(DCDC->DCDC_V18P_REG & DCDC_REQUESTED) == 0) {
|
|
DCDC->DCDC_CTRL1_REG &= ~DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int regulator_da1469x_count_voltages(const struct device *dev)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
|
|
return linear_range_group_values_count(config->desc->voltage_ranges,
|
|
config->desc->voltage_range_count);
|
|
}
|
|
|
|
static int regulator_da1469x_list_voltage(const struct device *dev,
|
|
unsigned int idx,
|
|
int32_t *volt_uv)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
|
|
if (config->desc->voltage_ranges) {
|
|
return linear_range_group_get_value(config->desc->voltage_ranges,
|
|
config->desc->voltage_range_count,
|
|
idx, volt_uv);
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int regulator_da1469x_set_voltage(const struct device *dev, int32_t min_uv,
|
|
int32_t max_uv)
|
|
{
|
|
int ret;
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
uint16_t idx;
|
|
uint32_t mask;
|
|
|
|
ret = linear_range_group_get_win_index(config->desc->voltage_ranges,
|
|
config->desc->voltage_range_count,
|
|
min_uv, max_uv, &idx);
|
|
|
|
if (ret == 0) {
|
|
mask = config->desc->voltage_idx_mask;
|
|
/*
|
|
* Mask is 0 for V18.
|
|
* Setting value 1.8V is accepted since range is valid and already checked.
|
|
*/
|
|
if (mask) {
|
|
CRG_TOP->POWER_CTRL_REG = (CRG_TOP->POWER_CTRL_REG & ~mask) |
|
|
FIELD_PREP(mask, idx);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int regulator_da1469x_get_voltage(const struct device *dev,
|
|
int32_t *volt_uv)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
uint16_t idx;
|
|
|
|
if (config->desc->voltage_idx_mask) {
|
|
idx = FIELD_GET(CRG_TOP->POWER_CTRL_REG, config->desc->voltage_idx_mask);
|
|
} else {
|
|
idx = 0;
|
|
}
|
|
|
|
return linear_range_group_get_value(config->desc->voltage_ranges,
|
|
config->desc->voltage_range_count, idx, volt_uv);
|
|
}
|
|
|
|
static int regulator_da1469x_set_current_limit(const struct device *dev,
|
|
int32_t min_ua, int32_t max_ua)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
int ret;
|
|
uint16_t idx;
|
|
uint32_t reg_val;
|
|
|
|
if (config->desc->current_ranges == NULL) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
ret = linear_range_group_get_win_index(config->desc->current_ranges,
|
|
1,
|
|
min_ua, max_ua, &idx);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* All registers have same bits layout */
|
|
reg_val = *config->desc->dcdc_register & ~(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk |
|
|
DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_LV_Msk |
|
|
DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MIN_Msk);
|
|
reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk, idx);
|
|
reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_LV_Msk, idx);
|
|
reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MIN_Msk, idx);
|
|
|
|
*config->desc->dcdc_register = reg_val;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int regulator_da1469x_get_current_limit(const struct device *dev,
|
|
int32_t *curr_ua)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
int ret;
|
|
uint16_t idx;
|
|
|
|
if (config->desc->current_ranges == NULL) {
|
|
return -ENOTSUP;
|
|
}
|
|
idx = FIELD_GET(*config->desc->dcdc_register,
|
|
DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk);
|
|
ret = linear_range_group_get_value(config->desc->current_ranges, 1, idx, curr_ua);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct regulator_driver_api regulator_da1469x_api = {
|
|
.enable = regulator_da1469x_enable,
|
|
.disable = regulator_da1469x_disable,
|
|
.count_voltages = regulator_da1469x_count_voltages,
|
|
.list_voltage = regulator_da1469x_list_voltage,
|
|
.set_voltage = regulator_da1469x_set_voltage,
|
|
.get_voltage = regulator_da1469x_get_voltage,
|
|
.set_current_limit = regulator_da1469x_set_current_limit,
|
|
.get_current_limit = regulator_da1469x_get_current_limit,
|
|
};
|
|
|
|
static int regulator_da1469x_init(const struct device *dev)
|
|
{
|
|
const struct regulator_da1469x_config *config = dev->config;
|
|
|
|
regulator_common_data_init(dev);
|
|
|
|
if ((config->rail == V30) &&
|
|
(config->power_bits & CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk)) {
|
|
CRG_TOP->POWER_CTRL_REG |= CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk;
|
|
}
|
|
|
|
return regulator_common_init(dev, 0);
|
|
}
|
|
|
|
#define REGULATOR_DA1469X_DEFINE(node, id, rail_id) \
|
|
static struct regulator_da1469x_data data_##id; \
|
|
\
|
|
static const struct regulator_da1469x_config config_##id = { \
|
|
.common = REGULATOR_DT_COMMON_CONFIG_INIT(node), \
|
|
.desc = &id ## _desc, \
|
|
.power_bits = \
|
|
(DT_PROP(node, renesas_regulator_v30_clamp) * \
|
|
CRG_TOP_POWER_CTRL_REG_CLAMP_3V0_VBAT_ENABLE_Msk) | \
|
|
(DT_PROP(node, renesas_regulator_v30_vbus) * \
|
|
DA1469X_LDO_3V0_MODE_VBAT) | \
|
|
(DT_PROP(node, renesas_regulator_v30_vbat) * \
|
|
DA1469X_LDO_3V0_MODE_VBUS) | \
|
|
(DT_PROP(node, renesas_regulator_sleep_ldo) * \
|
|
(DA1469X_LDO_ ## rail_id ##_RET)) | \
|
|
(DT_PROP(node, renesas_regulator_v30_ref_bandgap) * \
|
|
CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk), \
|
|
.dcdc_bits = \
|
|
(DT_PROP(node, renesas_regulator_dcdc_vbat_high) * \
|
|
DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_HV_Msk) | \
|
|
(DT_PROP(node, renesas_regulator_dcdc_vbat_low) * \
|
|
DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_LV_Msk) \
|
|
}; \
|
|
DEVICE_DT_DEFINE(node, regulator_da1469x_init, NULL, &data_##id, \
|
|
&config_##id, PRE_KERNEL_1, \
|
|
CONFIG_REGULATOR_DA1469X_INIT_PRIORITY, \
|
|
®ulator_da1469x_api);
|
|
|
|
#define REGULATOR_DA1469X_DEFINE_COND(inst, child, source) \
|
|
COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \
|
|
(REGULATOR_DA1469X_DEFINE( \
|
|
DT_INST_CHILD(inst, child), child, source)), \
|
|
())
|
|
|
|
#define REGULATOR_DA1469X_DEFINE_ALL(inst) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, vdd_clamp, VDD_CLAMP) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, vdd_sleep, VDD_SLEEP) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, vdd, VDD) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, v14, V14) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, v18, V18) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, v18p, V18P) \
|
|
REGULATOR_DA1469X_DEFINE_COND(inst, v30, V30) \
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(REGULATOR_DA1469X_DEFINE_ALL)
|