From f7cfcd2f7e3678f199306894dd8eaaf195cc998e Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Wed, 30 Aug 2023 03:21:57 +0300
Subject: [PATCH 3/4] clk: qcom: mmcc-msm8996: introduce turnaround time

In order to prevent the GX GDSC from toggling too fast, introduce the
turnaround time. Delay turning GDSC on if it was turned off lately
enough.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 kernel/drivers/clk/qcom/gdsc.c         | 39 ++++++++++++++++++++++++++
 kernel/drivers/clk/qcom/gdsc.h         |  3 ++
 kernel/drivers/clk/qcom/mmcc-msm8996.c |  1 +
 3 files changed, 43 insertions(+)

diff --git a/kernel/drivers/clk/qcom/gdsc.c b/kernel/drivers/clk/qcom/gdsc.c
index cf23cfd7e467..6c4349336b0c 100644
--- a/kernel/drivers/clk/qcom/gdsc.c
+++ b/kernel/drivers/clk/qcom/gdsc.c
@@ -237,11 +237,47 @@ static void gdsc_retain_ff_on(struct gdsc *sc)
 	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);
 }
 
+static void gdsc_enable_delay(unsigned int delay)
+{
+	unsigned int ms = delay / 1000;
+	unsigned int us = delay % 1000;
+
+	if (ms > 0) {
+		/*
+		 * For small enough values, handle super-millisecond
+		 * delays in the usleep_range() call below.
+		 */
+		if (ms < 20)
+			us += ms * 1000;
+		else
+			msleep(ms);
+	}
+
+	/*
+	 * Give the scheduler some room to coalesce with any other
+	 * wakeup sources. For delays shorter than 10 us, don't even
+	 * bother setting up high-resolution timers and just busy-
+	 * loop.
+	 */
+	if (us >= 10)
+		usleep_range(us, us + 100);
+	else
+		udelay(us);
+}
+
 static int gdsc_enable(struct generic_pm_domain *domain)
 {
 	struct gdsc *sc = domain_to_gdsc(domain);
 	int ret;
 
+	if (sc->off_on_delay && sc->last_off) {
+		ktime_t end = ktime_add_us(sc->last_off, sc->off_on_delay);
+		s64 remain = ktime_us_delta(end, ktime_get());
+
+		if (remain > 0)
+			gdsc_enable_delay(remain);
+	}
+
 	if (sc->pwrsts == PWRSTS_ON)
 		return gdsc_deassert_reset(sc);
 
@@ -331,6 +367,9 @@ static int gdsc_disable(struct generic_pm_domain *domain)
 	if (sc->flags & CLAMP_IO)
 		gdsc_assert_clamp_io(sc);
 
+	if (sc->off_on_delay)
+		sc->last_off = ktime_get();
+
 	return 0;
 }
 
diff --git a/kernel/drivers/clk/qcom/gdsc.h b/kernel/drivers/clk/qcom/gdsc.h
index 762f1b5e1ec5..196b6d22a069 100644
--- a/kernel/drivers/clk/qcom/gdsc.h
+++ b/kernel/drivers/clk/qcom/gdsc.h
@@ -64,6 +64,9 @@ struct gdsc {
 
 	const char 			*supply;
 	struct regulator		*rsupply;
+
+	u64				off_on_delay;
+	ktime_t				last_off;
 };
 
 struct gdsc_desc {
diff --git a/kernel/drivers/clk/qcom/mmcc-msm8996.c b/kernel/drivers/clk/qcom/mmcc-msm8996.c
index 24843e4f2599..985fa34ca23c 100644
--- a/kernel/drivers/clk/qcom/mmcc-msm8996.c
+++ b/kernel/drivers/clk/qcom/mmcc-msm8996.c
@@ -3074,6 +3074,7 @@ static struct gdsc gpu_gx_gdsc = {
 	.parent = &gpu_gdsc.pd,
 	.flags = CLAMP_IO,
 	.supply = "vdd-gfx",
+	.off_on_delay = 250 * 1000,
 };
 
 static struct clk_regmap *mmcc_msm8996_clocks[] = {
-- 
2.39.2

