[PATCH 2/4] clk/qcom: Treat different GDSCs didderently

Alexey Minnekhanov alexeymin at postmarketos.org
Wed Jan 8 12:59:09 CET 2025


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 at 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;
-- 
2.45.2



More information about the U-Boot mailing list