MPDECISION: updated mpdecision, 8 steps, optimized for 8960 devices

file:3dfa4d89eb383dc701c17fa93e03cdd005958b0e -> file:311459d2d06175fd8cb248fd81f70a12aeb2ac84
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -1,10 +1,12 @@
/*
* arch/arm/mach-msm/msm_mpdecision.c
*
- * cpu auto-hotplug/unplug based on system load for MSM dualcore cpus
- * single core while screen is off
+ * This program features:
+ * -cpu auto-hotplug/unplug based on system load for MSM multicore cpus
+ * -single core while screen is off
+ * -extensive sysfs tuneables
*
- * Copyright (c) 2011, Chad Goodman <chad.goodman@gmail.com> - All Rights Reserved
+ * Copyright (c) 2011-2012, Chad Goodman <chad.goodman@gmail.com> - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,12 +33,13 @@
#include <asm-generic/cputime.h>
#include <linux/hrtimer.h>
#include <linux/delay.h>
-
#include "acpuclock.h"
-#define MPDEC_TAG "[AnthraX-MPD]: "
-#define MSM_MPDEC_STARTDELAY 70000
-#define MSM_MPDEC_DELAY 500
+#define DEBUG 0
+
+#define MPDEC_TAG "[AnThRaX MPDEC]: "
+#define MSM_MPDEC_STARTDELAY 20000
+#define MSM_MPDEC_DELAY 70
#define MSM_MPDEC_PAUSE 10000
#define MSM_MPDEC_IDLE_FREQ 486000
@@ -56,7 +59,8 @@ struct msm_mpdec_cpudata_t {
static DEFINE_PER_CPU(struct msm_mpdec_cpudata_t, msm_mpdec_cpudata);
static struct delayed_work msm_mpdec_work;
-static DEFINE_MUTEX(msm_cpu_lock);
+static struct workqueue_struct *msm_mpdec_workq;
+static DEFINE_MUTEX(mpdec_msm_cpu_lock);
static struct msm_mpdec_tuners {
unsigned int startdelay;
@@ -64,22 +68,75 @@ static struct msm_mpdec_tuners {
unsigned int pause;
bool scroff_single_core;
unsigned long int idle_freq;
+ unsigned int max_cpus;
+ unsigned int min_cpus;
} msm_mpdec_tuners_ins = {
.startdelay = MSM_MPDEC_STARTDELAY,
.delay = MSM_MPDEC_DELAY,
.pause = MSM_MPDEC_PAUSE,
.scroff_single_core = true,
.idle_freq = MSM_MPDEC_IDLE_FREQ,
+ .max_cpus = CONFIG_NR_CPUS,
+ .min_cpus = 1,
};
-static unsigned int NwNs_Threshold[4] = {35, 0, 0, 5};
-static unsigned int TwTs_Threshold[4] = {250, 0, 0, 250};
+static unsigned int NwNs_Threshold[8] = {19, 10, 28, 30, 32, 30, 0, 35};
+static unsigned int TwTs_Threshold[8] = {140, 0, 140, 190, 140, 190, 0, 190};
extern unsigned int get_rq_info(void);
+extern unsigned long acpuclk_get_rate(int);
unsigned int state = MSM_MPDEC_IDLE;
bool was_paused = false;
+static unsigned long get_rate(int cpu)
+{
+ return acpuclk_get_rate(cpu);
+}
+
+static int get_slowest_cpu(void)
+{
+ int i, cpu = 0;
+ unsigned long rate, slow_rate = 0;
+
+ for (i = 1; i < CONFIG_NR_CPUS; i++) {
+ if (!cpu_online(i))
+ continue;
+
+ rate = get_rate(i);
+ if (slow_rate == 0) {
+ slow_rate = rate;
+ }
+
+ if ((rate <= slow_rate) && (slow_rate != 0)) {
+ cpu = i;
+ slow_rate = rate;
+ }
+ }
+
+ return cpu;
+}
+
+static unsigned long get_slowest_cpu_rate(void)
+{
+ int i = 0;
+ unsigned long rate, slow_rate = 0;
+
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ if (!cpu_online(i))
+ continue;
+ rate = get_rate(i);
+ if ((rate < slow_rate) && (slow_rate != 0)) {
+ slow_rate = rate;
+ }
+ if (slow_rate == 0) {
+ slow_rate = rate;
+ }
+ }
+
+ return slow_rate;
+}
+
static int mp_decision(void)
{
static bool first_call = true;
@@ -111,20 +168,19 @@ static int mp_decision(void)
if (nr_cpu_online) {
index = (nr_cpu_online - 1) * 2;
- if ((nr_cpu_online < 2) && (rq_depth >= NwNs_Threshold[index])) {
- if (total_time >= TwTs_Threshold[index]) {
+ if ((nr_cpu_online < CONFIG_NR_CPUS) && (rq_depth >= NwNs_Threshold[index])) {
+ if ((total_time >= TwTs_Threshold[index]) &&
+ (nr_cpu_online < msm_mpdec_tuners_ins.max_cpus)) {
new_state = MSM_MPDEC_UP;
- if (acpuclk_get_rate((CONFIG_NR_CPUS - 2)) <=
- msm_mpdec_tuners_ins.idle_freq)
+ if (get_slowest_cpu_rate() <= msm_mpdec_tuners_ins.idle_freq)
new_state = MSM_MPDEC_IDLE;
}
- } else if (rq_depth <= NwNs_Threshold[index+1]) {
- if (total_time >= TwTs_Threshold[index+1] ) {
+ } else if ((nr_cpu_online > 1) && (rq_depth <= NwNs_Threshold[index+1])) {
+ if ((total_time >= TwTs_Threshold[index+1]) &&
+ (nr_cpu_online > msm_mpdec_tuners_ins.min_cpus)) {
new_state = MSM_MPDEC_DOWN;
- if (cpu_online((CONFIG_NR_CPUS - 1)))
- if (acpuclk_get_rate((CONFIG_NR_CPUS - 1)) >
- msm_mpdec_tuners_ins.idle_freq)
- new_state = MSM_MPDEC_IDLE;
+ if (get_slowest_cpu_rate() > msm_mpdec_tuners_ins.idle_freq)
+ new_state = MSM_MPDEC_IDLE;
}
} else {
new_state = MSM_MPDEC_IDLE;
@@ -139,7 +195,10 @@ static int mp_decision(void)
}
last_time = ktime_to_ms(ktime_get());
-
+#if DEBUG
+ pr_info(MPDEC_TAG"[DEBUG] rq: %u, new_state: %i | Mask=[%d%d%d%d]\n",
+ rq_depth, new_state, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3));
+#endif
return new_state;
}
@@ -147,13 +206,24 @@ static void msm_mpdec_work_thread(struct
{
unsigned int cpu = nr_cpu_ids;
cputime64_t on_time = 0;
+ bool suspended = false;
- if (per_cpu(msm_mpdec_cpudata, (CONFIG_NR_CPUS - 1)).device_suspended == true)
+ if (ktime_to_ms(ktime_get()) <= msm_mpdec_tuners_ins.startdelay)
+ goto out;
+
+ for_each_possible_cpu(cpu) {
+ if ((per_cpu(msm_mpdec_cpudata, cpu).device_suspended == true)) {
+ suspended = true;
+ break;
+ }
+ }
+ if (suspended == true)
goto out;
- if (!mutex_trylock(&msm_cpu_lock))
+ if (!mutex_trylock(&mpdec_msm_cpu_lock))
goto out;
+ /* if sth messed with the cpus, update the check vars so we can proceed */
if (was_paused) {
for_each_possible_cpu(cpu) {
if (cpu_online(cpu))
@@ -170,14 +240,14 @@ static void msm_mpdec_work_thread(struct
case MSM_MPDEC_IDLE:
break;
case MSM_MPDEC_DOWN:
- cpu = (CONFIG_NR_CPUS - 1);
+ cpu = get_slowest_cpu();
if (cpu < nr_cpu_ids) {
if ((per_cpu(msm_mpdec_cpudata, cpu).online == true) && (cpu_online(cpu))) {
cpu_down(cpu);
per_cpu(msm_mpdec_cpudata, cpu).online = false;
on_time = ktime_to_ms(ktime_get()) - per_cpu(msm_mpdec_cpudata, cpu).on_time;
- pr_info(MPDEC_TAG"CPU[%d] on->off | Mask=[%d%d] | time online: %llu\n",
- cpu, cpu_online(0), cpu_online(1), on_time);
+ pr_info(MPDEC_TAG"CPU[%d] on->off | Mask=[%d%d%d%d] | time online: %llu\n",
+ cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3), on_time);
} else if (per_cpu(msm_mpdec_cpudata, cpu).online != cpu_online(cpu)) {
pr_info(MPDEC_TAG"CPU[%d] was controlled outside of mpdecision! | pausing [%d]ms\n",
cpu, msm_mpdec_tuners_ins.pause);
@@ -187,14 +257,14 @@ static void msm_mpdec_work_thread(struct
}
break;
case MSM_MPDEC_UP:
- cpu = (CONFIG_NR_CPUS - 1);
+ cpu = cpumask_next_zero(0, cpu_online_mask);
if (cpu < nr_cpu_ids) {
if ((per_cpu(msm_mpdec_cpudata, cpu).online == false) && (!cpu_online(cpu))) {
cpu_up(cpu);
per_cpu(msm_mpdec_cpudata, cpu).online = true;
per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get());
- pr_info(MPDEC_TAG"CPU[%d] off->on | Mask=[%d%d]\n",
- cpu, cpu_online(0), cpu_online(1));
+ pr_info(MPDEC_TAG"CPU[%d] off->on | Mask=[%d%d%d%d]\n",
+ cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3));
} else if (per_cpu(msm_mpdec_cpudata, cpu).online != cpu_online(cpu)) {
pr_info(MPDEC_TAG"CPU[%d] was controlled outside of mpdecision! | pausing [%d]ms\n",
cpu, msm_mpdec_tuners_ins.pause);
@@ -207,47 +277,47 @@ static void msm_mpdec_work_thread(struct
pr_err(MPDEC_TAG"%s: invalid mpdec hotplug state %d\n",
__func__, state);
}
- mutex_unlock(&msm_cpu_lock);
+ mutex_unlock(&mpdec_msm_cpu_lock);
out:
if (state != MSM_MPDEC_DISABLED)
- schedule_delayed_work(&msm_mpdec_work,
+ queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work,
msecs_to_jiffies(msm_mpdec_tuners_ins.delay));
return;
}
static void msm_mpdec_early_suspend(struct early_suspend *h)
{
- int cpu = 0;
+ int cpu = nr_cpu_ids;
for_each_possible_cpu(cpu) {
mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex);
- if (((cpu >= (CONFIG_NR_CPUS - 1)) && (num_online_cpus() > 1)) && (msm_mpdec_tuners_ins.scroff_single_core)) {
- cpu_down(cpu);
- pr_info(MPDEC_TAG"Screen -> off. Suspended CPU%d | Mask=[%d%d]\n",
- cpu, cpu_online(0), cpu_online(1));
+ if ((cpu >= 1) && (cpu_online(cpu))) {
+ cpu_down(cpu);
+ pr_info(MPDEC_TAG"Screen -> off. Suspended CPU[%d] | Mask=[%d%d%d%d]\n",
+ cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3));
per_cpu(msm_mpdec_cpudata, cpu).online = false;
}
per_cpu(msm_mpdec_cpudata, cpu).device_suspended = true;
mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex);
}
+ /* main work thread can sleep now */
+ cancel_delayed_work_sync(&msm_mpdec_work);
+
+ pr_info(MPDEC_TAG"Screen -> off. Deactivated mpdecision.\n");
}
static void msm_mpdec_late_resume(struct early_suspend *h)
{
- int cpu = 0;
- for_each_possible_cpu(cpu) {
- mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex);
- if ((cpu >= (CONFIG_NR_CPUS - 1)) && (num_online_cpus() < CONFIG_NR_CPUS)) {
- /* Enable cpus when screen comes online. */
- cpu_up(cpu);
- per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get());
- per_cpu(msm_mpdec_cpudata, cpu).online = true;
- pr_info(MPDEC_TAG"Screen -> on. Hot plugged CPU%d | Mask=[%d%d]\n",
- cpu, cpu_online(0), cpu_online(1));
- }
+ int cpu = nr_cpu_ids;
+ for_each_possible_cpu(cpu)
per_cpu(msm_mpdec_cpudata, cpu).device_suspended = false;
- mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex);
- }
+
+ /* wake up main work thread */
+ queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work,
+ msecs_to_jiffies(msm_mpdec_tuners_ins.delay));
+
+ pr_info(MPDEC_TAG"Screen -> on. Activated mpdecision. | Mask=[%d%d%d%d]\n",
+ cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3));
}
static struct early_suspend msm_mpdec_early_suspend_handler = {
@@ -270,8 +340,85 @@ show_one(startdelay, startdelay);
show_one(delay, delay);
show_one(pause, pause);
show_one(scroff_single_core, scroff_single_core);
+show_one(min_cpus, min_cpus);
+show_one(max_cpus, max_cpus);
+
+#define show_one_twts(file_name, arraypos) \
+static ssize_t show_##file_name \
+(struct kobject *kobj, struct attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%u\n", TwTs_Threshold[arraypos]); \
+}
+show_one_twts(twts_threshold_0, 0);
+show_one_twts(twts_threshold_1, 1);
+show_one_twts(twts_threshold_2, 2);
+show_one_twts(twts_threshold_3, 3);
+show_one_twts(twts_threshold_4, 4);
+show_one_twts(twts_threshold_5, 5);
+show_one_twts(twts_threshold_6, 6);
+show_one_twts(twts_threshold_7, 7);
+
+#define store_one_twts(file_name, arraypos) \
+static ssize_t store_##file_name \
+(struct kobject *a, struct attribute *b, const char *buf, size_t count) \
+{ \
+ unsigned int input; \
+ int ret; \
+ ret = sscanf(buf, "%u", &input); \
+ if (ret != 1) \
+ return -EINVAL; \
+ TwTs_Threshold[arraypos] = input; \
+ return count; \
+} \
+define_one_global_rw(file_name);
+store_one_twts(twts_threshold_0, 0);
+store_one_twts(twts_threshold_1, 1);
+store_one_twts(twts_threshold_2, 2);
+store_one_twts(twts_threshold_3, 3);
+store_one_twts(twts_threshold_4, 4);
+store_one_twts(twts_threshold_5, 5);
+store_one_twts(twts_threshold_6, 6);
+store_one_twts(twts_threshold_7, 7);
+
+#define show_one_nwns(file_name, arraypos) \
+static ssize_t show_##file_name \
+(struct kobject *kobj, struct attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%u\n", NwNs_Threshold[arraypos]); \
+}
+show_one_nwns(nwns_threshold_0, 0);
+show_one_nwns(nwns_threshold_1, 1);
+show_one_nwns(nwns_threshold_2, 2);
+show_one_nwns(nwns_threshold_3, 3);
+show_one_nwns(nwns_threshold_4, 4);
+show_one_nwns(nwns_threshold_5, 5);
+show_one_nwns(nwns_threshold_6, 6);
+show_one_nwns(nwns_threshold_7, 7);
+
+#define store_one_nwns(file_name, arraypos) \
+static ssize_t store_##file_name \
+(struct kobject *a, struct attribute *b, const char *buf, size_t count) \
+{ \
+ unsigned int input; \
+ int ret; \
+ ret = sscanf(buf, "%u", &input); \
+ if (ret != 1) \
+ return -EINVAL; \
+ NwNs_Threshold[arraypos] = input; \
+ return count; \
+} \
+define_one_global_rw(file_name);
+store_one_nwns(nwns_threshold_0, 0);
+store_one_nwns(nwns_threshold_1, 1);
+store_one_nwns(nwns_threshold_2, 2);
+store_one_nwns(nwns_threshold_3, 3);
+store_one_nwns(nwns_threshold_4, 4);
+store_one_nwns(nwns_threshold_5, 5);
+store_one_nwns(nwns_threshold_6, 6);
+store_one_nwns(nwns_threshold_7, 7);
+
static ssize_t show_idle_freq (struct kobject *kobj, struct attribute *attr,
- char *buf)
+ char *buf)
{
return sprintf(buf, "%lu\n", msm_mpdec_tuners_ins.idle_freq);
}
@@ -295,30 +442,6 @@ static ssize_t show_enabled(struct kobje
return sprintf(buf, "%u\n", enabled);
}
-static ssize_t show_nwns_threshold_up(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", NwNs_Threshold[0]);
-}
-
-static ssize_t show_nwns_threshold_down(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", NwNs_Threshold[3]);
-}
-
-static ssize_t show_twts_threshold_up(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", TwTs_Threshold[0]);
-}
-
-static ssize_t show_twts_threshold_down(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", TwTs_Threshold[3]);
-}
-
static ssize_t store_startdelay(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
@@ -362,13 +485,14 @@ static ssize_t store_pause(struct kobjec
}
static ssize_t store_idle_freq(struct kobject *a, struct attribute *b,
- const char *buf, size_t count)
+ const char *buf, size_t count)
{
long unsigned int input;
int ret;
ret = sscanf(buf, "%lu", &input);
if (ret != 1)
return -EINVAL;
+
msm_mpdec_tuners_ins.idle_freq = input;
return count;
@@ -395,6 +519,34 @@ static ssize_t store_scroff_single_core(
return count;
}
+static ssize_t store_max_cpus(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+ if ((ret != 1) || input > CONFIG_NR_CPUS)
+ return -EINVAL;
+
+ msm_mpdec_tuners_ins.max_cpus = input;
+
+ return count;
+}
+
+static ssize_t store_min_cpus(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+ if ((ret != 1) || input < 1)
+ return -EINVAL;
+
+ msm_mpdec_tuners_ins.min_cpus = input;
+
+ return count;
+}
+
static ssize_t store_enabled(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
@@ -423,22 +575,24 @@ static ssize_t store_enabled(struct kobj
switch (buf[0]) {
case '0':
state = MSM_MPDEC_DISABLED;
- cpu = (CONFIG_NR_CPUS - 1);
- if (!cpu_online(cpu)) {
- per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get());
- per_cpu(msm_mpdec_cpudata, cpu).online = true;
- cpu_up(cpu);
- pr_info(MPDEC_TAG"disabled... Hot plugged CPU[%d] | Mask=[%d%d]\n",
- cpu, cpu_online(0), cpu_online(1));
- } else {
- pr_info(MPDEC_TAG"disabled...\n");
- }
+ pr_info(MPDEC_TAG"nap time... Hot plugging offline CPUs...\n");
+
+ for (cpu = 1; cpu < CONFIG_NR_CPUS; cpu++) {
+ if (!cpu_online(cpu)) {
+ per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get());
+ per_cpu(msm_mpdec_cpudata, cpu).online = true;
+ cpu_up(cpu);
+ pr_info(MPDEC_TAG" DISABLED... Hot plugged CPU[%d] | Mask=[%d%d%d%d]\n",
+ cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3));
+ }
+ }
break;
case '1':
state = MSM_MPDEC_IDLE;
was_paused = true;
- schedule_delayed_work(&msm_mpdec_work, 0);
- pr_info(MPDEC_TAG" mpdecision enabled...\n");
+ queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work,
+ msecs_to_jiffies(msm_mpdec_tuners_ins.delay));
+ pr_info(MPDEC_TAG" ENABLED mpdecision...\n");
break;
default:
ret = -EINVAL;
@@ -446,72 +600,14 @@ static ssize_t store_enabled(struct kobj
return count;
}
-static ssize_t store_nwns_threshold_up(struct kobject *a, struct attribute *b,
- const char *buf, size_t count)
-{
- unsigned int input;
- int ret;
- ret = sscanf(buf, "%u", &input);
- if (ret != 1)
- return -EINVAL;
-
- NwNs_Threshold[0] = input;
-
- return count;
-}
-
-static ssize_t store_nwns_threshold_down(struct kobject *a, struct attribute *b,
- const char *buf, size_t count)
-{
- unsigned int input;
- int ret;
- ret = sscanf(buf, "%u", &input);
- if (ret != 1)
- return -EINVAL;
-
- NwNs_Threshold[3] = input;
-
- return count;
-}
-
-static ssize_t store_twts_threshold_up(struct kobject *a, struct attribute *b,
- const char *buf, size_t count)
-{
- unsigned int input;
- int ret;
- ret = sscanf(buf, "%u", &input);
- if (ret != 1)
- return -EINVAL;
-
- TwTs_Threshold[0] = input;
-
- return count;
-}
-
-static ssize_t store_twts_threshold_down(struct kobject *a, struct attribute *b,
- const char *buf, size_t count)
-{
- unsigned int input;
- int ret;
- ret = sscanf(buf, "%u", &input);
- if (ret != 1)
- return -EINVAL;
-
- TwTs_Threshold[3] = input;
-
- return count;
-}
-
define_one_global_rw(startdelay);
define_one_global_rw(delay);
define_one_global_rw(pause);
define_one_global_rw(scroff_single_core);
define_one_global_rw(idle_freq);
+define_one_global_rw(min_cpus);
+define_one_global_rw(max_cpus);
define_one_global_rw(enabled);
-define_one_global_rw(nwns_threshold_up);
-define_one_global_rw(nwns_threshold_down);
-define_one_global_rw(twts_threshold_up);
-define_one_global_rw(twts_threshold_down);
static struct attribute *msm_mpdec_attributes[] = {
&startdelay.attr,
@@ -519,11 +615,25 @@ static struct attribute *msm_mpdec_attri
&pause.attr,
&scroff_single_core.attr,
&idle_freq.attr,
+ &min_cpus.attr,
+ &max_cpus.attr,
&enabled.attr,
- &nwns_threshold_up.attr,
- &nwns_threshold_down.attr,
- &twts_threshold_up.attr,
- &twts_threshold_down.attr,
+ &twts_threshold_0.attr,
+ &twts_threshold_1.attr,
+ &twts_threshold_2.attr,
+ &twts_threshold_3.attr,
+ &twts_threshold_4.attr,
+ &twts_threshold_5.attr,
+ &twts_threshold_6.attr,
+ &twts_threshold_7.attr,
+ &nwns_threshold_0.attr,
+ &nwns_threshold_1.attr,
+ &nwns_threshold_2.attr,
+ &nwns_threshold_3.attr,
+ &nwns_threshold_4.attr,
+ &nwns_threshold_5.attr,
+ &nwns_threshold_6.attr,
+ &nwns_threshold_7.attr,
NULL
};
@@ -534,7 +644,7 @@ static struct attribute_group msm_mpdec_
};
/**************************** SYSFS END ****************************/
-static int __init msm_mpdec(void)
+static int __init msm_mpdec_init(void)
{
int cpu, rc, err = 0;
@@ -544,9 +654,16 @@ static int __init msm_mpdec(void)
per_cpu(msm_mpdec_cpudata, cpu).online = true;
}
+ was_paused = true;
+
+ msm_mpdec_workq = alloc_workqueue(
+ "mpdec", WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
+ if (!msm_mpdec_workq)
+ return -ENOMEM;
INIT_DELAYED_WORK(&msm_mpdec_work, msm_mpdec_work_thread);
if (state != MSM_MPDEC_DISABLED)
- schedule_delayed_work(&msm_mpdec_work, 0);
+ queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work,
+ msecs_to_jiffies(msm_mpdec_tuners_ins.delay));
register_early_suspend(&msm_mpdec_early_suspend_handler);
@@ -564,9 +681,12 @@ static int __init msm_mpdec(void)
return err;
}
+late_initcall(msm_mpdec_init);
-late_initcall(msm_mpdec);
-
-MODULE_DESCRIPTION("Kernel based MPDECISION");
-
+void msm_mpdec_exit(void)
+{
+ destroy_workqueue(msm_mpdec_workq);
+ destroy_workqueue(msm_mpdec_workq);
+}
+MODULE_DESCRIPTION("Kernel based MPDECISION (C) 2011-12 Chad Goodman");