Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpufreq: Tegra: implement intermediate frequency callbacks

Tegra has been switching to intermediate frequency (pll_p_clk) forever.
CPUFreq core has better support for handling notifications for these
frequencies and so we can adapt Tegra's driver to it.

Also do a WARN() if clk_set_parent() fails while moving back to pll_x
as we should have atleast restored to earlier frequency on error.

Tested-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Viresh Kumar and committed by
Rafael J. Wysocki
00917ddc 1c03a2d0

+62 -35
+62 -35
drivers/cpufreq/tegra-cpufreq.c
··· 45 45 static struct clk *pll_x_clk; 46 46 static struct clk *pll_p_clk; 47 47 static struct clk *emc_clk; 48 + static bool pll_x_prepared; 48 49 49 - static int tegra_cpu_clk_set_rate(unsigned long rate) 50 + static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy, 51 + unsigned int index) 52 + { 53 + unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000; 54 + 55 + /* 56 + * Don't switch to intermediate freq if: 57 + * - we are already at it, i.e. policy->cur == ifreq 58 + * - index corresponds to ifreq 59 + */ 60 + if ((freq_table[index].frequency == ifreq) || (policy->cur == ifreq)) 61 + return 0; 62 + 63 + return ifreq; 64 + } 65 + 66 + static int tegra_target_intermediate(struct cpufreq_policy *policy, 67 + unsigned int index) 50 68 { 51 69 int ret; 52 70 53 71 /* 54 72 * Take an extra reference to the main pll so it doesn't turn 55 - * off when we move the cpu off of it 73 + * off when we move the cpu off of it as enabling it again while we 74 + * switch to it from tegra_target() would take additional time. Though 75 + * when target-freq is intermediate freq, we don't need to take this 76 + * reference. 56 77 */ 57 78 clk_prepare_enable(pll_x_clk); 58 79 59 80 ret = clk_set_parent(cpu_clk, pll_p_clk); 60 - if (ret) { 61 - pr_err("Failed to switch cpu to clock pll_p\n"); 62 - goto out; 63 - } 81 + if (ret) 82 + clk_disable_unprepare(pll_x_clk); 83 + else 84 + pll_x_prepared = true; 64 85 65 - if (rate == clk_get_rate(pll_p_clk)) 66 - goto out; 67 - 68 - ret = clk_set_rate(pll_x_clk, rate); 69 - if (ret) { 70 - pr_err("Failed to change pll_x to %lu\n", rate); 71 - goto out; 72 - } 73 - 74 - ret = clk_set_parent(cpu_clk, pll_x_clk); 75 - if (ret) { 76 - pr_err("Failed to switch cpu to clock pll_x\n"); 77 - goto out; 78 - } 79 - 80 - out: 81 - clk_disable_unprepare(pll_x_clk); 82 86 return ret; 83 87 } 84 88 85 89 static int tegra_target(struct cpufreq_policy *policy, unsigned int index) 86 90 { 87 91 unsigned long rate = freq_table[index].frequency; 92 + unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000; 88 93 int ret = 0; 89 94 90 95 /* ··· 103 98 else 104 99 clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ 105 100 106 - ret = tegra_cpu_clk_set_rate(rate * 1000); 101 + /* 102 + * target freq == pll_p, don't need to take extra reference to pll_x_clk 103 + * as it isn't used anymore. 104 + */ 105 + if (rate == ifreq) 106 + return clk_set_parent(cpu_clk, pll_p_clk); 107 + 108 + ret = clk_set_rate(pll_x_clk, rate * 1000); 109 + /* Restore to earlier frequency on error, i.e. pll_x */ 107 110 if (ret) 108 - pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n", 109 - rate); 111 + pr_err("Failed to change pll_x to %lu\n", rate); 112 + 113 + ret = clk_set_parent(cpu_clk, pll_x_clk); 114 + /* This shouldn't fail while changing or restoring */ 115 + WARN_ON(ret); 116 + 117 + /* 118 + * Drop count to pll_x clock only if we switched to intermediate freq 119 + * earlier while transitioning to a target frequency. 120 + */ 121 + if (pll_x_prepared) { 122 + clk_disable_unprepare(pll_x_clk); 123 + pll_x_prepared = false; 124 + } 110 125 111 126 return ret; 112 127 } ··· 162 137 } 163 138 164 139 static struct cpufreq_driver tegra_cpufreq_driver = { 165 - .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 166 - .verify = cpufreq_generic_frequency_table_verify, 167 - .target_index = tegra_target, 168 - .get = cpufreq_generic_get, 169 - .init = tegra_cpu_init, 170 - .exit = tegra_cpu_exit, 171 - .name = "tegra", 172 - .attr = cpufreq_generic_attr, 140 + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 141 + .verify = cpufreq_generic_frequency_table_verify, 142 + .get_intermediate = tegra_get_intermediate, 143 + .target_intermediate = tegra_target_intermediate, 144 + .target_index = tegra_target, 145 + .get = cpufreq_generic_get, 146 + .init = tegra_cpu_init, 147 + .exit = tegra_cpu_exit, 148 + .name = "tegra", 149 + .attr = cpufreq_generic_attr, 173 150 #ifdef CONFIG_PM 174 - .suspend = cpufreq_generic_suspend, 151 + .suspend = cpufreq_generic_suspend, 175 152 #endif 176 153 }; 177 154