
Apparently not all GDSCs are the same. In Linux driver, depending on which GDSC flags are set, different status register is checked when powering it on/off. And on top of that, different bit inside that register is tested.
Port missing parts from Linux driver to U-Boot: - add GDSC flags; - adjust logic when checking GDSC status to match one in Linux driver.
This fixes e.g. SDM660's USB_30_GDSC to be forever stuck during power on, since unlike SDM845 one it doesn't have POLL_CFG_GDSCR flag.
Even though only POLL_CFG_GDSCR is the important one here, add all flags from Linux driver, to make it easier to compare which flags are needed for which GDSC, making porting process easier.
Signed-off-by: Alexey Minnekhanov alexeymin@postmarketos.org --- drivers/clk/qcom/clock-qcom.c | 45 ++++++++++++++++++++++++----------- drivers/clk/qcom/clock-qcom.h | 16 ++++++++++++- 2 files changed, 46 insertions(+), 15 deletions(-)
diff --git a/drivers/clk/qcom/clock-qcom.c b/drivers/clk/qcom/clock-qcom.c index 1cfc430c14a5..bf9249b55af3 100644 --- a/drivers/clk/qcom/clock-qcom.c +++ b/drivers/clk/qcom/clock-qcom.c @@ -461,7 +461,7 @@ static int qcom_power_set(struct power_domain *pwr, bool on) struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(pwr->dev); void __iomem *base = dev_get_priv(pwr->dev); const struct qcom_power_map *map; - u32 value; + u32 value, status_reg; int ret;
if (pwr->id >= data->num_power_domains) @@ -481,19 +481,36 @@ static int qcom_power_set(struct power_domain *pwr, bool on)
writel(value, base + map->reg);
- if (on) - ret = readl_poll_timeout(base + map->reg + CFG_GDSCR_OFFSET, - value, - (value & GDSC_POWER_UP_COMPLETE) || - (value & GDSC_PWR_ON_MASK), - GDSC_STATUS_POLL_TIMEOUT_US); - - else - ret = readl_poll_timeout(base + map->reg + CFG_GDSCR_OFFSET, - value, - (value & GDSC_POWER_DOWN_COMPLETE) || - !(value & GDSC_PWR_ON_MASK), - GDSC_STATUS_POLL_TIMEOUT_US); + /* depending on the type of gdsc the status register is different */ + /* and we need to check different status bit */ + if (map->flags & POLL_CFG_GDSCR) { + status_reg = map->reg + CFG_GDSCR_OFFSET; + + if (on) + ret = readl_poll_timeout(base + status_reg, + value, + (value & GDSC_POWER_UP_COMPLETE), + GDSC_STATUS_POLL_TIMEOUT_US); + else + ret = readl_poll_timeout(base + status_reg, + value, + (value & GDSC_POWER_DOWN_COMPLETE), + GDSC_STATUS_POLL_TIMEOUT_US); + } else { + status_reg = map->reg; + + if (on) + ret = readl_poll_timeout(base + status_reg, + value, + (value & GDSC_PWR_ON_MASK), + GDSC_STATUS_POLL_TIMEOUT_US); + + else + ret = readl_poll_timeout(base + status_reg, + value, + !(value & GDSC_PWR_ON_MASK), + GDSC_STATUS_POLL_TIMEOUT_US); + }
if (ret == -ETIMEDOUT) printf("WARNING: GDSC %lu is stuck during power o%s\n", diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h index 78d9b1d81ece..84965b2555c7 100644 --- a/drivers/clk/qcom/clock-qcom.h +++ b/drivers/clk/qcom/clock-qcom.h @@ -63,8 +63,22 @@ struct qcom_reset_map { u8 bit; };
+enum qcom_gdsc_flags { + VOTABLE = BIT(0), + CLAMP_IO = BIT(1), + HW_CTRL = BIT(2), + SW_RESET = BIT(3), + AON_RESET = BIT(4), + POLL_CFG_GDSCR = BIT(5), + ALWAYS_ON = BIT(6), + RETAIN_FF_ENABLE = BIT(7), + NO_RET_PERIPH = BIT(8), + HW_CTRL_TRIGGER = BIT(9), +}; + struct qcom_power_map { - unsigned int reg; + unsigned int reg; /* GDSCR */ + unsigned int flags; };
struct clk;