Commit 1a1a0d5c authored by Brian Norris's avatar Brian Norris Committed by Kalle Valo
Browse files

ath10k: snoc: fix unabalanced regulator error handling



If a regulator fails to set its voltage, we end up with an unbalanced
call to regulator_disable(), because the error path starts with the
current regulator (which was never enabled).

Factor out the "on" function to perform (and unwind if failed) a single
regulator at a time, and then main loop (ath10k_snoc_vreg_on()) can just
worry about unwinding the regulators that were already enabled.

It also helps to factor out the "off" function, to avoid repeating some
code here.

Signed-off-by: default avatarBrian Norris <briannorris@chromium.org>
Reviewed-by: default avatarDouglas Anderson <dianders@chromium.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 887a3dcf
Loading
Loading
Loading
Loading
+75 −54
Original line number Diff line number Diff line
@@ -1335,18 +1335,10 @@ static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
	return ret;
}

static int ath10k_snoc_vreg_on(struct ath10k *ar)
static int __ath10k_snoc_vreg_on(struct ath10k *ar,
				 struct ath10k_vreg_info *vreg_info)
{
	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
	struct ath10k_vreg_info *vreg_info;
	int ret = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
		vreg_info = &ar_snoc->vreg[i];

		if (!vreg_info->reg)
			continue;
	int ret;

	ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
		   vreg_info->name);
@@ -1357,18 +1349,15 @@ static int ath10k_snoc_vreg_on(struct ath10k *ar)
		ath10k_err(ar,
			   "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
			   vreg_info->name, vreg_info->min_v, vreg_info->max_v);
			goto err_reg_config;
		return ret;
	}

	if (vreg_info->load_ua) {
			ret = regulator_set_load(vreg_info->reg,
						 vreg_info->load_ua);
		ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua);
		if (ret < 0) {
				ath10k_err(ar,
					   "failed to set regulator %s load: %d\n",
					   vreg_info->name,
					   vreg_info->load_ua);
				goto err_reg_config;
			ath10k_err(ar, "failed to set regulator %s load: %d\n",
				   vreg_info->name, vreg_info->load_ua);
			goto err_set_load;
		}
	}

@@ -1376,25 +1365,74 @@ static int ath10k_snoc_vreg_on(struct ath10k *ar)
	if (ret) {
		ath10k_err(ar, "failed to enable regulator %s\n",
			   vreg_info->name);
			goto err_reg_config;
		goto err_enable;
	}

	if (vreg_info->settle_delay)
		udelay(vreg_info->settle_delay);

	return 0;

err_enable:
	regulator_set_load(vreg_info->reg, 0);
err_set_load:
	regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);

	return ret;
}

static int __ath10k_snoc_vreg_off(struct ath10k *ar,
				  struct ath10k_vreg_info *vreg_info)
{
	int ret;

	ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
		   vreg_info->name);

	ret = regulator_disable(vreg_info->reg);
	if (ret)
		ath10k_err(ar, "failed to disable regulator %s\n",
			   vreg_info->name);

	ret = regulator_set_load(vreg_info->reg, 0);
	if (ret < 0)
		ath10k_err(ar, "failed to set load %s\n", vreg_info->name);

	ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
	if (ret)
		ath10k_err(ar, "failed to set voltage %s\n", vreg_info->name);

	return ret;
}

static int ath10k_snoc_vreg_on(struct ath10k *ar)
{
	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
	struct ath10k_vreg_info *vreg_info;
	int ret = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
		vreg_info = &ar_snoc->vreg[i];

		if (!vreg_info->reg)
			continue;

		ret = __ath10k_snoc_vreg_on(ar, vreg_info);
		if (ret)
			goto err_reg_config;
	}

	return 0;

err_reg_config:
	for (; i >= 0; i--) {
	for (i = i - 1; i >= 0; i--) {
		vreg_info = &ar_snoc->vreg[i];

		if (!vreg_info->reg)
			continue;

		regulator_disable(vreg_info->reg);
		regulator_set_load(vreg_info->reg, 0);
		regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
		__ath10k_snoc_vreg_off(ar, vreg_info);
	}

	return ret;
@@ -1413,24 +1451,7 @@ static int ath10k_snoc_vreg_off(struct ath10k *ar)
		if (!vreg_info->reg)
			continue;

		ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
			   vreg_info->name);

		ret = regulator_disable(vreg_info->reg);
		if (ret)
			ath10k_err(ar, "failed to disable regulator %s\n",
				   vreg_info->name);

		ret = regulator_set_load(vreg_info->reg, 0);
		if (ret < 0)
			ath10k_err(ar, "failed to set load %s\n",
				   vreg_info->name);

		ret = regulator_set_voltage(vreg_info->reg, 0,
					    vreg_info->max_v);
		if (ret)
			ath10k_err(ar, "failed to set voltage %s\n",
				   vreg_info->name);
		ret = __ath10k_snoc_vreg_off(ar, vreg_info);
	}

	return ret;