diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2014-06-02 13:19:29 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-06-05 17:34:07 -0400 |
commit | 00917ddc7c2fa7d2790241ed3570e8e4a0466923 (patch) | |
tree | b186a5e397ea7abe4e5531f6432ca010a222a374 | |
parent | 1c03a2d04d7ab6d27c1fef8614f08187d974bd21 (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.c | 97 |
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; | |||
45 | static struct clk *pll_x_clk; | 45 | static struct clk *pll_x_clk; |
46 | static struct clk *pll_p_clk; | 46 | static struct clk *pll_p_clk; |
47 | static struct clk *emc_clk; | 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 | 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 | ||
80 | out: | ||
81 | clk_disable_unprepare(pll_x_clk); | ||
82 | return ret; | 86 | return ret; |
83 | } | 87 | } |
84 | 88 | ||
85 | static int tegra_target(struct cpufreq_policy *policy, unsigned int index) | 89 | static 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 | ||
139 | static struct cpufreq_driver tegra_cpufreq_driver = { | 164 | static 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 | ||