From: Jagadeesh Kona <quic_jkona@xxxxxxxxxxx> Add support for set and get hwmode callbacks to switch the GDSC between SW and HW modes. Currently, the GDSC is moved to HW control mode using HW_CTRL flag and if this flag is present, GDSC is moved to HW mode as part of GDSC enable itself. The intention is to keep the HW_CTRL flag functionality as is, since many older chipsets still use this flag. Introduce a new HW_CTRL_TRIGGER flag to switch the GDSC back and forth between HW/SW modes dynamically at runtime. If HW_CTRL_TRIGGER flag is present, register set_hwmode_dev callback to switch the GDSC mode which can be invoked from consumer drivers using dev_pm_genpd_set_hwmode function. Unlike HW_CTRL flag, HW_CTRL_TRIGGER won't move the GDSC to HW control mode as part of GDSC enable itself, GDSC will be moved to HW control mode only when consumer driver explicity calls dev_pm_genpd_set_hwmode to switch to HW mode. Also add the dev_pm_genpd_get_hwmode to allow the consumers to read the actual HW/SW mode from hardware. Signed-off-by: Jagadeesh Kona <quic_jkona@xxxxxxxxxxx> Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxx> --- drivers/clk/qcom/gdsc.c | 32 ++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 1 + 2 files changed, 33 insertions(+) diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 5358e28122ab..3e4a721f1605 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -363,6 +363,34 @@ static int gdsc_disable(struct generic_pm_domain *domain) return 0; } +static int gdsc_set_mode(struct generic_pm_domain *domain, struct device *dev, bool mode) +{ + struct gdsc *sc = domain_to_gdsc(domain); + + if (sc->rsupply && !regulator_is_enabled(sc->rsupply)) { + pr_err("Cannot set mode while parent is disabled\n"); + return -EIO; + } + + return gdsc_hwctrl(sc, mode); +} + +static bool gdsc_get_mode(struct generic_pm_domain *domain, struct device *dev) +{ + struct gdsc *sc = domain_to_gdsc(domain); + u32 val; + int ret; + + ret = regmap_read(sc->regmap, sc->gdscr, &val); + if (ret) + return ret; + + if (val & HW_CONTROL_MASK) + return true; + + return false; +} + static int gdsc_init(struct gdsc *sc) { u32 mask, val; @@ -451,6 +479,10 @@ static int gdsc_init(struct gdsc *sc) sc->pd.power_off = gdsc_disable; if (!sc->pd.power_on) sc->pd.power_on = gdsc_enable; + if (sc->flags & HW_CTRL_TRIGGER) { + sc->pd.set_hwmode_dev = gdsc_set_mode; + sc->pd.get_hwmode_dev = gdsc_get_mode; + } ret = pm_genpd_init(&sc->pd, NULL, !on); if (ret) diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index 803512688336..1e2779b823d1 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -67,6 +67,7 @@ struct gdsc { #define ALWAYS_ON BIT(6) #define RETAIN_FF_ENABLE BIT(7) #define NO_RET_PERIPH BIT(8) +#define HW_CTRL_TRIGGER BIT(9) struct reset_controller_dev *rcdev; unsigned int *resets; unsigned int reset_count; -- 2.34.1