|
@@ -39,7 +39,7 @@ static struct cpufreq_driver *cpufreq_driver;
|
|
|
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
|
|
|
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
|
|
|
static DEFINE_RWLOCK(cpufreq_driver_lock);
|
|
|
-static DEFINE_MUTEX(cpufreq_governor_lock);
|
|
|
+DEFINE_MUTEX(cpufreq_governor_lock);
|
|
|
static LIST_HEAD(cpufreq_policy_list);
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
@@ -176,6 +176,20 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cpufreq_generic_init);
|
|
|
|
|
|
+unsigned int cpufreq_generic_get(unsigned int cpu)
|
|
|
+{
|
|
|
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
+
|
|
|
+ if (!policy || IS_ERR(policy->clk)) {
|
|
|
+ pr_err("%s: No %s associated to cpu: %d\n", __func__,
|
|
|
+ policy ? "clk" : "policy", cpu);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return clk_get_rate(policy->clk) / 1000;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cpufreq_generic_get);
|
|
|
+
|
|
|
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
|
|
|
{
|
|
|
struct cpufreq_policy *policy = NULL;
|
|
@@ -320,10 +334,51 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
|
|
|
|
|
|
+/* Do post notifications when there are chances that transition has failed */
|
|
|
+void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
|
|
|
+ struct cpufreq_freqs *freqs, int transition_failed)
|
|
|
+{
|
|
|
+ cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
|
|
|
+ if (!transition_failed)
|
|
|
+ return;
|
|
|
+
|
|
|
+ swap(freqs->old, freqs->new);
|
|
|
+ cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
|
|
|
+ cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition);
|
|
|
+
|
|
|
|
|
|
/*********************************************************************
|
|
|
* SYSFS INTERFACE *
|
|
|
*********************************************************************/
|
|
|
+ssize_t show_boost(struct kobject *kobj,
|
|
|
+ struct attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ return sprintf(buf, "%d\n", cpufreq_driver->boost_enabled);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t store_boost(struct kobject *kobj, struct attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ int ret, enable;
|
|
|
+
|
|
|
+ ret = sscanf(buf, "%d", &enable);
|
|
|
+ if (ret != 1 || enable < 0 || enable > 1)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (cpufreq_boost_trigger_state(enable)) {
|
|
|
+ pr_err("%s: Cannot %s BOOST!\n", __func__,
|
|
|
+ enable ? "enable" : "disable");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("%s: cpufreq BOOST %s\n", __func__,
|
|
|
+ enable ? "enabled" : "disabled");
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+define_one_global_rw(boost);
|
|
|
|
|
|
static struct cpufreq_governor *__find_governor(const char *str_governor)
|
|
|
{
|
|
@@ -929,6 +984,9 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
|
|
|
struct kobject *kobj;
|
|
|
struct completion *cmp;
|
|
|
|
|
|
+ blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
+ CPUFREQ_REMOVE_POLICY, policy);
|
|
|
+
|
|
|
down_read(&policy->rwsem);
|
|
|
kobj = &policy->kobj;
|
|
|
cmp = &policy->kobj_unregister;
|
|
@@ -1051,6 +1109,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
|
|
|
goto err_set_policy_cpu;
|
|
|
}
|
|
|
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ for_each_cpu(j, policy->cpus)
|
|
|
+ per_cpu(cpufreq_cpu_data, j) = policy;
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
if (cpufreq_driver->get) {
|
|
|
policy->cur = cpufreq_driver->get(policy->cpu);
|
|
|
if (!policy->cur) {
|
|
@@ -1059,6 +1122,46 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Sometimes boot loaders set CPU frequency to a value outside of
|
|
|
+ * frequency table present with cpufreq core. In such cases CPU might be
|
|
|
+ * unstable if it has to run on that frequency for long duration of time
|
|
|
+ * and so its better to set it to a frequency which is specified in
|
|
|
+ * freq-table. This also makes cpufreq stats inconsistent as
|
|
|
+ * cpufreq-stats would fail to register because current frequency of CPU
|
|
|
+ * isn't found in freq-table.
|
|
|
+ *
|
|
|
+ * Because we don't want this change to effect boot process badly, we go
|
|
|
+ * for the next freq which is >= policy->cur ('cur' must be set by now,
|
|
|
+ * otherwise we will end up setting freq to lowest of the table as 'cur'
|
|
|
+ * is initialized to zero).
|
|
|
+ *
|
|
|
+ * We are passing target-freq as "policy->cur - 1" otherwise
|
|
|
+ * __cpufreq_driver_target() would simply fail, as policy->cur will be
|
|
|
+ * equal to target-freq.
|
|
|
+ */
|
|
|
+ if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK)
|
|
|
+ && has_target()) {
|
|
|
+ /* Are we running at unknown frequency ? */
|
|
|
+ ret = cpufreq_frequency_table_get_index(policy, policy->cur);
|
|
|
+ if (ret == -EINVAL) {
|
|
|
+ /* Warn user and fix it */
|
|
|
+ pr_warn("%s: CPU%d: Running at unlisted freq: %u KHz\n",
|
|
|
+ __func__, policy->cpu, policy->cur);
|
|
|
+ ret = __cpufreq_driver_target(policy, policy->cur - 1,
|
|
|
+ CPUFREQ_RELATION_L);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Reaching here after boot in a few seconds may not
|
|
|
+ * mean that system will remain stable at "unknown"
|
|
|
+ * frequency for longer duration. Hence, a BUG_ON().
|
|
|
+ */
|
|
|
+ BUG_ON(ret);
|
|
|
+ pr_warn("%s: CPU%d: Unlisted initial frequency changed to: %u KHz\n",
|
|
|
+ __func__, policy->cpu, policy->cur);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* related cpus should atleast have policy->cpus */
|
|
|
cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);
|
|
|
|
|
@@ -1085,15 +1188,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
- for_each_cpu(j, policy->cpus)
|
|
|
- per_cpu(cpufreq_cpu_data, j) = policy;
|
|
|
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-
|
|
|
if (!frozen) {
|
|
|
ret = cpufreq_add_dev_interface(policy, dev);
|
|
|
if (ret)
|
|
|
goto err_out_unregister;
|
|
|
+ blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
+ CPUFREQ_CREATE_POLICY, policy);
|
|
|
}
|
|
|
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
@@ -1115,12 +1215,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
|
|
|
return 0;
|
|
|
|
|
|
err_out_unregister:
|
|
|
+err_get_freq:
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
for_each_cpu(j, policy->cpus)
|
|
|
per_cpu(cpufreq_cpu_data, j) = NULL;
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
-err_get_freq:
|
|
|
if (cpufreq_driver->exit)
|
|
|
cpufreq_driver->exit(policy);
|
|
|
err_set_policy_cpu:
|
|
@@ -1725,17 +1825,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
|
|
pr_err("%s: Failed to change cpu frequency: %d\n",
|
|
|
__func__, retval);
|
|
|
|
|
|
- if (notify) {
|
|
|
- /*
|
|
|
- * Notify with old freq in case we failed to change
|
|
|
- * frequency
|
|
|
- */
|
|
|
- if (retval)
|
|
|
- freqs.new = freqs.old;
|
|
|
-
|
|
|
- cpufreq_notify_transition(policy, &freqs,
|
|
|
- CPUFREQ_POSTCHANGE);
|
|
|
- }
|
|
|
+ if (notify)
|
|
|
+ cpufreq_notify_post_transition(policy, &freqs, retval);
|
|
|
}
|
|
|
|
|
|
out:
|
|
@@ -2119,6 +2210,73 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = {
|
|
|
.notifier_call = cpufreq_cpu_callback,
|
|
|
};
|
|
|
|
|
|
+/*********************************************************************
|
|
|
+ * BOOST *
|
|
|
+ *********************************************************************/
|
|
|
+static int cpufreq_boost_set_sw(int state)
|
|
|
+{
|
|
|
+ struct cpufreq_frequency_table *freq_table;
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
+ int ret = -EINVAL;
|
|
|
+
|
|
|
+ list_for_each_entry(policy, &cpufreq_policy_list, policy_list) {
|
|
|
+ freq_table = cpufreq_frequency_get_table(policy->cpu);
|
|
|
+ if (freq_table) {
|
|
|
+ ret = cpufreq_frequency_table_cpuinfo(policy,
|
|
|
+ freq_table);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Policy frequency update failed\n",
|
|
|
+ __func__);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ policy->user_policy.max = policy->max;
|
|
|
+ __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int cpufreq_boost_trigger_state(int state)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (cpufreq_driver->boost_enabled == state)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ cpufreq_driver->boost_enabled = state;
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ ret = cpufreq_driver->set_boost(state);
|
|
|
+ if (ret) {
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ cpufreq_driver->boost_enabled = !state;
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ pr_err("%s: Cannot %s BOOST\n", __func__,
|
|
|
+ state ? "enable" : "disable");
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int cpufreq_boost_supported(void)
|
|
|
+{
|
|
|
+ if (likely(cpufreq_driver))
|
|
|
+ return cpufreq_driver->boost_supported;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cpufreq_boost_supported);
|
|
|
+
|
|
|
+int cpufreq_boost_enabled(void)
|
|
|
+{
|
|
|
+ return cpufreq_driver->boost_enabled;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cpufreq_boost_enabled);
|
|
|
+
|
|
|
/*********************************************************************
|
|
|
* REGISTER / UNREGISTER CPUFREQ DRIVER *
|
|
|
*********************************************************************/
|
|
@@ -2159,9 +2317,25 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
|
|
|
cpufreq_driver = driver_data;
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
+ if (cpufreq_boost_supported()) {
|
|
|
+ /*
|
|
|
+ * Check if driver provides function to enable boost -
|
|
|
+ * if not, use cpufreq_boost_set_sw as default
|
|
|
+ */
|
|
|
+ if (!cpufreq_driver->set_boost)
|
|
|
+ cpufreq_driver->set_boost = cpufreq_boost_set_sw;
|
|
|
+
|
|
|
+ ret = cpufreq_sysfs_create_file(&boost.attr);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: cannot register global BOOST sysfs file\n",
|
|
|
+ __func__);
|
|
|
+ goto err_null_driver;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
ret = subsys_interface_register(&cpufreq_interface);
|
|
|
if (ret)
|
|
|
- goto err_null_driver;
|
|
|
+ goto err_boost_unreg;
|
|
|
|
|
|
if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) {
|
|
|
int i;
|
|
@@ -2188,6 +2362,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
|
|
|
return 0;
|
|
|
err_if_unreg:
|
|
|
subsys_interface_unregister(&cpufreq_interface);
|
|
|
+err_boost_unreg:
|
|
|
+ if (cpufreq_boost_supported())
|
|
|
+ cpufreq_sysfs_remove_file(&boost.attr);
|
|
|
err_null_driver:
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
cpufreq_driver = NULL;
|
|
@@ -2214,6 +2391,9 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
|
|
|
pr_debug("unregistering driver %s\n", driver->name);
|
|
|
|
|
|
subsys_interface_unregister(&cpufreq_interface);
|
|
|
+ if (cpufreq_boost_supported())
|
|
|
+ cpufreq_sysfs_remove_file(&boost.attr);
|
|
|
+
|
|
|
unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
|
|
|
|
|
|
down_write(&cpufreq_rwsem);
|