cpufreq: cpufreq_interactive: avoid NULL point access
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / cpufreq_interactive.c
index bd83be39f17bb7ad1f04947579c4854c6f181adc..055e990a2f1db035cbbff273fccefe8c0dfd13c8 100644 (file)
@@ -19,6 +19,9 @@
 #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>
@@ -106,6 +109,14 @@ struct cpufreq_interactive_tunables {
        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
@@ -116,28 +127,6 @@ struct cpufreq_interactive_tunables {
        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;
 
@@ -405,7 +394,12 @@ static void cpufreq_interactive_timer(unsigned long data)
                                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 <
@@ -1155,6 +1149,150 @@ static struct notifier_block cpufreq_interactive_idle_nb = {
        .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)
 {
@@ -1206,9 +1344,12 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                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) {
@@ -1216,7 +1357,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        policy->governor_data = NULL;
                        if (!have_governor_per_policy()) {
                                common_tunables = NULL;
-                               cpufreq_put_global_kobject();
                        }
                        return rc;
                }
@@ -1225,6 +1365,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        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;
@@ -1232,6 +1376,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
        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);
@@ -1240,9 +1387,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        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;
                }