regulator: qcom_pm8008: remove explicit parent supply management

Remove explicit parent supply management (i.e. regulator_enable(),
regulator_disable(), and regulator_set_voltage calls) from inside
of the qcom_pm8008-regulator regulator_ops callback functions.
This helps to eliminate a source of mutex deadlock introduced by
commit f8702f9e4a ("regulator: core: Use ww_mutex for regulators
locking").

Change-Id: Ib4d731d1230b3a8cf9e1094d0d63a7c99531fa9d
Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
David Collins 2020-04-02 17:43:07 -07:00
parent 17c22334d7
commit 96edec9b74

View File

@ -48,8 +48,6 @@
#define LDO_VSET_LB_REG(base) (base + 0x40)
#define LDO_VSET_VALID_LB_REG(base) (base + 0x42)
#define LDO_MODE_CTL1_REG(base) (base + 0x45)
#define MODE_PRIMARY_MASK GENMASK(2, 0)
#define LDO_MODE_NPM 7
@ -66,7 +64,6 @@
#define LDO_PD_CTL_REG(base) (base + 0xA0)
#define STRONG_PD_EN_BIT BIT(7)
#define MAX_REG_NAME 20
#define PM8008_MAX_LDO 7
struct pm8008_chip {
@ -80,6 +77,8 @@ struct pm8008_chip {
struct regulator_data {
char *name;
char *supply_name;
int min_uv;
int max_uv;
int hpm_min_load_ua;
int min_dropout_uv;
};
@ -89,26 +88,24 @@ struct pm8008_regulator {
struct regmap *regmap;
struct regulator_desc rdesc;
struct regulator_dev *rdev;
struct regulator *parent_supply;
struct regulator *en_supply;
struct device_node *of_node;
struct notifier_block nb;
u16 base;
int hpm_min_load_ua;
int min_dropout_uv;
int step_rate;
bool enable_ocp_broadcast;
};
static struct regulator_data reg_data[PM8008_MAX_LDO] = {
/* name, parent, min load, headroom */
{"l1", "vdd_l1_l2", 10000, 225000},
{"l2", "vdd_l1_l2", 10000, 225000},
{"l3", "vdd_l3_l4", 10000, 200000},
{"l4", "vdd_l3_l4", 10000, 200000},
{"l5", "vdd_l5", 10000, 300000},
{"l6", "vdd_l6", 10000, 300000},
{"l7", "vdd_l7", 10000, 300000},
/* name parent min_uv max_uv hpm_load headroom_uv */
{"l1", "vdd_l1_l2", 528000, 1504000, 30000, 225000},
{"l2", "vdd_l1_l2", 528000, 1504000, 30000, 225000},
{"l3", "vdd_l3_l4", 1504000, 3400000, 10000, 200000},
{"l4", "vdd_l3_l4", 1504000, 3400000, 10000, 200000},
{"l5", "vdd_l5", 1504000, 3400000, 10000, 300000},
{"l6", "vdd_l6", 1504000, 3400000, 10000, 300000},
{"l7", "vdd_l7", 1504000, 3400000, 10000, 300000},
};
/* common functions */
@ -157,7 +154,7 @@ static int pm8008_regulator_get_voltage(struct regulator_dev *rdev)
int rc;
rc = pm8008_read(pm8008_reg->regmap,
LDO_VSET_VALID_LB_REG(pm8008_reg->base),
LDO_VSET_LB_REG(pm8008_reg->base),
vset_raw, 2);
if (rc < 0) {
pm8008_err(pm8008_reg,
@ -206,33 +203,13 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev)
return rc;
}
if (pm8008_reg->parent_supply) {
rc = regulator_set_voltage(pm8008_reg->parent_supply,
current_uv + pm8008_reg->min_dropout_uv,
INT_MAX);
if (rc < 0) {
pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
rc);
goto remove_en;
}
rc = regulator_enable(pm8008_reg->parent_supply);
if (rc < 0) {
pm8008_err(pm8008_reg,
"failed to enable parent rc=%d\n", rc);
regulator_set_voltage(pm8008_reg->parent_supply, 0,
INT_MAX);
goto remove_en;
}
}
rc = pm8008_masked_write(pm8008_reg->regmap,
LDO_ENABLE_REG(pm8008_reg->base),
ENABLE_BIT, ENABLE_BIT);
if (rc < 0) {
pm8008_err(pm8008_reg,
"failed to enable regulator rc=%d\n", rc);
goto remove_vote;
goto remove_en;
}
/*
@ -270,19 +247,6 @@ disable_ldo:
pm8008_masked_write(pm8008_reg->regmap,
LDO_ENABLE_REG(pm8008_reg->base), ENABLE_BIT, 0);
remove_vote:
if (pm8008_reg->parent_supply) {
rc2 = regulator_disable(pm8008_reg->parent_supply);
if (rc2 < 0)
pm8008_err(pm8008_reg, "failed to disable parent supply rc=%d\n",
rc2);
rc2 = regulator_set_voltage(pm8008_reg->parent_supply, 0,
INT_MAX);
if (rc2 < 0)
pm8008_err(pm8008_reg, "failed to remove voltage vote for parent supply rc=%d\n",
rc2);
}
remove_en:
rc2 = regulator_disable(pm8008_reg->en_supply);
if (rc2 < 0)
@ -306,23 +270,6 @@ static int pm8008_regulator_disable(struct regulator_dev *rdev)
return rc;
}
/* remove voltage vote from parent regulator */
if (pm8008_reg->parent_supply) {
rc = regulator_disable(pm8008_reg->parent_supply);
if (rc < 0) {
pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n",
rc);
return rc;
}
rc = regulator_set_voltage(pm8008_reg->parent_supply,
0, INT_MAX);
if (rc < 0) {
pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n",
rc);
return rc;
}
}
/* remove vote from chip enable regulator */
rc = regulator_disable(pm8008_reg->en_supply);
if (rc < 0) {
@ -380,68 +327,17 @@ static int pm8008_regulator_set_voltage(struct regulator_dev *rdev,
int min_uv, int max_uv, unsigned int *selector)
{
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
int rc = 0, current_uv = 0, rounded_uv = 0, enabled = 0;
if (pm8008_reg->parent_supply) {
enabled = pm8008_regulator_is_enabled(rdev);
if (enabled < 0) {
return enabled;
} else if (enabled) {
current_uv = pm8008_regulator_get_voltage(rdev);
if (current_uv < 0)
return current_uv;
rounded_uv = roundup(min_uv, VSET_STEP_UV);
}
}
/*
* Set the parent_supply voltage before changing the LDO voltage when
* the LDO voltage is being increased.
*/
if (pm8008_reg->parent_supply && enabled && rounded_uv >= current_uv) {
/* Request parent voltage with headroom */
rc = regulator_set_voltage(pm8008_reg->parent_supply,
rounded_uv + pm8008_reg->min_dropout_uv,
INT_MAX);
if (rc < 0) {
pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
rc);
return rc;
}
}
int rc;
rc = pm8008_write_voltage(pm8008_reg, min_uv, max_uv);
if (rc < 0)
return rc;
/*
* Set the parent_supply voltage after changing the LDO voltage when
* the LDO voltage is being reduced.
*/
if (pm8008_reg->parent_supply && enabled && rounded_uv < current_uv) {
/*
* Ensure sufficient time for the LDO voltage to slew down
* before reducing the parent supply voltage. The regulator
* framework will add the same delay after this function returns
* in all cases (i.e. enabled/disabled and increasing/decreasing
* voltage).
*/
udelay(pm8008_regulator_set_voltage_time(rdev, rounded_uv,
current_uv));
/* Request parent voltage with headroom */
rc = regulator_set_voltage(pm8008_reg->parent_supply,
rounded_uv + pm8008_reg->min_dropout_uv,
INT_MAX);
if (rc < 0) {
pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
rc);
return rc;
}
}
*selector = DIV_ROUND_UP(min_uv - pm8008_reg->rdesc.min_uV,
VSET_STEP_UV);
pm8008_debug(pm8008_reg, "voltage set to %d\n", min_uv);
return rc;
return 0;
}
static int pm8008_regulator_set_mode(struct regulator_dev *rdev,
@ -501,6 +397,7 @@ static struct regulator_ops pm8008_regulator_ops = {
.is_enabled = pm8008_regulator_is_enabled,
.set_voltage = pm8008_regulator_set_voltage,
.get_voltage = pm8008_regulator_get_voltage,
.list_voltage = regulator_list_voltage_linear,
.set_mode = pm8008_regulator_set_mode,
.get_mode = pm8008_regulator_get_mode,
.set_load = pm8008_regulator_set_load,
@ -568,7 +465,6 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
struct regulator_init_data *init_data;
struct device *dev = pm8008_reg->dev;
struct device_node *reg_node = pm8008_reg->of_node;
char buff[MAX_REG_NAME];
int rc, i, init_voltage;
u32 base = 0;
u8 reg;
@ -590,10 +486,6 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
}
pm8008_reg->base = base;
pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv;
of_property_read_u32(reg_node, "qcom,min-dropout-voltage",
&pm8008_reg->min_dropout_uv);
pm8008_reg->hpm_min_load_ua = reg_data[i].hpm_min_load_ua;
of_property_read_u32(reg_node, "qcom,hpm-min-load",
&pm8008_reg->hpm_min_load_ua);
@ -633,19 +525,6 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
}
pm8008_reg->step_rate = 38400 >> (reg & STEP_RATE_MASK);
scnprintf(buff, MAX_REG_NAME, "%s-supply", reg_data[i].supply_name);
if (of_find_property(dev->of_node, buff, NULL)) {
pm8008_reg->parent_supply = devm_regulator_get(dev,
reg_data[i].supply_name);
if (IS_ERR(pm8008_reg->parent_supply)) {
rc = PTR_ERR(pm8008_reg->parent_supply);
if (rc != -EPROBE_DEFER)
pr_err("%s: failed to get parent regulator rc=%d\n",
name, rc);
return rc;
}
}
/* pm8008_en should be present otherwise fail the regulator probe */
pm8008_reg->en_supply = devm_regulator_get(dev, "pm8008_en");
if (IS_ERR(pm8008_reg->en_supply)) {
@ -687,7 +566,16 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
pm8008_reg->rdesc.type = REGULATOR_VOLTAGE;
pm8008_reg->rdesc.ops = &pm8008_regulator_ops;
pm8008_reg->rdesc.name = init_data->constraints.name;
pm8008_reg->rdesc.n_voltages = 1;
pm8008_reg->rdesc.supply_name = reg_data[i].supply_name;
pm8008_reg->rdesc.uV_step = VSET_STEP_UV;
pm8008_reg->rdesc.min_uV = reg_data[i].min_uv;
pm8008_reg->rdesc.n_voltages
= ((reg_data[i].max_uv - reg_data[i].min_uv)
/ pm8008_reg->rdesc.uV_step) + 1;
pm8008_reg->rdesc.min_dropout_uV = reg_data[i].min_dropout_uv;
of_property_read_u32(reg_node, "qcom,min-dropout-voltage",
&pm8008_reg->rdesc.min_dropout_uV);
pm8008_reg->rdev = devm_regulator_register(dev, &pm8008_reg->rdesc,
&reg_config);