drivers: regulator: pca9420: refactor mode handling
- Similar to what was done for other parts of the driver, remove any register specification from Devicetree (modesel-reg/mask) - Keep all the information in the driver, and define modes as "numbers", e.g. PCA9420_MODE0: 0, PCA9420_MODE1: 1, etc. - Bindings provide IC defaults now (all modes allowed 0/1/2/3 and initial mode set to 0). - When mode is controlled via the MODESEL0/1 pins (ie directly by an iMX MCU using the dedicated PMIC_MODE0/1 pins), the driver will not allow to select a mode (it is not possible). This mode is now enabled by setting `nxp,enable-modesel-pins` in Devicetree. When enabled, all the allowed modes are configured to be selectable via pins. When disabled, mode can be set via I2C (using TOP_CNTL3 MODE0/1_I2C fields) Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
parent
535f5a6c04
commit
649a8195b3
5 changed files with 159 additions and 308 deletions
|
@ -18,6 +18,8 @@
|
|||
/** General purpose registers */
|
||||
/** @brief Top level system ctrl 0 */
|
||||
#define PCA9420_TOP_CNTL0 0x09U
|
||||
/** @brief Top level system ctrl 3 */
|
||||
#define PCA9420_TOP_CNTL3 0x0CU
|
||||
|
||||
/** Regulator status indication registers */
|
||||
/** @brief Mode configuration for mode 0_0 */
|
||||
|
@ -34,11 +36,15 @@
|
|||
#define PCA9420_TOP_CNTL0_VIN_ILIM_SEL_MASK 0xE0U
|
||||
#define PCA9420_TOP_CNTL0_VIN_ILIM_SEL_DISABLED 0x7U
|
||||
|
||||
/** @brief I2C Mode control mask */
|
||||
#define PCA9420_TOP_CNTL3_MODE_I2C_POS 3U
|
||||
#define PCA9420_TOP_CNTL3_MODE_I2C_MASK 0x18U
|
||||
|
||||
/*
|
||||
* @brief Mode control selection mask. When this bit is set, the external
|
||||
* PMIC pins MODESEL0 and MODESEL1 can be used to select the active mode
|
||||
*/
|
||||
#define PCA9420_MODECFG_0_MODE_CTRL_SEL_MASK 0x40U
|
||||
#define PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN 0x40U
|
||||
|
||||
/*
|
||||
* @brief Mode configuration upon falling edge applied to ON pin. If set,
|
||||
|
@ -75,6 +81,9 @@
|
|||
/** VIN ILIM minimum value, uA */
|
||||
#define PCA9420_VIN_ILIM_MIN_UA 85000
|
||||
|
||||
/** Offset applied to MODECFG* registers for a given mode */
|
||||
#define PCA9420_MODECFG_OFFSET(mode) ((mode) * 4U)
|
||||
|
||||
struct regulator_pca9420_desc {
|
||||
uint8_t enable_reg;
|
||||
uint8_t enable_mask;
|
||||
|
@ -89,11 +98,13 @@ struct regulator_pca9420_desc {
|
|||
struct regulator_pca9420_common_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
int32_t vin_ilim_ua;
|
||||
uint8_t modesel_reg;
|
||||
uint8_t modesel_mask;
|
||||
const uint16_t *allowed_modes;
|
||||
int num_modes;
|
||||
uint16_t initial_mode;
|
||||
const uint8_t *allowed_modes;
|
||||
uint8_t allowed_modes_cnt;
|
||||
bool enable_modesel_pins;
|
||||
};
|
||||
|
||||
struct regulator_pca9420_common_data {
|
||||
uint8_t mode;
|
||||
};
|
||||
|
||||
struct regulator_pca9420_config {
|
||||
|
@ -181,13 +192,23 @@ static int regulator_pca9420_is_supported_voltage(const struct device *dev,
|
|||
int32_t min_uv,
|
||||
int32_t max_uv);
|
||||
|
||||
/*
|
||||
* Internal helper function- gets the voltage from a regulator, with an
|
||||
* offset applied to the vsel_reg. Useful to support reading voltages
|
||||
* in another target mode
|
||||
*/
|
||||
static int regulator_pca9420_get_voltage_offset(const struct device *dev,
|
||||
uint32_t off, int32_t *voltage)
|
||||
static bool regulator_pca9420_is_mode_allowed(const struct device *dev,
|
||||
uint8_t mode)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
|
||||
for (uint8_t i = 0U; i < cconfig->allowed_modes_cnt; i++) {
|
||||
if (mode == cconfig->allowed_modes[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int regulator_pca9420_get_voltage_mode(const struct device *dev,
|
||||
uint8_t mode, int32_t *voltage)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
|
@ -195,8 +216,14 @@ static int regulator_pca9420_get_voltage_offset(const struct device *dev,
|
|||
int ret;
|
||||
uint8_t raw_reg;
|
||||
|
||||
ret = i2c_reg_read_byte_dt(&cconfig->i2c, config->desc->vsel_reg + off,
|
||||
&raw_reg);
|
||||
if (!regulator_pca9420_is_mode_allowed(dev, mode)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = i2c_reg_read_byte_dt(
|
||||
&cconfig->i2c,
|
||||
config->desc->vsel_reg + PCA9420_MODECFG_OFFSET(mode),
|
||||
&raw_reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -208,20 +235,19 @@ static int regulator_pca9420_get_voltage_offset(const struct device *dev,
|
|||
voltage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper function- sets the voltage for a regulator, with an
|
||||
* offset applied to the vsel_reg. Useful to support setting voltages in
|
||||
* another target mode.
|
||||
*/
|
||||
static int regulator_set_voltage_offset(const struct device *dev,
|
||||
int32_t min_uv, int32_t max_uv,
|
||||
uint32_t off)
|
||||
static int regulator_set_voltage_mode(const struct device *dev,
|
||||
int32_t min_uv, int32_t max_uv,
|
||||
uint8_t mode)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
uint16_t idx;
|
||||
int ret;
|
||||
|
||||
if (!regulator_pca9420_is_mode_allowed(dev, mode)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = linear_range_group_get_win_index(config->desc->ranges,
|
||||
config->desc->num_ranges, min_uv,
|
||||
max_uv, &idx);
|
||||
|
@ -231,9 +257,10 @@ static int regulator_set_voltage_offset(const struct device *dev,
|
|||
|
||||
idx <<= config->desc->vsel_pos;
|
||||
|
||||
return i2c_reg_update_byte_dt(&cconfig->i2c,
|
||||
config->desc->vsel_reg + off,
|
||||
config->desc->vsel_mask, (uint8_t)idx);
|
||||
return i2c_reg_update_byte_dt(
|
||||
&cconfig->i2c,
|
||||
config->desc->vsel_reg + PCA9420_MODECFG_OFFSET(mode),
|
||||
config->desc->vsel_mask, (uint8_t)idx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,7 +285,7 @@ static int regulator_pca9420_count_modes(const struct device *dev)
|
|||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
|
||||
return cconfig->num_modes;
|
||||
return cconfig->allowed_modes_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,7 +330,10 @@ static int regulator_pca9420_is_supported_voltage(const struct device *dev,
|
|||
static int regulator_pca9420_set_voltage(const struct device *dev,
|
||||
int32_t min_uv, int32_t max_uv)
|
||||
{
|
||||
return regulator_set_voltage_offset(dev, min_uv, max_uv, 0);
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
struct regulator_pca9420_common_data *cdata = config->parent->data;
|
||||
|
||||
return regulator_set_voltage_mode(dev, min_uv, max_uv, cdata->mode);
|
||||
}
|
||||
|
||||
|
||||
|
@ -313,9 +343,11 @@ static int regulator_pca9420_set_voltage(const struct device *dev,
|
|||
*/
|
||||
static int32_t regulator_pca9420_get_voltage(const struct device *dev)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
struct regulator_pca9420_common_data *cdata = config->parent->data;
|
||||
int32_t voltage = 0;
|
||||
|
||||
(void)regulator_pca9420_get_voltage_offset(dev, 0, &voltage);
|
||||
(void)regulator_pca9420_get_voltage_mode(dev, cdata->mode, &voltage);
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
@ -347,26 +379,7 @@ static int regulator_pca9420_set_mode_voltage(const struct device *dev,
|
|||
uint32_t mode, int32_t min_uv,
|
||||
int32_t max_uv)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
uint8_t i, sel_off;
|
||||
|
||||
if (cconfig->num_modes == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Search for mode ID in allowed modes. */
|
||||
for (i = 0 ; i < cconfig->num_modes; i++) {
|
||||
if (cconfig->allowed_modes[i] == mode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == cconfig->num_modes) {
|
||||
/* Mode was not found */
|
||||
return -EINVAL;
|
||||
}
|
||||
sel_off = ((mode & PCA9420_MODE_OFFSET_MASK) >> PCA9420_MODE_OFFSET_SHIFT);
|
||||
return regulator_set_voltage_offset(dev, min_uv, max_uv, sel_off);
|
||||
return regulator_set_voltage_mode(dev, min_uv, max_uv, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -379,27 +392,13 @@ static int regulator_pca9420_mode_disable(const struct device *dev,
|
|||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
uint8_t i, sel_off, dis_val;
|
||||
uint8_t dis_val;
|
||||
|
||||
if (cconfig->num_modes == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Search for mode ID in allowed modes. */
|
||||
for (i = 0 ; i < cconfig->num_modes; i++) {
|
||||
if (cconfig->allowed_modes[i] == mode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == cconfig->num_modes) {
|
||||
/* Mode was not found */
|
||||
return -EINVAL;
|
||||
}
|
||||
sel_off = ((mode & PCA9420_MODE_OFFSET_MASK) >> PCA9420_MODE_OFFSET_SHIFT);
|
||||
dis_val = config->enable_inverted ? config->desc->enable_val : 0;
|
||||
return i2c_reg_update_byte_dt(&cconfig->i2c,
|
||||
config->desc->enable_reg + sel_off,
|
||||
config->desc->enable_mask, dis_val);
|
||||
return i2c_reg_update_byte_dt(
|
||||
&cconfig->i2c,
|
||||
config->desc->enable_reg + PCA9420_MODECFG_OFFSET(mode),
|
||||
config->desc->enable_mask, dis_val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -412,27 +411,13 @@ static int regulator_pca9420_mode_enable(const struct device *dev,
|
|||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
uint8_t i, sel_off, en_val;
|
||||
uint8_t en_val;
|
||||
|
||||
if (cconfig->num_modes == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Search for mode ID in allowed modes. */
|
||||
for (i = 0 ; i < cconfig->num_modes; i++) {
|
||||
if (cconfig->allowed_modes[i] == mode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == cconfig->num_modes) {
|
||||
/* Mode was not found */
|
||||
return -EINVAL;
|
||||
}
|
||||
sel_off = ((mode & PCA9420_MODE_OFFSET_MASK) >> PCA9420_MODE_OFFSET_SHIFT);
|
||||
en_val = config->enable_inverted ? 0 : config->desc->enable_val;
|
||||
return i2c_reg_update_byte_dt(&cconfig->i2c,
|
||||
config->desc->enable_reg + sel_off,
|
||||
config->desc->enable_mask, en_val);
|
||||
return i2c_reg_update_byte_dt(
|
||||
&cconfig->i2c,
|
||||
config->desc->enable_reg + PCA9420_MODECFG_OFFSET(mode),
|
||||
config->desc->enable_mask, en_val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -444,74 +429,37 @@ static int regulator_pca9420_mode_enable(const struct device *dev,
|
|||
static int32_t regulator_pca9420_get_mode_voltage(const struct device *dev,
|
||||
uint32_t mode)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
uint8_t i, sel_off;
|
||||
int32_t voltage;
|
||||
|
||||
if (cconfig->num_modes == 0) {
|
||||
return -ENOTSUP;
|
||||
if (regulator_pca9420_get_voltage_mode(dev, mode, &voltage) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search for mode ID in allowed modes. */
|
||||
for (i = 0 ; i < cconfig->num_modes; i++) {
|
||||
if (cconfig->allowed_modes[i] == mode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == cconfig->num_modes) {
|
||||
/* Mode was not found */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sel_off = ((mode & PCA9420_MODE_OFFSET_MASK) >> PCA9420_MODE_OFFSET_SHIFT);
|
||||
|
||||
(void)regulator_pca9420_get_voltage_offset(dev, sel_off, &voltage);
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Part of the extended regulator consumer API
|
||||
* switches the regulator to a given mode. This API will apply a mode for
|
||||
* the regulator.
|
||||
*/
|
||||
static int regulator_pca9420_set_mode(const struct device *dev, uint32_t mode)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
int rc;
|
||||
uint8_t i, sel_off;
|
||||
struct regulator_pca9420_common_data *cdata = config->parent->data;
|
||||
int ret;
|
||||
|
||||
if (cconfig->num_modes == 0) {
|
||||
if (cconfig->enable_modesel_pins ||
|
||||
!regulator_pca9420_is_mode_allowed(dev, mode)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Search for mode ID in allowed modes. */
|
||||
for (i = 0 ; i < cconfig->num_modes; i++) {
|
||||
if (cconfig->allowed_modes[i] == mode) {
|
||||
break;
|
||||
}
|
||||
ret = i2c_reg_update_byte_dt(&cconfig->i2c, PCA9420_TOP_CNTL3,
|
||||
mode << PCA9420_TOP_CNTL3_MODE_I2C_POS,
|
||||
PCA9420_TOP_CNTL3_MODE_I2C_MASK);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (i == cconfig->num_modes) {
|
||||
/* Mode was not found */
|
||||
return -EINVAL;
|
||||
}
|
||||
sel_off = ((mode & PCA9420_MODE_OFFSET_MASK) >> PCA9420_MODE_OFFSET_SHIFT);
|
||||
/* Configure mode */
|
||||
if (mode & PCA9420_MODE_FLAG_MODESEL_MULTI_REG) {
|
||||
/* Select mode with offset calculation */
|
||||
rc = i2c_reg_update_byte_dt(&cconfig->i2c,
|
||||
cconfig->modesel_reg + sel_off,
|
||||
mode & PCA9420_MODE_SELECTOR_MASK,
|
||||
cconfig->modesel_mask);
|
||||
} else {
|
||||
/* Select mode without offset to modesel_reg */
|
||||
rc = i2c_reg_update_byte_dt(&cconfig->i2c, cconfig->modesel_reg,
|
||||
mode & PCA9420_MODE_SELECTOR_MASK,
|
||||
cconfig->modesel_mask);
|
||||
}
|
||||
return rc;
|
||||
|
||||
cdata->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regulator_pca9420_enable(const struct device *dev)
|
||||
|
@ -539,7 +487,6 @@ static int regulator_pca9420_disable(const struct device *dev)
|
|||
static int regulator_pca9420_init(const struct device *dev)
|
||||
{
|
||||
const struct regulator_pca9420_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_config *cconfig = config->parent->config;
|
||||
int rc = 0;
|
||||
|
||||
regulator_common_data_init(dev);
|
||||
|
@ -551,21 +498,44 @@ static int regulator_pca9420_init(const struct device *dev)
|
|||
if (config->boot_on) {
|
||||
rc = regulator_pca9420_enable(dev);
|
||||
}
|
||||
if (cconfig->initial_mode) {
|
||||
rc = regulator_pca9420_set_mode(dev, cconfig->initial_mode);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int regulator_pca9420_common_init(const struct device *dev)
|
||||
{
|
||||
const struct regulator_pca9420_common_config *config = dev->config;
|
||||
const struct regulator_pca9420_common_data *data = dev->data;
|
||||
uint8_t reg_val = PCA9420_TOP_CNTL0_VIN_ILIM_SEL_DISABLED;
|
||||
int ret;
|
||||
|
||||
if (!device_is_ready(config->i2c.bus)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (config->enable_modesel_pins) {
|
||||
/* enable MODESEL0/1 pins for each allowed mode */
|
||||
for (uint8_t i = 0U; i < config->allowed_modes_cnt; i++) {
|
||||
ret = i2c_reg_update_byte_dt(
|
||||
&config->i2c,
|
||||
PCA9420_MODECFG_0_0 +
|
||||
PCA9420_MODECFG_OFFSET(config->allowed_modes[i]),
|
||||
PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN,
|
||||
PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = i2c_reg_update_byte_dt(
|
||||
&config->i2c, PCA9420_TOP_CNTL3,
|
||||
data->mode << PCA9420_TOP_CNTL3_MODE_I2C_POS,
|
||||
PCA9420_TOP_CNTL3_MODE_I2C_MASK);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* configure VIN current limit */
|
||||
if (config->vin_ilim_ua != 0U) {
|
||||
reg_val = (config->vin_ilim_ua - PCA9420_VIN_ILIM_MIN_UA) /
|
||||
|
@ -618,22 +588,24 @@ static const struct regulator_driver_api api = {
|
|||
())
|
||||
|
||||
#define REGULATOR_PCA9420_DEFINE_ALL(inst) \
|
||||
static const uint16_t allowed_modes_##inst[] = \
|
||||
DT_INST_PROP_OR(inst, regulator_allowed_modes, {}); \
|
||||
static const uint8_t allowed_modes_##inst[] = \
|
||||
DT_INST_PROP(inst, regulator_allowed_modes); \
|
||||
\
|
||||
static struct regulator_pca9420_common_data data_##inst = { \
|
||||
.mode = DT_INST_PROP(inst, regulator_initial_mode) \
|
||||
}; \
|
||||
\
|
||||
static const struct regulator_pca9420_common_config config_##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.vin_ilim_ua = DT_INST_PROP(inst, nxp_vin_ilim_microamp), \
|
||||
.allowed_modes = allowed_modes_##inst, \
|
||||
.num_modes = ARRAY_SIZE(allowed_modes_##inst), \
|
||||
.initial_mode = \
|
||||
DT_INST_PROP_OR(inst, regulator_initial_mode, 0), \
|
||||
.modesel_reg = DT_INST_PROP_OR(inst, modesel_reg, 0), \
|
||||
.modesel_mask = DT_INST_PROP_OR(inst, modesel_mask, 0), \
|
||||
.allowed_modes_cnt = ARRAY_SIZE(allowed_modes_##inst), \
|
||||
.enable_modesel_pins = \
|
||||
DT_INST_PROP(inst, nxp_enable_modesel_pins), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, regulator_pca9420_common_init, NULL, NULL, \
|
||||
&config_##inst, POST_KERNEL, \
|
||||
DEVICE_DT_INST_DEFINE(inst, regulator_pca9420_common_init, NULL, \
|
||||
&data_##inst, &config_##inst, POST_KERNEL, \
|
||||
CONFIG_REGULATOR_PCA9420_COMMON_INIT_PRIORITY, \
|
||||
NULL); \
|
||||
\
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue