Skip to content

Commit

Permalink
cpufreq: Add boost frequency support in core
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lukasz Majewski authored and Rafael J. Wysocki committed Jan 17, 2014
1 parent b69880f commit 6f19efc
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 8 deletions.
118 changes: 117 additions & 1 deletion drivers/cpufreq/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 *
*********************************************************************/
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down
56 changes: 49 additions & 7 deletions drivers/cpufreq/freq_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand All @@ -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);
Expand Down
24 changes: 24 additions & 0 deletions include/linux/cpufreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand All @@ -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);

Expand Down

0 comments on commit 6f19efc

Please sign in to comment.