[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20201217180638.22748-25-digetx@gmail.com>
Date: Thu, 17 Dec 2020 21:06:14 +0300
From: Dmitry Osipenko <digetx@...il.com>
To: Thierry Reding <thierry.reding@...il.com>,
Jonathan Hunter <jonathanh@...dia.com>,
Mark Brown <broonie@...nel.org>,
Liam Girdwood <lgirdwood@...il.com>,
Ulf Hansson <ulf.hansson@...aro.org>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Rob Herring <robh+dt@...nel.org>,
Peter Geis <pgwipeout@...il.com>,
Nicolas Chauvet <kwizart@...il.com>,
Krzysztof Kozlowski <krzk@...nel.org>,
"Rafael J. Wysocki" <rjw@...ysocki.net>,
Kevin Hilman <khilman@...nel.org>,
Peter De Schrijver <pdeschrijver@...dia.com>,
Viresh Kumar <vireshk@...nel.org>,
Stephen Boyd <sboyd@...nel.org>,
Michael Turquette <mturquette@...libre.com>
Cc: devel@...verdev.osuosl.org, linux-kernel@...r.kernel.org,
devicetree@...r.kernel.org, dri-devel@...ts.freedesktop.org,
linux-media@...r.kernel.org, linux-tegra@...r.kernel.org,
linux-clk@...r.kernel.org
Subject: [PATCH v2 24/48] soc/tegra: pmc: Ensure that clock rates aren't too high
Switch all clocks of a power domain to a safe rate which is suitable
for all possible voltages in order to ensure that hardware constraints
aren't violated when power domain state toggles.
Signed-off-by: Dmitry Osipenko <digetx@...il.com>
---
drivers/soc/tegra/pmc.c | 92 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 90 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 985373ce52b1..8edcd5b319f6 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -237,6 +237,7 @@ struct tegra_powergate {
unsigned int id;
struct clk **clks;
unsigned int num_clks;
+ unsigned long *clk_rates;
struct reset_control *reset;
};
@@ -586,6 +587,57 @@ static int __tegra_powergate_remove_clamping(struct tegra_pmc *pmc,
return 0;
}
+static int tegra_powergate_prepare_clocks(struct tegra_powergate *pg)
+{
+ unsigned long safe_rate = 100 * 1000 * 1000;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < pg->num_clks; i++) {
+ pg->clk_rates[i] = clk_get_rate(pg->clks[i]);
+
+ if (!pg->clk_rates[i]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (pg->clk_rates[i] <= safe_rate)
+ continue;
+
+ /*
+ * We don't know whether voltage state is okay for the
+ * current clock rate, hence it's better to temporally
+ * switch clock to a safe rate which is suitable for
+ * all voltages, before enabling the clock.
+ */
+ err = clk_set_rate(pg->clks[i], safe_rate);
+ if (err)
+ goto out;
+ }
+
+ return 0;
+
+out:
+ while (i--)
+ clk_set_rate(pg->clks[i], pg->clk_rates[i]);
+
+ return err;
+}
+
+static int tegra_powergate_unprepare_clocks(struct tegra_powergate *pg)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < pg->num_clks; i++) {
+ err = clk_set_rate(pg->clks[i], pg->clk_rates[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static void tegra_powergate_disable_clocks(struct tegra_powergate *pg)
{
unsigned int i;
@@ -636,10 +688,14 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
usleep_range(10, 20);
- err = tegra_powergate_enable_clocks(pg);
+ err = tegra_powergate_prepare_clocks(pg);
if (err)
goto powergate_off;
+ err = tegra_powergate_enable_clocks(pg);
+ if (err)
+ goto unprepare_clks;
+
usleep_range(10, 20);
err = __tegra_powergate_remove_clamping(pg->pmc, pg->id);
@@ -670,12 +726,19 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
if (disable_clocks)
tegra_powergate_disable_clocks(pg);
+ err = tegra_powergate_unprepare_clocks(pg);
+ if (err)
+ return err;
+
return 0;
disable_clks:
tegra_powergate_disable_clocks(pg);
usleep_range(10, 20);
+unprepare_clks:
+ tegra_powergate_unprepare_clocks(pg);
+
powergate_off:
tegra_powergate_set(pg->pmc, pg->id, false);
@@ -686,10 +749,14 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
{
int err;
- err = tegra_powergate_enable_clocks(pg);
+ err = tegra_powergate_prepare_clocks(pg);
if (err)
return err;
+ err = tegra_powergate_enable_clocks(pg);
+ if (err)
+ goto unprepare_clks;
+
usleep_range(10, 20);
err = reset_control_assert(pg->reset);
@@ -706,6 +773,10 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
if (err)
goto assert_resets;
+ err = tegra_powergate_unprepare_clocks(pg);
+ if (err)
+ return err;
+
return 0;
assert_resets:
@@ -717,6 +788,9 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
disable_clks:
tegra_powergate_disable_clocks(pg);
+unprepare_clks:
+ tegra_powergate_unprepare_clocks(pg);
+
return err;
}
@@ -834,6 +908,12 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
if (!pg)
return -ENOMEM;
+ pg->clk_rates = kzalloc(sizeof(*pg->clk_rates), GFP_KERNEL);
+ if (!pg->clk_rates) {
+ kfree(pg->clks);
+ return -ENOMEM;
+ }
+
pg->id = id;
pg->clks = &clk;
pg->num_clks = 1;
@@ -845,6 +925,7 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
dev_err(pmc->dev, "failed to turn on partition %d: %d\n", id,
err);
+ kfree(pg->clk_rates);
kfree(pg);
return err;
@@ -995,6 +1076,12 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
if (!pg->clks)
return -ENOMEM;
+ pg->clk_rates = kcalloc(count, sizeof(*pg->clk_rates), GFP_KERNEL);
+ if (!pg->clk_rates) {
+ kfree(pg->clks);
+ return -ENOMEM;
+ }
+
for (i = 0; i < count; i++) {
pg->clks[i] = of_clk_get(np, i);
if (IS_ERR(pg->clks[i])) {
@@ -1011,6 +1098,7 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
while (i--)
clk_put(pg->clks[i]);
+ kfree(pg->clk_rates);
kfree(pg->clks);
return err;
--
2.29.2
Powered by blists - more mailing lists