Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 167c01a0 authored by Deepak Katragadda's avatar Deepak Katragadda Committed by David Collins
Browse files

clk: qcom: gdsc-regulator: Place a min operational vote on GDSC parent



For GDSCs that reside on domains that are collapsible and
support a minimum voltage of retention, explicitly vote on
the minimal operating voltage, LowSVS for the rail. This is
required as an interim step before enabling/disabling the
GDSC. Once that's done and the GDSC has latched on to its
new state, remove this voltage vote.

Note that this fix-up patch has been squashed in:
commit c7d3dc884292 ("clk: qcom: gdsc: Fix issue with
dereferencing NULL pointer in gdsc APIs").

Change-Id: Ibac43bba8248d68f8355d1a9db9cfcbfb9fa7557
Signed-off-by: default avatarDeepak Katragadda <dkatraga@codeaurora.org>
[collinsd@codeaurora.org: squashed in a fix-up patch]
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent cee10e49
Loading
Loading
Loading
Loading
+57 −10
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 */

#include <linux/kernel.h>
@@ -19,6 +19,8 @@
#include <linux/reset.h>
#include <linux/mfd/syscon.h>

#include <dt-bindings/regulator/qcom,rpmh-regulator-levels.h>

/* GDSCR */
#define PWR_ON_MASK		BIT(31)
#define CLK_DIS_WAIT_MASK	(0xF << 12)
@@ -49,6 +51,7 @@ struct gdsc {
	struct regmap           *hw_ctrl;
	struct regmap           *sw_reset;
	struct clk		**clocks;
	struct regulator	*parent_regulator;
	struct reset_control	**reset_clocks;
	bool			toggle_logic;
	bool			resets_asserted;
@@ -147,6 +150,15 @@ static int gdsc_enable(struct regulator_dev *rdev)

	mutex_lock(&gdsc_seq_lock);

	if (sc->parent_regulator) {
		ret = regulator_set_voltage(sc->parent_regulator,
				RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX);
		if (ret) {
			mutex_unlock(&gdsc_seq_lock);
			return ret;
		}
	}

	if (sc->root_en || sc->force_root_en)
		clk_prepare_enable(sc->clocks[sc->root_clk_idx]);

@@ -154,8 +166,8 @@ static int gdsc_enable(struct regulator_dev *rdev)
	if (regval & HW_CONTROL_MASK) {
		dev_warn(&rdev->dev, "Invalid enable while %s is under HW control\n",
				sc->rdesc.name);
		mutex_unlock(&gdsc_seq_lock);
		return -EBUSY;
		ret = -EBUSY;
		goto end;
	}

	if (sc->toggle_logic) {
@@ -238,9 +250,7 @@ static int gdsc_enable(struct regulator_dev *rdev)
					dev_err(&rdev->dev, "%s final state (after additional %d us timeout): 0x%x, GDS_HW_CTRL: 0x%x\n",
						sc->rdesc.name, sc->gds_timeout,
						regval, hw_ctrl_regval);

					mutex_unlock(&gdsc_seq_lock);
					return ret;
					goto end;
				}
			} else {
				dev_err(&rdev->dev, "%s enable timed out: 0x%x\n",
@@ -252,10 +262,7 @@ static int gdsc_enable(struct regulator_dev *rdev)
				dev_err(&rdev->dev, "%s final state: 0x%x (%d us after timeout)\n",
					sc->rdesc.name, regval,
					sc->gds_timeout);

				mutex_unlock(&gdsc_seq_lock);

				return ret;
				goto end;
			}
		}
	} else {
@@ -279,6 +286,9 @@ static int gdsc_enable(struct regulator_dev *rdev)
		clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);

	sc->is_gdsc_enabled = true;
end:
	if (sc->parent_regulator)
		regulator_set_voltage(sc->parent_regulator, 0, INT_MAX);

	mutex_unlock(&gdsc_seq_lock);

@@ -293,6 +303,15 @@ static int gdsc_disable(struct regulator_dev *rdev)

	mutex_lock(&gdsc_seq_lock);

	if (sc->parent_regulator) {
		ret = regulator_set_voltage(sc->parent_regulator,
				RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX);
		if (ret) {
			mutex_unlock(&gdsc_seq_lock);
			return ret;
		}
	}

	if (sc->force_root_en)
		clk_prepare_enable(sc->clocks[sc->root_clk_idx]);

@@ -341,6 +360,9 @@ static int gdsc_disable(struct regulator_dev *rdev)
	if ((sc->is_gdsc_enabled && sc->root_en) || sc->force_root_en)
		clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);

	if (sc->parent_regulator)
		regulator_set_voltage(sc->parent_regulator, 0, INT_MAX);

	sc->is_gdsc_enabled = false;

	mutex_unlock(&gdsc_seq_lock);
@@ -371,6 +393,15 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode)

	mutex_lock(&gdsc_seq_lock);

	if (sc->parent_regulator) {
		ret = regulator_set_voltage(sc->parent_regulator,
				RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX);
		if (ret) {
			mutex_unlock(&gdsc_seq_lock);
			return ret;
		}
	}

	regmap_read(sc->regmap, REG_OFFSET, &regval);

	switch (mode) {
@@ -414,6 +445,9 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode)
		break;
	}

	if (sc->parent_regulator)
		regulator_set_voltage(sc->parent_regulator, 0, INT_MAX);

	mutex_unlock(&gdsc_seq_lock);

	return ret;
@@ -530,6 +564,19 @@ static int gdsc_probe(struct platform_device *pdev)
	sc->force_root_en = of_property_read_bool(pdev->dev.of_node,
						"qcom,force-enable-root-clk");

	if (of_find_property(pdev->dev.of_node, "vdd_parent-supply", NULL)) {
		sc->parent_regulator = devm_regulator_get(&pdev->dev,
							"vdd_parent");
		if (IS_ERR(sc->parent_regulator)) {
			ret = PTR_ERR(sc->parent_regulator);
			if (ret != -EPROBE_DEFER)
				dev_err(&pdev->dev,
				"Unable to get vdd_parent regulator, err: %d\n",
					ret);
			return ret;
		}
	}

	for (i = 0; i < sc->clock_count; i++) {
		const char *clock_name;