#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/cpufreq.h>
+#ifdef CONFIG_ARCH_ROCKCHIP
+#include <linux/input.h>
+#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
int boostpulse_duration_val;
/* End time of boost pulse in ktime converted to usecs */
u64 boostpulse_endtime;
+#ifdef CONFIG_ARCH_ROCKCHIP
+ /* Frequency to which a touch boost takes the cpus to */
+ unsigned long touchboost_freq;
+ /* Duration of a touchboost pulse in usecs */
+ int touchboostpulse_duration_val;
+ /* End time of touchboost pulse in ktime converted to usecs */
+ u64 touchboostpulse_endtime;
+#endif
bool boosted;
/*
* Max additional time to wait in idle, beyond timer_rate, at speeds
bool io_is_busy;
};
-/*
- * HACK: FIXME: Bring back cpufreq_{get,put}_global_kobject()
- * definition removed by upstream commit 8eec1020f0c0 "cpufreq:
- * create cpu/cpufreq at boot time" to fix build failures.
- */
-static int cpufreq_global_kobject_usage;
-
-int cpufreq_get_global_kobject(void)
-{
- if (!cpufreq_global_kobject_usage++)
- return kobject_add(cpufreq_global_kobject,
- &cpu_subsys.dev_root->kobj, "%s", "cpufreq");
-
- return 0;
-}
-
-void cpufreq_put_global_kobject(void)
-{
- if (!--cpufreq_global_kobject_usage)
- kobject_del(cpufreq_global_kobject);
-}
-
/* For cases where we have single governor instance for system */
static struct cpufreq_interactive_tunables *common_tunables;
pcpu->policy->cur < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
-
+#ifdef CONFIG_ARCH_ROCKCHIP
+ if ((now < tunables->touchboostpulse_endtime) &&
+ (new_freq < tunables->touchboost_freq)) {
+ new_freq = tunables->touchboost_freq;
+ }
+#endif
if (pcpu->policy->cur >= tunables->hispeed_freq &&
new_freq > pcpu->policy->cur &&
now - pcpu->pol_hispeed_val_time <
up_read(&pcpu->enable_sem);
}
+static void cpufreq_interactive_get_policy_info(struct cpufreq_policy *policy,
+ unsigned int *pmax_freq,
+ u64 *phvt, u64 *pfvt)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ unsigned int max_freq = 0;
+ u64 hvt = ~0ULL, fvt = 0;
+ unsigned int i;
+
+ for_each_cpu(i, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, i);
+
+ fvt = max(fvt, pcpu->loc_floor_val_time);
+ if (pcpu->target_freq > max_freq) {
+ max_freq = pcpu->target_freq;
+ hvt = pcpu->loc_hispeed_val_time;
+ } else if (pcpu->target_freq == max_freq) {
+ hvt = min(hvt, pcpu->loc_hispeed_val_time);
+ }
+ }
+
+ *pmax_freq = max_freq;
+ *phvt = hvt;
+ *pfvt = fvt;
+}
+
+static void cpufreq_interactive_adjust_cpu(unsigned int cpu,
+ struct cpufreq_policy *policy)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ u64 hvt, fvt;
+ unsigned int max_freq;
+ int i;
+
+ cpufreq_interactive_get_policy_info(policy, &max_freq, &hvt, &fvt);
+
+ for_each_cpu(i, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, i);
+ pcpu->pol_floor_val_time = fvt;
+ }
+
+ if (max_freq != policy->cur) {
+ __cpufreq_driver_target(policy, max_freq, CPUFREQ_RELATION_H);
+ for_each_cpu(i, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, i);
+ pcpu->pol_hispeed_val_time = hvt;
+ }
+ }
+
+ trace_cpufreq_interactive_setspeed(cpu, max_freq, policy->cur);
+}
+
static int cpufreq_interactive_speedchange_task(void *data)
{
unsigned int cpu;
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
for_each_cpu(cpu, &tmp_mask) {
- unsigned int j;
- unsigned int max_freq = 0;
- struct cpufreq_interactive_cpuinfo *pjcpu;
- u64 hvt = ~0ULL, fvt = 0;
-
pcpu = &per_cpu(cpuinfo, cpu);
- if (!down_read_trylock(&pcpu->enable_sem))
- continue;
- if (!pcpu->governor_enabled) {
- up_read(&pcpu->enable_sem);
- continue;
- }
-
- for_each_cpu(j, pcpu->policy->cpus) {
- pjcpu = &per_cpu(cpuinfo, j);
- fvt = max(fvt, pjcpu->loc_floor_val_time);
- if (pjcpu->target_freq > max_freq) {
- max_freq = pjcpu->target_freq;
- hvt = pjcpu->loc_hispeed_val_time;
- } else if (pjcpu->target_freq == max_freq) {
- hvt = min(hvt, pjcpu->loc_hispeed_val_time);
- }
- }
- for_each_cpu(j, pcpu->policy->cpus) {
- pjcpu = &per_cpu(cpuinfo, j);
- pjcpu->pol_floor_val_time = fvt;
- }
+ down_write(&pcpu->policy->rwsem);
- if (max_freq != pcpu->policy->cur) {
- __cpufreq_driver_target(pcpu->policy,
- max_freq,
- CPUFREQ_RELATION_H);
- for_each_cpu(j, pcpu->policy->cpus) {
- pjcpu = &per_cpu(cpuinfo, j);
- pjcpu->pol_hispeed_val_time = hvt;
- }
+ if (likely(down_read_trylock(&pcpu->enable_sem))) {
+ if (likely(pcpu->governor_enabled))
+ cpufreq_interactive_adjust_cpu(cpu,
+ pcpu->policy);
+ up_read(&pcpu->enable_sem);
}
- trace_cpufreq_interactive_setspeed(cpu,
- pcpu->target_freq,
- pcpu->policy->cur);
- up_read(&pcpu->enable_sem);
+ up_write(&pcpu->policy->rwsem);
}
}
for_each_online_cpu(i) {
pcpu = &per_cpu(cpuinfo, i);
- if (tunables != pcpu->policy->governor_data)
+
+ if (!down_read_trylock(&pcpu->enable_sem))
+ continue;
+
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ continue;
+ }
+
+ if (tunables != pcpu->policy->governor_data) {
+ up_read(&pcpu->enable_sem);
continue;
+ }
spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
if (pcpu->target_freq < tunables->hispeed_freq) {
anyboost = 1;
}
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
+
+ up_read(&pcpu->enable_sem);
}
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);
.notifier_call = cpufreq_interactive_idle_notifier,
};
+#ifdef CONFIG_ARCH_ROCKCHIP
+static void cpufreq_interactive_input_event(struct input_handle *handle,
+ unsigned int type,
+ unsigned int code,
+ int value)
+{
+ u64 now, endtime;
+ int i;
+ int anyboost = 0;
+ unsigned long flags[2];
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ struct cpufreq_interactive_tunables *tunables;
+
+ if ((type != EV_ABS) && (type != EV_KEY))
+ return;
+
+ trace_cpufreq_interactive_boost("touch");
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]);
+
+ now = ktime_to_us(ktime_get());
+ for_each_online_cpu(i) {
+ pcpu = &per_cpu(cpuinfo, i);
+ if (!pcpu->policy)
+ continue;
+
+ if (have_governor_per_policy())
+ tunables = pcpu->policy->governor_data;
+ else
+ tunables = common_tunables;
+ if (!tunables)
+ continue;
+
+ endtime = now + tunables->touchboostpulse_duration_val;
+ if (endtime < (tunables->touchboostpulse_endtime +
+ 10 * USEC_PER_MSEC))
+ continue;
+ tunables->touchboostpulse_endtime = endtime;
+
+ spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
+ if (pcpu->target_freq < tunables->touchboost_freq) {
+ pcpu->target_freq = tunables->touchboost_freq;
+ cpumask_set_cpu(i, &speedchange_cpumask);
+ pcpu->loc_hispeed_val_time =
+ ktime_to_us(ktime_get());
+ anyboost = 1;
+ }
+
+ pcpu->floor_freq = tunables->touchboost_freq;
+ pcpu->loc_floor_val_time = ktime_to_us(ktime_get());
+
+ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
+ }
+
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);
+
+ if (anyboost)
+ wake_up_process(speedchange_task);
+}
+
+static int cpufreq_interactive_input_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "cpufreq";
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err2;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err1;
+
+ return 0;
+err1:
+ input_unregister_handle(handle);
+err2:
+ kfree(handle);
+ return error;
+}
+
+static void cpufreq_interactive_input_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id cpufreq_interactive_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+ BIT_MASK(ABS_MT_POSITION_X) |
+ BIT_MASK(ABS_MT_POSITION_Y) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_KEYBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+ .absbit = { [BIT_WORD(ABS_X)] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+
+static struct input_handler cpufreq_interactive_input_handler = {
+ .event = cpufreq_interactive_input_event,
+ .connect = cpufreq_interactive_input_connect,
+ .disconnect = cpufreq_interactive_input_disconnect,
+ .name = "cpufreq_interactive",
+ .id_table = cpufreq_interactive_ids,
+};
+
+static void rockchip_cpufreq_policy_init(struct cpufreq_policy *policy)
+{
+ struct cpufreq_interactive_tunables *tunables = policy->governor_data;
+
+ tunables->min_sample_time = 40 * USEC_PER_MSEC;
+ tunables->boostpulse_duration_val = 40 * USEC_PER_MSEC;
+ if (policy->cpu == 0) {
+ tunables->hispeed_freq = 1008000;
+ tunables->touchboostpulse_duration_val = 500 * USEC_PER_MSEC;
+ tunables->touchboost_freq = 1200000;
+ } else {
+ tunables->hispeed_freq = 816000;
+ }
+}
+#endif
+
static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
unsigned int event)
{
policy->governor_data = tunables;
if (!have_governor_per_policy()) {
common_tunables = tunables;
- WARN_ON(cpufreq_get_global_kobject());
}
+#ifdef CONFIG_ARCH_ROCKCHIP
+ rockchip_cpufreq_policy_init(policy);
+#endif
+
rc = sysfs_create_group(get_governor_parent_kobj(policy),
get_sysfs_attr());
if (rc) {
policy->governor_data = NULL;
if (!have_governor_per_policy()) {
common_tunables = NULL;
- cpufreq_put_global_kobject();
}
return rc;
}
idle_notifier_register(&cpufreq_interactive_idle_nb);
cpufreq_register_notifier(&cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
+#ifdef CONFIG_ARCH_ROCKCHIP
+ rc = input_register_handler(&cpufreq_interactive_input_handler);
+#endif
+
}
break;
case CPUFREQ_GOV_POLICY_EXIT:
if (!--tunables->usage_count) {
if (policy->governor->initialized == 1) {
+#ifdef CONFIG_ARCH_ROCKCHIP
+ input_unregister_handler(&cpufreq_interactive_input_handler);
+#endif
cpufreq_unregister_notifier(&cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
idle_notifier_unregister(&cpufreq_interactive_idle_nb);
sysfs_remove_group(get_governor_parent_kobj(policy),
get_sysfs_attr());
- if (!have_governor_per_policy())
- cpufreq_put_global_kobject();
-
kfree(tunables);
common_tunables = NULL;
}