Commit 66ee50c6 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding
Browse files

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: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent c45e66a6
Loading
Loading
Loading
Loading
+90 −2
Original line number Diff line number Diff line
@@ -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;