aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2014-06-02 13:19:29 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-06-05 17:34:07 -0400
commit00917ddc7c2fa7d2790241ed3570e8e4a0466923 (patch)
treeb186a5e397ea7abe4e5531f6432ca010a222a374
parent1c03a2d04d7ab6d27c1fef8614f08187d974bd21 (diff)
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>
-rw-r--r--drivers/cpufreq/tegra-cpufreq.c97
1 files changed, 62 insertions, 35 deletions
diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c
index 6e774c6ac20b..a5fbc0a8d897 100644
--- a/drivers/cpufreq/tegra-cpufreq.c
+++ b/drivers/cpufreq/tegra-cpufreq.c
@@ -45,46 +45,51 @@ static struct clk *cpu_clk;
45static struct clk *pll_x_clk; 45static struct clk *pll_x_clk;
46static struct clk *pll_p_clk; 46static struct clk *pll_p_clk;
47static struct clk *emc_clk; 47static struct clk *emc_clk;
48static bool pll_x_prepared;
48 49
49static int tegra_cpu_clk_set_rate(unsigned long rate) 50static 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
66static int tegra_target_intermediate(struct cpufreq_policy *policy,
67 unsigned int index)
50{ 68{
51 int ret; 69 int ret;
52 70
53 /* 71 /*
54 * Take an extra reference to the main pll so it doesn't turn 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 clk_prepare_enable(pll_x_clk); 78 clk_prepare_enable(pll_x_clk);
58 79
59 ret = clk_set_parent(cpu_clk, pll_p_clk); 80 ret = clk_set_parent(cpu_clk, pll_p_clk);
60 if (ret) { 81 if (ret)
61 pr_err("Failed to switch cpu to clock pll_p\n"); 82 clk_disable_unprepare(pll_x_clk);
62 goto out; 83 else
63 } 84 pll_x_prepared = true;
64
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 85
80out:
81 clk_disable_unprepare(pll_x_clk);
82 return ret; 86 return ret;
83} 87}
84 88
85static int tegra_target(struct cpufreq_policy *policy, unsigned int index) 89static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
86{ 90{
87 unsigned long rate = freq_table[index].frequency; 91 unsigned long rate = freq_table[index].frequency;
92 unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
88 int ret = 0; 93 int ret = 0;
89 94
90 /* 95 /*
@@ -98,10 +103,30 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
98 else 103 else
99 clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ 104 clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
100 105
101 ret = tegra_cpu_clk_set_rate(rate * 1000); 106 /*
107 * target freq == pll_p, don't need to take extra reference to pll_x_clk
108 * as it isn't used anymore.
109 */
110 if (rate == ifreq)
111 return clk_set_parent(cpu_clk, pll_p_clk);
112
113 ret = clk_set_rate(pll_x_clk, rate * 1000);
114 /* Restore to earlier frequency on error, i.e. pll_x */
102 if (ret) 115 if (ret)
103 pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n", 116 pr_err("Failed to change pll_x to %lu\n", rate);
104 rate); 117
118 ret = clk_set_parent(cpu_clk, pll_x_clk);
119 /* This shouldn't fail while changing or restoring */
120 WARN_ON(ret);
121
122 /*
123 * Drop count to pll_x clock only if we switched to intermediate freq
124 * earlier while transitioning to a target frequency.
125 */
126 if (pll_x_prepared) {
127 clk_disable_unprepare(pll_x_clk);
128 pll_x_prepared = false;
129 }
105 130
106 return ret; 131 return ret;
107} 132}
@@ -137,16 +162,18 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
137} 162}
138 163
139static struct cpufreq_driver tegra_cpufreq_driver = { 164static struct cpufreq_driver tegra_cpufreq_driver = {
140 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 165 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
141 .verify = cpufreq_generic_frequency_table_verify, 166 .verify = cpufreq_generic_frequency_table_verify,
142 .target_index = tegra_target, 167 .get_intermediate = tegra_get_intermediate,
143 .get = cpufreq_generic_get, 168 .target_intermediate = tegra_target_intermediate,
144 .init = tegra_cpu_init, 169 .target_index = tegra_target,
145 .exit = tegra_cpu_exit, 170 .get = cpufreq_generic_get,
146 .name = "tegra", 171 .init = tegra_cpu_init,
147 .attr = cpufreq_generic_attr, 172 .exit = tegra_cpu_exit,
173 .name = "tegra",
174 .attr = cpufreq_generic_attr,
148#ifdef CONFIG_PM 175#ifdef CONFIG_PM
149 .suspend = cpufreq_generic_suspend, 176 .suspend = cpufreq_generic_suspend,
150#endif 177#endif
151}; 178};
152 179