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: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
c45e66a6b9
commit
66ee50c6e2
@ -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 @@ out:
|
||||
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 @@ assert_resets:
|
||||
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 @@ err:
|
||||
while (i--)
|
||||
clk_put(pg->clks[i]);
|
||||
|
||||
kfree(pg->clk_rates);
|
||||
kfree(pg->clks);
|
||||
|
||||
return err;
|
||||
|
Loading…
Reference in New Issue
Block a user