regulator: qcom-rpmh: readback voltage/bypass/mode set during bootup

Currently, during regulator registration, regulator framework sends an
unnecessary `min-microvolts` request for the rpmh-regulator device. This
happens because in current design, we do not have a way to readback the
voltage settings that was set during the bootloader stage.

Fix this by using the rpmh_read() API to read the regulator voltage
settings done during boot and make it available to regulator framework
from the very first read after the bootup.

Also use this API to read the mode/bypass settings as well. This will
provide the regulator framework a sense of the initial settings done by
bootloader and thus preventing any redundant writes for any setting post
bootup incase the same setting was already applied during bootup.

Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Link: https://patch.msgid.link/20260801-b4-read-rpmh-v5-v6-3-9fcb54928523@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Kamal Wadhwa 2026-08-01 13:30:29 +05:30 committed by Mark Brown
parent abd14bebb8
commit 09d99ff7fc
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -4,6 +4,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -61,8 +62,13 @@ static const struct resource_name_formats vreg_rsc_name_lookup[NUM_REGULATOR_TYP
};
#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0
#define RPMH_REGULATOR_VOLTAGE_MASK GENMASK(12, 0)
#define RPMH_REGULATOR_REG_ENABLE 0x4
#define RPMH_REGULATOR_ENABLE_MASK BIT(0)
#define RPMH_REGULATOR_REG_VRM_MODE 0x8
#define RPMH_REGULATOR_MODE_MASK GENMASK(2, 0)
#define PMIC4_LDO_MODE_RETENTION 4
#define PMIC4_LDO_MODE_LPM 5
@ -248,9 +254,34 @@ static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
selector > vreg->voltage_selector);
}
static int _rpmh_regulator_vrm_get_voltage(struct regulator_dev *rdev, int *uV)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
struct tcs_cmd cmd = {
.addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
};
int ret;
ret = rpmh_read(vreg->dev, &cmd);
if (!ret)
*uV = (cmd.data & RPMH_REGULATOR_VOLTAGE_MASK) * 1000;
else
dev_err(vreg->dev, "failed to read VOLTAGE ret = %d\n", ret);
return ret;
}
static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
int ret, uV = 0;
if (vreg->voltage_selector < 0) {
ret = _rpmh_regulator_vrm_get_voltage(rdev, &uV);
if (!ret && uV != 0)
vreg->voltage_selector = regulator_map_voltage_linear_range(rdev,
uV, INT_MAX);
}
return vreg->voltage_selector;
}
@ -336,6 +367,22 @@ static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
return ret;
}
static int rpmh_regulator_vrm_get_pmic_mode(struct rpmh_vreg *vreg, int *pmic_mode)
{
struct tcs_cmd cmd = {
.addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
};
int ret;
ret = rpmh_read(vreg->dev, &cmd);
if (!ret)
*pmic_mode = cmd.data & RPMH_REGULATOR_MODE_MASK;
else
return -EINVAL;
return 0;
}
static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
@ -540,6 +587,73 @@ static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
return 0;
}
static int rpmh_regulator_determine_initial_mode(struct rpmh_vreg *vreg)
{
struct tcs_cmd cmd = {
.addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
};
int ret, pmic_mode, mode;
int sts;
ret = rpmh_read(vreg->dev, &cmd);
if (ret) {
dev_err(vreg->dev, "failed to read ENABLE status ret = %d\n", ret);
return ret;
}
sts = cmd.data & RPMH_REGULATOR_ENABLE_MASK;
if (!sts)
return 0;
if (vreg->hw_data->regulator_type == XOB)
return 0;
ret = rpmh_regulator_vrm_get_pmic_mode(vreg, &pmic_mode);
if (ret < 0) {
vreg->mode = REGULATOR_MODE_INVALID;
dev_err(vreg->dev, "failed to read pmic_mode ret = %d\n", ret);
return ret;
}
/*
* NOTE: Since BOB4 BYPASS_MODE value = 0 we cannot confirm if that BOB
* regulator has been sent into bypass mode by bootloader or if bootloader
* just has not requested for any mode voting. Due this limitation, we
* must check if the read pmic_mode value is non-zero before comparing it
* to bypass mode value. This also is needed to avoid setting BYPASS status
* for LDOs which dont support bypass mode, and have the pmic_bypass_mode
* uninitialized value as zero in the vreg hw data. For such cases assume
* lowest mode, if pmic_mode is zero, to allow for mode voting.
*/
if (!pmic_mode) {
for (mode = REGULATOR_MODE_STANDBY; mode > REGULATOR_MODE_INVALID; mode >>= 1) {
if (vreg->hw_data->pmic_mode_map[mode] >= 0) {
vreg->mode = mode;
break;
}
}
return 0;
}
if (vreg->hw_data->pmic_bypass_mode == pmic_mode) {
vreg->bypassed = true;
return 0;
}
for (mode = REGULATOR_MODE_STANDBY; mode > REGULATOR_MODE_INVALID; mode >>= 1) {
if (pmic_mode == vreg->hw_data->pmic_mode_map[mode]) {
vreg->mode = mode;
break;
}
}
return 0;
}
static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_INVALID] = -EINVAL,
[REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
@ -1838,6 +1952,12 @@ static int rpmh_regulator_probe(struct platform_device *pdev)
vreg_data);
if (ret < 0)
return ret;
ret = rpmh_regulator_determine_initial_mode(vreg);
if (ret < 0)
dev_err(dev, "failed to read initial mode for %s\n",
vreg->rdesc.name);
}
return 0;