diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index c47dc9f92279..77d402ee6bf6 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -4,6 +4,7 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ +#include #include #include #include @@ -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;