]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
cpufreq: Add boost frequency support in core
authorLukasz Majewski <l.majewski@samsung.com>
Fri, 20 Dec 2013 14:24:49 +0000 (15:24 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 17 Jan 2014 01:00:44 +0000 (02:00 +0100)
This commit adds boost frequency support in cpufreq core (Hardware &
Software). Some SoCs (like Exynos4 - e.g. 4x12) allow setting frequency
above its normal operation limits. Such mode shall be only used for a
short time.

Overclocking (boost) support is essentially provided by platform
dependent cpufreq driver.

This commit unifies support for SW and HW (Intel) overclocking solutions
in the core cpufreq driver. Previously the "boost" sysfs attribute was
defined in the ACPI processor driver code. By default boost is disabled.
One global attribute is available at: /sys/devices/system/cpu/cpufreq/boost.

It only shows up when cpufreq driver supports overclocking.
Under the hood frequencies dedicated for boosting are marked with a
special flag (CPUFREQ_BOOST_FREQ) at driver's frequency table.
It is the user's concern to enable/disable overclocking with a proper call
to sysfs.

The cpufreq_boost_trigger_state() function is defined non static on purpose.
It is used later with thermal subsystem to provide automatic enable/disable
of the BOOST feature.

Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/cpufreq/cpufreq.c
drivers/cpufreq/freq_table.c
include/linux/cpufreq.h

index d7efdfe0c12cba32f6232dc162b6641d07af3504..08ca8c9f41cdeb27f6e4bb4879b2912798e1a72e 100644 (file)
@@ -352,6 +352,33 @@ 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)
 {
@@ -2183,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                *
  *********************************************************************/
@@ -2223,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;
@@ -2252,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;
@@ -2278,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);
index a8ac0427fbfeb215f00de7a645b69c5e6d51bf63..8e54f97899ba6feb7225275b7ddf2473ff8dc35b 100644 (file)
@@ -32,6 +32,10 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
 
                        continue;
                }
+               if (!cpufreq_boost_enabled()
+                   && table[i].driver_data == CPUFREQ_BOOST_FREQ)
+                       continue;
+
                pr_debug("table entry %u: %u kHz, %u driver_data\n",
                                        i, freq, table[i].driver_data);
                if (freq < min_freq)
@@ -204,7 +208,8 @@ static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
 /**
  * show_available_freqs - show available frequencies for the specified CPU
  */
-static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
+                                   bool show_boost)
 {
        unsigned int i = 0;
        unsigned int cpu = policy->cpu;
@@ -219,6 +224,20 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
        for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
                if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
                        continue;
+               /*
+                * show_boost = true and driver_data = BOOST freq
+                * display BOOST freqs
+                *
+                * show_boost = false and driver_data = BOOST freq
+                * show_boost = true and driver_data != BOOST freq
+                * continue - do not display anything
+                *
+                * show_boost = false and driver_data != BOOST freq
+                * display NON BOOST freqs
+                */
+               if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ))
+                       continue;
+
                count += sprintf(&buf[count], "%d ", table[i].frequency);
        }
        count += sprintf(&buf[count], "\n");
@@ -227,16 +246,39 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
 
 }
 
-struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
-       .attr = { .name = "scaling_available_frequencies",
-                 .mode = 0444,
-               },
-       .show = show_available_freqs,
-};
+#define cpufreq_attr_available_freq(_name)       \
+struct freq_attr cpufreq_freq_attr_##_name##_freqs =     \
+__ATTR_RO(_name##_frequencies)
+
+/**
+ * show_scaling_available_frequencies - show available normal frequencies for
+ * the specified CPU
+ */
+static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
+                                                 char *buf)
+{
+       return show_available_freqs(policy, buf, false);
+}
+cpufreq_attr_available_freq(scaling_available);
 EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
 
+/**
+ * show_available_boost_freqs - show available boost frequencies for
+ * the specified CPU
+ */
+static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
+                                             char *buf)
+{
+       return show_available_freqs(policy, buf, true);
+}
+cpufreq_attr_available_freq(scaling_boost);
+EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
+
 struct freq_attr *cpufreq_generic_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
+#ifdef CONFIG_CPU_FREQ_BOOST_SW
+       &cpufreq_freq_attr_scaling_boost_freqs,
+#endif
        NULL,
 };
 EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
index 422f10561e0b4a3884890cf8280f0f9040e51885..4d89e0e6f9ccaa641dd2a6b2c35c8c7b7bbf2916 100644 (file)
@@ -227,6 +227,11 @@ struct cpufreq_driver {
        int     (*suspend)      (struct cpufreq_policy *policy);
        int     (*resume)       (struct cpufreq_policy *policy);
        struct freq_attr        **attr;
+
+       /* platform specific boost support code */
+       bool                    boost_supported;
+       bool                    boost_enabled;
+       int     (*set_boost)    (int state);
 };
 
 /* flags */
@@ -435,6 +440,7 @@ extern struct cpufreq_governor cpufreq_gov_conservative;
 
 #define CPUFREQ_ENTRY_INVALID ~0
 #define CPUFREQ_TABLE_END     ~1
+#define CPUFREQ_BOOST_FREQ    ~2
 
 struct cpufreq_frequency_table {
        unsigned int    driver_data; /* driver specific data, not used by core */
@@ -460,6 +466,24 @@ int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
 void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy);
 ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf);
 
+#ifdef CONFIG_CPU_FREQ
+int cpufreq_boost_trigger_state(int state);
+int cpufreq_boost_supported(void);
+int cpufreq_boost_enabled(void);
+#else
+static inline int cpufreq_boost_trigger_state(int state)
+{
+       return 0;
+}
+static inline int cpufreq_boost_supported(void)
+{
+       return 0;
+}
+static inline int cpufreq_boost_enabled(void)
+{
+       return 0;
+}
+#endif
 /* the following funtion is for cpufreq core use only */
 struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu);