diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 218 |
1 files changed, 199 insertions, 19 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8d19f7c06010..08ca8c9f41cd 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -39,7 +39,7 @@ static struct cpufreq_driver *cpufreq_driver; | |||
39 | static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); | 39 | static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); |
40 | static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); | 40 | static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); |
41 | static DEFINE_RWLOCK(cpufreq_driver_lock); | 41 | static DEFINE_RWLOCK(cpufreq_driver_lock); |
42 | static DEFINE_MUTEX(cpufreq_governor_lock); | 42 | DEFINE_MUTEX(cpufreq_governor_lock); |
43 | static LIST_HEAD(cpufreq_policy_list); | 43 | static LIST_HEAD(cpufreq_policy_list); |
44 | 44 | ||
45 | #ifdef CONFIG_HOTPLUG_CPU | 45 | #ifdef CONFIG_HOTPLUG_CPU |
@@ -176,6 +176,20 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, | |||
176 | } | 176 | } |
177 | EXPORT_SYMBOL_GPL(cpufreq_generic_init); | 177 | EXPORT_SYMBOL_GPL(cpufreq_generic_init); |
178 | 178 | ||
179 | unsigned int cpufreq_generic_get(unsigned int cpu) | ||
180 | { | ||
181 | struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); | ||
182 | |||
183 | if (!policy || IS_ERR(policy->clk)) { | ||
184 | pr_err("%s: No %s associated to cpu: %d\n", __func__, | ||
185 | policy ? "clk" : "policy", cpu); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | return clk_get_rate(policy->clk) / 1000; | ||
190 | } | ||
191 | EXPORT_SYMBOL_GPL(cpufreq_generic_get); | ||
192 | |||
179 | struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) | 193 | struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) |
180 | { | 194 | { |
181 | struct cpufreq_policy *policy = NULL; | 195 | struct cpufreq_policy *policy = NULL; |
@@ -320,10 +334,51 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, | |||
320 | } | 334 | } |
321 | EXPORT_SYMBOL_GPL(cpufreq_notify_transition); | 335 | EXPORT_SYMBOL_GPL(cpufreq_notify_transition); |
322 | 336 | ||
337 | /* Do post notifications when there are chances that transition has failed */ | ||
338 | void cpufreq_notify_post_transition(struct cpufreq_policy *policy, | ||
339 | struct cpufreq_freqs *freqs, int transition_failed) | ||
340 | { | ||
341 | cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE); | ||
342 | if (!transition_failed) | ||
343 | return; | ||
344 | |||
345 | swap(freqs->old, freqs->new); | ||
346 | cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE); | ||
347 | cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE); | ||
348 | } | ||
349 | EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition); | ||
350 | |||
323 | 351 | ||
324 | /********************************************************************* | 352 | /********************************************************************* |
325 | * SYSFS INTERFACE * | 353 | * SYSFS INTERFACE * |
326 | *********************************************************************/ | 354 | *********************************************************************/ |
355 | ssize_t show_boost(struct kobject *kobj, | ||
356 | struct attribute *attr, char *buf) | ||
357 | { | ||
358 | return sprintf(buf, "%d\n", cpufreq_driver->boost_enabled); | ||
359 | } | ||
360 | |||
361 | static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, | ||
362 | const char *buf, size_t count) | ||
363 | { | ||
364 | int ret, enable; | ||
365 | |||
366 | ret = sscanf(buf, "%d", &enable); | ||
367 | if (ret != 1 || enable < 0 || enable > 1) | ||
368 | return -EINVAL; | ||
369 | |||
370 | if (cpufreq_boost_trigger_state(enable)) { | ||
371 | pr_err("%s: Cannot %s BOOST!\n", __func__, | ||
372 | enable ? "enable" : "disable"); | ||
373 | return -EINVAL; | ||
374 | } | ||
375 | |||
376 | pr_debug("%s: cpufreq BOOST %s\n", __func__, | ||
377 | enable ? "enabled" : "disabled"); | ||
378 | |||
379 | return count; | ||
380 | } | ||
381 | define_one_global_rw(boost); | ||
327 | 382 | ||
328 | static struct cpufreq_governor *__find_governor(const char *str_governor) | 383 | static struct cpufreq_governor *__find_governor(const char *str_governor) |
329 | { | 384 | { |
@@ -929,6 +984,9 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) | |||
929 | struct kobject *kobj; | 984 | struct kobject *kobj; |
930 | struct completion *cmp; | 985 | struct completion *cmp; |
931 | 986 | ||
987 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, | ||
988 | CPUFREQ_REMOVE_POLICY, policy); | ||
989 | |||
932 | down_read(&policy->rwsem); | 990 | down_read(&policy->rwsem); |
933 | kobj = &policy->kobj; | 991 | kobj = &policy->kobj; |
934 | cmp = &policy->kobj_unregister; | 992 | cmp = &policy->kobj_unregister; |
@@ -1051,6 +1109,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, | |||
1051 | goto err_set_policy_cpu; | 1109 | goto err_set_policy_cpu; |
1052 | } | 1110 | } |
1053 | 1111 | ||
1112 | write_lock_irqsave(&cpufreq_driver_lock, flags); | ||
1113 | for_each_cpu(j, policy->cpus) | ||
1114 | per_cpu(cpufreq_cpu_data, j) = policy; | ||
1115 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | ||
1116 | |||
1054 | if (cpufreq_driver->get) { | 1117 | if (cpufreq_driver->get) { |
1055 | policy->cur = cpufreq_driver->get(policy->cpu); | 1118 | policy->cur = cpufreq_driver->get(policy->cpu); |
1056 | if (!policy->cur) { | 1119 | if (!policy->cur) { |
@@ -1059,6 +1122,46 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, | |||
1059 | } | 1122 | } |
1060 | } | 1123 | } |
1061 | 1124 | ||
1125 | /* | ||
1126 | * Sometimes boot loaders set CPU frequency to a value outside of | ||
1127 | * frequency table present with cpufreq core. In such cases CPU might be | ||
1128 | * unstable if it has to run on that frequency for long duration of time | ||
1129 | * and so its better to set it to a frequency which is specified in | ||
1130 | * freq-table. This also makes cpufreq stats inconsistent as | ||
1131 | * cpufreq-stats would fail to register because current frequency of CPU | ||
1132 | * isn't found in freq-table. | ||
1133 | * | ||
1134 | * Because we don't want this change to effect boot process badly, we go | ||
1135 | * for the next freq which is >= policy->cur ('cur' must be set by now, | ||
1136 | * otherwise we will end up setting freq to lowest of the table as 'cur' | ||
1137 | * is initialized to zero). | ||
1138 | * | ||
1139 | * We are passing target-freq as "policy->cur - 1" otherwise | ||
1140 | * __cpufreq_driver_target() would simply fail, as policy->cur will be | ||
1141 | * equal to target-freq. | ||
1142 | */ | ||
1143 | if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK) | ||
1144 | && has_target()) { | ||
1145 | /* Are we running at unknown frequency ? */ | ||
1146 | ret = cpufreq_frequency_table_get_index(policy, policy->cur); | ||
1147 | if (ret == -EINVAL) { | ||
1148 | /* Warn user and fix it */ | ||
1149 | pr_warn("%s: CPU%d: Running at unlisted freq: %u KHz\n", | ||
1150 | __func__, policy->cpu, policy->cur); | ||
1151 | ret = __cpufreq_driver_target(policy, policy->cur - 1, | ||
1152 | CPUFREQ_RELATION_L); | ||
1153 | |||
1154 | /* | ||
1155 | * Reaching here after boot in a few seconds may not | ||
1156 | * mean that system will remain stable at "unknown" | ||
1157 | * frequency for longer duration. Hence, a BUG_ON(). | ||
1158 | */ | ||
1159 | BUG_ON(ret); | ||
1160 | pr_warn("%s: CPU%d: Unlisted initial frequency changed to: %u KHz\n", | ||
1161 | __func__, policy->cpu, policy->cur); | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1062 | /* related cpus should atleast have policy->cpus */ | 1165 | /* related cpus should atleast have policy->cpus */ |
1063 | cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); | 1166 | cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); |
1064 | 1167 | ||
@@ -1085,15 +1188,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, | |||
1085 | } | 1188 | } |
1086 | #endif | 1189 | #endif |
1087 | 1190 | ||
1088 | write_lock_irqsave(&cpufreq_driver_lock, flags); | ||
1089 | for_each_cpu(j, policy->cpus) | ||
1090 | per_cpu(cpufreq_cpu_data, j) = policy; | ||
1091 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | ||
1092 | |||
1093 | if (!frozen) { | 1191 | if (!frozen) { |
1094 | ret = cpufreq_add_dev_interface(policy, dev); | 1192 | ret = cpufreq_add_dev_interface(policy, dev); |
1095 | if (ret) | 1193 | if (ret) |
1096 | goto err_out_unregister; | 1194 | goto err_out_unregister; |
1195 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, | ||
1196 | CPUFREQ_CREATE_POLICY, policy); | ||
1097 | } | 1197 | } |
1098 | 1198 | ||
1099 | write_lock_irqsave(&cpufreq_driver_lock, flags); | 1199 | write_lock_irqsave(&cpufreq_driver_lock, flags); |
@@ -1115,12 +1215,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, | |||
1115 | return 0; | 1215 | return 0; |
1116 | 1216 | ||
1117 | err_out_unregister: | 1217 | err_out_unregister: |
1218 | err_get_freq: | ||
1118 | write_lock_irqsave(&cpufreq_driver_lock, flags); | 1219 | write_lock_irqsave(&cpufreq_driver_lock, flags); |
1119 | for_each_cpu(j, policy->cpus) | 1220 | for_each_cpu(j, policy->cpus) |
1120 | per_cpu(cpufreq_cpu_data, j) = NULL; | 1221 | per_cpu(cpufreq_cpu_data, j) = NULL; |
1121 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | 1222 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); |
1122 | 1223 | ||
1123 | err_get_freq: | ||
1124 | if (cpufreq_driver->exit) | 1224 | if (cpufreq_driver->exit) |
1125 | cpufreq_driver->exit(policy); | 1225 | cpufreq_driver->exit(policy); |
1126 | err_set_policy_cpu: | 1226 | err_set_policy_cpu: |
@@ -1725,17 +1825,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, | |||
1725 | pr_err("%s: Failed to change cpu frequency: %d\n", | 1825 | pr_err("%s: Failed to change cpu frequency: %d\n", |
1726 | __func__, retval); | 1826 | __func__, retval); |
1727 | 1827 | ||
1728 | if (notify) { | 1828 | if (notify) |
1729 | /* | 1829 | cpufreq_notify_post_transition(policy, &freqs, retval); |
1730 | * Notify with old freq in case we failed to change | ||
1731 | * frequency | ||
1732 | */ | ||
1733 | if (retval) | ||
1734 | freqs.new = freqs.old; | ||
1735 | |||
1736 | cpufreq_notify_transition(policy, &freqs, | ||
1737 | CPUFREQ_POSTCHANGE); | ||
1738 | } | ||
1739 | } | 1830 | } |
1740 | 1831 | ||
1741 | out: | 1832 | out: |
@@ -2120,6 +2211,73 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = { | |||
2120 | }; | 2211 | }; |
2121 | 2212 | ||
2122 | /********************************************************************* | 2213 | /********************************************************************* |
2214 | * BOOST * | ||
2215 | *********************************************************************/ | ||
2216 | static int cpufreq_boost_set_sw(int state) | ||
2217 | { | ||
2218 | struct cpufreq_frequency_table *freq_table; | ||
2219 | struct cpufreq_policy *policy; | ||
2220 | int ret = -EINVAL; | ||
2221 | |||
2222 | list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { | ||
2223 | freq_table = cpufreq_frequency_get_table(policy->cpu); | ||
2224 | if (freq_table) { | ||
2225 | ret = cpufreq_frequency_table_cpuinfo(policy, | ||
2226 | freq_table); | ||
2227 | if (ret) { | ||
2228 | pr_err("%s: Policy frequency update failed\n", | ||
2229 | __func__); | ||
2230 | break; | ||
2231 | } | ||
2232 | policy->user_policy.max = policy->max; | ||
2233 | __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); | ||
2234 | } | ||
2235 | } | ||
2236 | |||
2237 | return ret; | ||
2238 | } | ||
2239 | |||
2240 | int cpufreq_boost_trigger_state(int state) | ||
2241 | { | ||
2242 | unsigned long flags; | ||
2243 | int ret = 0; | ||
2244 | |||
2245 | if (cpufreq_driver->boost_enabled == state) | ||
2246 | return 0; | ||
2247 | |||
2248 | write_lock_irqsave(&cpufreq_driver_lock, flags); | ||
2249 | cpufreq_driver->boost_enabled = state; | ||
2250 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | ||
2251 | |||
2252 | ret = cpufreq_driver->set_boost(state); | ||
2253 | if (ret) { | ||
2254 | write_lock_irqsave(&cpufreq_driver_lock, flags); | ||
2255 | cpufreq_driver->boost_enabled = !state; | ||
2256 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | ||
2257 | |||
2258 | pr_err("%s: Cannot %s BOOST\n", __func__, | ||
2259 | state ? "enable" : "disable"); | ||
2260 | } | ||
2261 | |||
2262 | return ret; | ||
2263 | } | ||
2264 | |||
2265 | int cpufreq_boost_supported(void) | ||
2266 | { | ||
2267 | if (likely(cpufreq_driver)) | ||
2268 | return cpufreq_driver->boost_supported; | ||
2269 | |||
2270 | return 0; | ||
2271 | } | ||
2272 | EXPORT_SYMBOL_GPL(cpufreq_boost_supported); | ||
2273 | |||
2274 | int cpufreq_boost_enabled(void) | ||
2275 | { | ||
2276 | return cpufreq_driver->boost_enabled; | ||
2277 | } | ||
2278 | EXPORT_SYMBOL_GPL(cpufreq_boost_enabled); | ||
2279 | |||
2280 | /********************************************************************* | ||
2123 | * REGISTER / UNREGISTER CPUFREQ DRIVER * | 2281 | * REGISTER / UNREGISTER CPUFREQ DRIVER * |
2124 | *********************************************************************/ | 2282 | *********************************************************************/ |
2125 | 2283 | ||
@@ -2159,9 +2317,25 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) | |||
2159 | cpufreq_driver = driver_data; | 2317 | cpufreq_driver = driver_data; |
2160 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); | 2318 | write_unlock_irqrestore(&cpufreq_driver_lock, flags); |
2161 | 2319 | ||
2320 | if (cpufreq_boost_supported()) { | ||
2321 | /* | ||
2322 | * Check if driver provides function to enable boost - | ||
2323 | * if not, use cpufreq_boost_set_sw as default | ||
2324 | */ | ||
2325 | if (!cpufreq_driver->set_boost) | ||
2326 | cpufreq_driver->set_boost = cpufreq_boost_set_sw; | ||
2327 | |||
2328 | ret = cpufreq_sysfs_create_file(&boost.attr); | ||
2329 | if (ret) { | ||
2330 | pr_err("%s: cannot register global BOOST sysfs file\n", | ||
2331 | __func__); | ||
2332 | goto err_null_driver; | ||
2333 | } | ||
2334 | } | ||
2335 | |||
2162 | ret = subsys_interface_register(&cpufreq_interface); | 2336 | ret = subsys_interface_register(&cpufreq_interface); |
2163 | if (ret) | 2337 | if (ret) |
2164 | goto err_null_driver; | 2338 | goto err_boost_unreg; |
2165 | 2339 | ||
2166 | if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) { | 2340 | if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) { |
2167 | int i; | 2341 | int i; |
@@ -2188,6 +2362,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) | |||
2188 | return 0; | 2362 | return 0; |
2189 | err_if_unreg: | 2363 | err_if_unreg: |
2190 | subsys_interface_unregister(&cpufreq_interface); | 2364 | subsys_interface_unregister(&cpufreq_interface); |
2365 | err_boost_unreg: | ||
2366 | if (cpufreq_boost_supported()) | ||
2367 | cpufreq_sysfs_remove_file(&boost.attr); | ||
2191 | err_null_driver: | 2368 | err_null_driver: |
2192 | write_lock_irqsave(&cpufreq_driver_lock, flags); | 2369 | write_lock_irqsave(&cpufreq_driver_lock, flags); |
2193 | cpufreq_driver = NULL; | 2370 | cpufreq_driver = NULL; |
@@ -2214,6 +2391,9 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) | |||
2214 | pr_debug("unregistering driver %s\n", driver->name); | 2391 | pr_debug("unregistering driver %s\n", driver->name); |
2215 | 2392 | ||
2216 | subsys_interface_unregister(&cpufreq_interface); | 2393 | subsys_interface_unregister(&cpufreq_interface); |
2394 | if (cpufreq_boost_supported()) | ||
2395 | cpufreq_sysfs_remove_file(&boost.attr); | ||
2396 | |||
2217 | unregister_hotcpu_notifier(&cpufreq_cpu_notifier); | 2397 | unregister_hotcpu_notifier(&cpufreq_cpu_notifier); |
2218 | 2398 | ||
2219 | down_write(&cpufreq_rwsem); | 2399 | down_write(&cpufreq_rwsem); |