提交 66ee50c6 编写于 作者: D Dmitry Osipenko 提交者: Thierry Reding

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.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: NDmitry Osipenko <digetx@gmail.com>
Signed-off-by: NThierry Reding <treding@nvidia.com>
上级 c45e66a6
......@@ -258,6 +258,7 @@ struct tegra_powergate {
unsigned int id;
struct clk **clks;
unsigned int num_clks;
unsigned long *clk_rates;
struct reset_control *reset;
};
......@@ -663,6 +664,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;
......@@ -713,10 +765,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);
......@@ -739,12 +795,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);
......@@ -755,10 +818,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);
......@@ -775,6 +842,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:
......@@ -786,6 +857,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;
}
......@@ -903,6 +977,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;
......@@ -914,6 +994,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;
......@@ -1064,6 +1145,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])) {
......@@ -1080,6 +1167,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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册