Commit e34c2f02 authored by Dennis Rassmann's avatar Dennis Rassmann
Browse files

Merge branch 'exp'

parents 824f15ea d467a6d5
......@@ -431,11 +431,13 @@ CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
# CONFIG_MSM_DALRPC is not set
CONFIG_MSM_MPDEC=y
CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN=y
CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y
CONFIG_MSM_CPU_FREQ_MAX=1512000
CONFIG_MSM_CPU_FREQ_MIN=384000
CONFIG_CPU_OVERCLOCK=y
CONFIG_CPU_VOLTAGE_TABLE=y
CONFIG_CMDLINE_OPTIONS=y
# CONFIG_MSM_AVS_HW is not set
# CONFIG_MSM_HW3D is not set
CONFIG_AMSS_7X25_VERSION_2009=y
......@@ -3122,6 +3124,7 @@ CONFIG_HAVE_CLK_PREPARE=y
#
CONFIG_IOMMU_SUPPORT=y
CONFIG_MSM_IOMMU=y
CONFIG_MSM_IOMMU_GPU_SYNC=y
CONFIG_IOMMU_PGTABLES_L2=y
#
......
......@@ -1640,6 +1640,13 @@ config MSM_MPDEC
This enables kernel based multi core control.
(up/down hotplug based on load)
config MSM_MPDEC_INPUTBOOST_CPUMIN
bool "Enable kernel based mpdecision"
depends on MSM_MPDEC
default n
help
This pushes the cpu min speed to predefined values on input events.
if CPU_FREQ_MSM
config MSM_CPU_FREQ_SET_MIN_MAX
......@@ -1669,6 +1676,12 @@ config CPU_VOLTAGE_TABLE
bool "Enable CPU Voltage Table via sysfs for adjustements"
default n
config CMDLINE_OPTIONS
bool "Enable cmdline s2w/khz/gov/maxscroff/scheduler command parsing"
default n
help
This enables the parsing of special cmdline variables.
config MSM_CPU_AVS
bool "Enable software controlled Adaptive Voltage Scaling (AVS)"
depends on (ARCH_MSM_SCORPION && QSD_SVS)
......
......@@ -25,6 +25,9 @@
#include <linux/earlysuspend.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <linux/cpu.h>
......@@ -32,31 +35,27 @@
#include <asm-generic/cputime.h>
#include <linux/hrtimer.h>
#include <linux/delay.h>
#include <linux/export.h>
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
#include <linux/input.h>
#include <linux/slab.h>
#endif
#include "acpuclock.h"
#define DEBUG 0
#define MPDEC_TAG "[MPDEC]: "
#define MSM_MPDEC_STARTDELAY 20000
#define MSM_MPDEC_DELAY 100
#define MSM_MPDEC_DELAY 130
#define MSM_MPDEC_PAUSE 10000
#define MSM_MPDEC_IDLE_FREQ 486000
struct global_attr {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj,
struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *a, struct attribute *b,
const char *c, size_t count);
};
#define define_one_global_ro(_name) \
static struct global_attr _name = \
__ATTR(_name, 0444, show_##_name, NULL)
#define define_one_global_rw(_name) \
static struct global_attr _name = \
__ATTR(_name, 0644, show_##_name, store_##_name)
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
#define MSM_MPDEC_BOOSTTIME 1000
#define MSM_MPDEC_BOOSTFREQ_CPU0 918000
#define MSM_MPDEC_BOOSTFREQ_CPU1 918000
#define MSM_MPDEC_BOOSTFREQ_CPU2 702000
#define MSM_MPDEC_BOOSTFREQ_CPU3 594000
#endif
enum {
MSM_MPDEC_DISABLED = 0,
......@@ -66,19 +65,34 @@ enum {
};
struct msm_mpdec_cpudata_t {
struct mutex suspend_mutex;
struct mutex hotplug_mutex;
int online;
int device_suspended;
cputime64_t on_time;
cputime64_t on_time_total;
long long unsigned int times_cpu_hotplugged;
long long unsigned int times_cpu_unplugged;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
struct mutex boost_mutex;
struct mutex unboost_mutex;
unsigned long int norm_min_freq;
unsigned long int boost_freq;
cputime64_t boost_until;
bool is_boosted;
bool revib_wq_running;
#endif
};
static DEFINE_PER_CPU(struct msm_mpdec_cpudata_t, msm_mpdec_cpudata);
static struct delayed_work msm_mpdec_work;
static struct workqueue_struct *msm_mpdec_workq;
static DEFINE_MUTEX(mpdec_msm_cpu_lock);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
static struct workqueue_struct *mpdec_input_wq;
static DEFINE_PER_CPU(struct work_struct, mpdec_input_work);
static struct workqueue_struct *msm_mpdec_revib_workq;
static DEFINE_PER_CPU(struct delayed_work, msm_mpdec_revib_work);
#endif
static struct msm_mpdec_tuners {
unsigned int startdelay;
......@@ -88,6 +102,11 @@ static struct msm_mpdec_tuners {
unsigned long int idle_freq;
unsigned int max_cpus;
unsigned int min_cpus;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
bool boost_enabled;
unsigned int boost_time;
unsigned long int boost_freq[4];
#endif
} msm_mpdec_tuners_ins = {
.startdelay = MSM_MPDEC_STARTDELAY,
.delay = MSM_MPDEC_DELAY,
......@@ -96,9 +115,19 @@ static struct msm_mpdec_tuners {
.idle_freq = MSM_MPDEC_IDLE_FREQ,
.max_cpus = CONFIG_NR_CPUS,
.min_cpus = 1,
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
.boost_enabled = true,
.boost_time = MSM_MPDEC_BOOSTTIME,
.boost_freq = {
MSM_MPDEC_BOOSTFREQ_CPU0,
MSM_MPDEC_BOOSTFREQ_CPU1,
MSM_MPDEC_BOOSTFREQ_CPU2,
MSM_MPDEC_BOOSTFREQ_CPU3
},
#endif
};
static unsigned int NwNs_Threshold[8] = {18, 0, 25, 15, 32, 28, 0, 35};
static unsigned int NwNs_Threshold[8] = {12, 0, 20, 7, 25, 10, 0, 18};
static unsigned int TwTs_Threshold[8] = {140, 0, 140, 190, 140, 190, 0, 190};
extern unsigned int get_rq_info(void);
......@@ -106,6 +135,12 @@ extern unsigned long acpuclk_get_rate(int);
unsigned int state = MSM_MPDEC_IDLE;
bool was_paused = false;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
bool is_screen_on = true;
static int update_cpu_min_freq(struct cpufreq_policy *cpu_policy,
int cpu, int new_freq);
static void unboost_cpu(int cpu);
#endif
static cputime64_t mpdec_paused_until = 0;
static unsigned long get_rate(int cpu) {
......@@ -151,6 +186,36 @@ static unsigned long get_slowest_cpu_rate(void) {
return slow_rate;
}
static void mpdec_cpu_up(int cpu) {
if (!cpu_online(cpu)) {
mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex);
cpu_up(cpu);
per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get());
per_cpu(msm_mpdec_cpudata, cpu).online = true;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged += 1;
pr_info(MPDEC_TAG"CPU[%d] off->on | Mask=[%d%d]\n",
cpu, cpu_online(0), cpu_online(1));
mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex);
}
}
EXPORT_SYMBOL_GPL(mpdec_cpu_up);
static void mpdec_cpu_down(int cpu) {
cputime64_t on_time = 0;
if (cpu_online(cpu)) {
mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex);
cpu_down(cpu);
on_time = (ktime_to_ms(ktime_get()) - per_cpu(msm_mpdec_cpudata, cpu).on_time);
per_cpu(msm_mpdec_cpudata, cpu).online = false;
per_cpu(msm_mpdec_cpudata, cpu).on_time_total += on_time;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged += 1;
pr_info(MPDEC_TAG"CPU[%d] on->off | Mask=[%d%d] | time online: %llu\n",
cpu, cpu_online(0), cpu_online(1), on_time);
mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex);
}
}
EXPORT_SYMBOL_GPL(mpdec_cpu_down);
static int mp_decision(void) {
static bool first_call = true;
int new_state = MSM_MPDEC_IDLE;
......@@ -217,7 +282,6 @@ static int mp_decision(void) {
static void msm_mpdec_work_thread(struct work_struct *work) {
unsigned int cpu = nr_cpu_ids;
cputime64_t on_time = 0;
bool suspended = false;
if (ktime_to_ms(ktime_get()) <= msm_mpdec_tuners_ins.startdelay)
......@@ -259,13 +323,10 @@ static void msm_mpdec_work_thread(struct work_struct *work) {
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;
per_cpu(msm_mpdec_cpudata, cpu).on_time_total += on_time;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged += 1;
pr_info(MPDEC_TAG"CPU[%d] on->off | Mask=[%d%d] | time online: %llu\n",
cpu, cpu_online(0), cpu_online(1), on_time);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
unboost_cpu(cpu);
#endif
mpdec_cpu_down(cpu);
} 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);
......@@ -278,12 +339,10 @@ static void msm_mpdec_work_thread(struct work_struct *work) {
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());
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged += 1;
pr_info(MPDEC_TAG"CPU[%d] off->on | Mask=[%d%d]\n",
cpu, cpu_online(0), cpu_online(1));
mpdec_cpu_up(cpu);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
unboost_cpu(cpu);
#endif
} 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);
......@@ -305,53 +364,243 @@ out:
return;
}
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
static int update_cpu_min_freq(struct cpufreq_policy *cpu_policy,
int cpu, int new_freq) {
int ret = 0;
if (!cpu_policy)
return -EINVAL;
cpufreq_verify_within_limits(cpu_policy, new_freq, cpu_policy->max);
cpu_policy->user_policy.min = new_freq;
ret = cpufreq_update_policy(cpu);
if (!ret) {
pr_debug(MPDEC_TAG"Touch event! Setting CPU%d min frequency to %d\n",
cpu, new_freq);
}
return ret;
}
static void unboost_cpu(int cpu) {
struct cpufreq_policy *cpu_policy = NULL;
if (cpu_online(cpu)) {
if (per_cpu(msm_mpdec_cpudata, cpu).is_boosted) {
if (mutex_trylock(&per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex)) {
cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) {
pr_debug(MPDEC_TAG"NULL policy on cpu %d\n", cpu);
return;
}
#if DEBUG
pr_info(MPDEC_TAG"un boosted cpu%i to %lu", cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq);
#endif
per_cpu(msm_mpdec_cpudata, cpu).is_boosted = false;
per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = false;
if ((cpu_policy->min != per_cpu(msm_mpdec_cpudata, cpu).boost_freq) &&
(cpu_policy->min != per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq)) {
pr_info(MPDEC_TAG"cpu%u min was changed while boosted (%lu->%u), using new min",
cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq, cpu_policy->min);
per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = cpu_policy->min;
}
update_cpu_min_freq(cpu_policy, cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq);
cpufreq_cpu_put(cpu_policy);
mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex);
}
}
}
return;
}
static void msm_mpdec_revib_work_thread(struct work_struct *work) {
int cpu = smp_processor_id();
if (ktime_to_ms(ktime_get()) > per_cpu(msm_mpdec_cpudata, cpu).boost_until) {
unboost_cpu(cpu);
} else {
queue_delayed_work_on(cpu,
msm_mpdec_revib_workq,
&per_cpu(msm_mpdec_revib_work, cpu),
msecs_to_jiffies((per_cpu(msm_mpdec_cpudata, cpu).boost_until - ktime_to_ms(ktime_get()))));
}
return;
}
static void mpdec_input_callback(struct work_struct *unused) {
struct cpufreq_policy *cpu_policy = NULL;
int cpu = smp_processor_id();
bool boosted = false;
if (!per_cpu(msm_mpdec_cpudata, cpu).is_boosted) {
if (mutex_trylock(&per_cpu(msm_mpdec_cpudata, cpu).boost_mutex)) {
cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) {
pr_debug(MPDEC_TAG"NULL policy on cpu %d\n", cpu);
return;
}
per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = cpu_policy->min;
update_cpu_min_freq(cpu_policy, cpu, per_cpu(msm_mpdec_cpudata, cpu).boost_freq);
#if DEBUG
pr_info(MPDEC_TAG"boosted cpu%i to %lu", cpu, per_cpu(msm_mpdec_cpudata, cpu).boost_freq);
#endif
per_cpu(msm_mpdec_cpudata, cpu).is_boosted = true;
per_cpu(msm_mpdec_cpudata, cpu).boost_until = ktime_to_ms(ktime_get()) + MSM_MPDEC_BOOSTTIME;
boosted = true;
cpufreq_cpu_put(cpu_policy);
mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).boost_mutex);
}
} else {
boosted = true;
}
if (boosted && !per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running) {
per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = true;
queue_delayed_work_on(cpu,
msm_mpdec_revib_workq,
&per_cpu(msm_mpdec_revib_work, cpu),
msecs_to_jiffies(MSM_MPDEC_BOOSTTIME));
} else if (boosted && per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running) {
per_cpu(msm_mpdec_cpudata, cpu).boost_until = ktime_to_ms(ktime_get()) + MSM_MPDEC_BOOSTTIME;
}
return;
}
static void mpdec_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value) {
int i = 0;
if (!msm_mpdec_tuners_ins.boost_enabled)
return;
if (!is_screen_on)
return;
for_each_online_cpu(i) {
queue_work_on(i, mpdec_input_wq, &per_cpu(mpdec_input_work, i));
}
}
static int input_dev_filter(const char *input_dev_name) {
if (strstr(input_dev_name, "touch") ||
strstr(input_dev_name, "keypad")) {
return 0;
} else {
return 1;
}
}
static int mpdec_input_connect(struct input_handler *handler,
struct input_dev *dev, const struct input_device_id *id) {
struct input_handle *handle;
int error;
if (input_dev_filter(dev->name))
return -ENODEV;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = "mpdec";
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 mpdec_input_disconnect(struct input_handle *handle) {
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
static const struct input_device_id mpdec_ids[] = {
{ .driver_info = 1 },
{ },
};
static struct input_handler mpdec_input_handler = {
.event = mpdec_input_event,
.connect = mpdec_input_connect,
.disconnect = mpdec_input_disconnect,
.name = "mpdec_inputreq",
.id_table = mpdec_ids,
};
#endif
static void msm_mpdec_early_suspend(struct early_suspend *h) {
int cpu = nr_cpu_ids;
cputime64_t on_time = 0;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
is_screen_on = false;
#endif
if (!msm_mpdec_tuners_ins.scroff_single_core) {
pr_info(MPDEC_TAG"Screen -> off\n");
return;
}
/* main work thread can sleep now */
cancel_delayed_work_sync(&msm_mpdec_work);
for_each_possible_cpu(cpu) {
mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
unboost_cpu(cpu);
#endif
if ((cpu >= 1) && (cpu_online(cpu))) {
cpu_down(cpu);
pr_info(MPDEC_TAG"Screen -> off. Suspended CPU[%d] | Mask=[%d%d]\n",
cpu, cpu_online(0), cpu_online(1));
per_cpu(msm_mpdec_cpudata, cpu).online = false;
on_time = ktime_to_ms(ktime_get()) - per_cpu(msm_mpdec_cpudata, cpu).on_time;
per_cpu(msm_mpdec_cpudata, cpu).on_time_total += on_time;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged += 1;
mpdec_cpu_down(cpu);
}
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 = nr_cpu_ids;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
is_screen_on = true;
#endif
for_each_possible_cpu(cpu)
per_cpu(msm_mpdec_cpudata, cpu).device_suspended = false;
mutex_lock(&per_cpu(msm_mpdec_cpudata, 1).suspend_mutex);
if (!cpu_online(1)) {
/* Always enable cpu1 when screen comes online.
* This boosts the wakeup process. */
cpu_up(1);
per_cpu(msm_mpdec_cpudata, 1).on_time = ktime_to_ms(ktime_get());
per_cpu(msm_mpdec_cpudata, 1).online = true;
per_cpu(msm_mpdec_cpudata, 1).times_cpu_hotplugged += 1;
pr_info(MPDEC_TAG"Screen -> on. Hot plugged CPU1 | Mask=[%d%d]\n",
if (msm_mpdec_tuners_ins.scroff_single_core) {
/* wake up main work thread */
was_paused = true;
queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, 0);
/* restore min/max cpus limits */
for (cpu=1; cpu<CONFIG_NR_CPUS; cpu++) {
if (cpu < msm_mpdec_tuners_ins.min_cpus) {
if (!cpu_online(cpu))
mpdec_cpu_up(cpu);
} else if (cpu > msm_mpdec_tuners_ins.max_cpus) {
if (cpu_online(cpu))
mpdec_cpu_down(cpu);
}
}
pr_info(MPDEC_TAG"Screen -> on. Activated mpdecision. | Mask=[%d%d]\n",
cpu_online(0), cpu_online(1));
} else {
pr_info(MPDEC_TAG"Screen -> on\n");
}
mutex_unlock(&per_cpu(msm_mpdec_cpudata, 1).suspend_mutex);
/* wake up main work thread */
was_paused = true;
queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, 0);
pr_info(MPDEC_TAG"Screen -> on. Activated mpdecision. | Mask=[%d%d]\n",
cpu_online(0), cpu_online(1));
}
static struct early_suspend msm_mpdec_early_suspend_handler = {
......@@ -376,6 +625,10 @@ show_one(pause, pause);
show_one(scroff_single_core, scroff_single_core);
show_one(min_cpus, min_cpus);
show_one(max_cpus, max_cpus);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
show_one(boost_enabled, boost_enabled);
show_one(boost_time, boost_time);
#endif
#define show_one_twts(file_name, arraypos) \
static ssize_t show_##file_name \
......@@ -541,14 +794,12 @@ static ssize_t store_scroff_single_core(struct kobject *a, struct attribute *b,
if (ret != 1)
return -EINVAL;
switch (buf[0]) {
case '0':
msm_mpdec_tuners_ins.scroff_single_core = input;
break;
case '1':
msm_mpdec_tuners_ins.scroff_single_core = input;
break;
default:
ret = -EINVAL;
case '0':
case '1':
msm_mpdec_tuners_ins.scroff_single_core = input;
break;
default:
ret = -EINVAL;
}
return count;
}
......@@ -557,12 +808,22 @@ static ssize_t store_max_cpus(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
unsigned int input;
int ret;
int ret, cpu;
ret = sscanf(buf, "%u", &input);
if ((ret != 1) || input > CONFIG_NR_CPUS)
if ((ret != 1) || input > CONFIG_NR_CPUS || input < msm_mpdec_tuners_ins.min_cpus)
return -EINVAL;
msm_mpdec_tuners_ins.max_cpus = input;
if (num_online_cpus() > input) {
for (cpu=CONFIG_NR_CPUS; cpu>0; cpu--) {
if (num_online_cpus() <= input)
break;
if (!cpu_online(cpu))
continue;
mpdec_cpu_down(cpu);
}
pr_info(MPDEC_TAG"max_cpus set to %u. Affected CPUs were unplugged!\n", input);
}
return count;
}
......@@ -571,12 +832,22 @@ static ssize_t store_min_cpus(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
unsigned int input;
int ret;
int ret, cpu;
ret = sscanf(buf, "%u", &input);
if ((ret != 1) || input < 1)
if ((ret != 1) || input < 1 || input > msm_mpdec_tuners_ins.max_cpus)
return -EINVAL;
msm_mpdec_tuners_ins.min_cpus = input;
if (num_online_cpus() < input) {
for (cpu=1; cpu<CONFIG_NR_CPUS; cpu++) {
if (num_online_cpus() >= input)
break;
if (cpu_online(cpu))
continue;
mpdec_cpu_up(cpu);
}
pr_info(MPDEC_TAG"min_cpus set to %u. Affected CPUs were hotplugged!\n", input);
}
return count;
}
......@@ -610,17 +881,9 @@ static ssize_t store_enabled(struct kobject *a, struct attribute *b,
case '0':
state = MSM_MPDEC_DISABLED;
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;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged += 1;
cpu_up(cpu);
pr_info(MPDEC_TAG"nap time... Hot plugged CPU[%d] | Mask=[%d%d]\n",
cpu, cpu_online(0), cpu_online(1));
}
}
for (cpu = 1; cpu < CONFIG_NR_CPUS; cpu++)
if (!cpu_online(cpu))
mpdec_cpu_up(cpu);
break;
case '1':
state = MSM_MPDEC_IDLE;
......@@ -635,6 +898,67 @@ static ssize_t store_enabled(struct kobject *a, struct attribute *b,
return count;
}
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
static ssize_t store_boost_enabled(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;
msm_mpdec_tuners_ins.boost_enabled = input;
return count;
}
static ssize_t store_boost_time(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;
msm_mpdec_tuners_ins.boost_time = input;
return count;
}
static ssize_t show_boost_freqs(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
int cpu = 0;
for_each_present_cpu(cpu) {
len += sprintf(buf + len, "%lu\n", per_cpu(msm_mpdec_cpudata, cpu).boost_freq);
}
return len;
}
static ssize_t store_boost_freqs(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
int i = 0;
unsigned int cpu = 0;
long unsigned int hz = 0;
const char *chz = NULL;
for (i=0; i<count; i++) {
if (buf[i] == ' ') {
sscanf(&buf[(i-1)], "%u", &cpu);
chz = &buf[(i+1)];
}
}
sscanf(chz, "%lu", &hz);
per_cpu(msm_mpdec_cpudata, cpu).boost_freq = hz;
return count;
}
define_one_global_rw(boost_freqs);
#endif
define_one_global_rw(startdelay);
define_one_global_rw(delay);
define_one_global_rw(pause);
......@@ -643,6 +967,10 @@ define_one_global_rw(idle_freq);
define_one_global_rw(min_cpus);
define_one_global_rw(max_cpus);
define_one_global_rw(enabled);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
define_one_global_rw(boost_enabled);
define_one_global_rw(boost_time);
#endif
static struct attribute *msm_mpdec_attributes[] = {
&startdelay.attr,
......@@ -669,6 +997,11 @@ static struct attribute *msm_mpdec_attributes[] = {
&nwns_threshold_5.attr,
&nwns_threshold_6.attr,
&nwns_threshold_7.attr,
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
&boost_freqs.attr,
&boost_enabled.attr,
&boost_time.attr,
#endif
NULL
};
......@@ -744,14 +1077,37 @@ static struct attribute_group msm_mpdec_stats_attr_group = {
static int __init msm_mpdec_init(void) {
int cpu, rc, err = 0;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
int i;
unsigned long int boost_freq = 0;
#endif
for_each_possible_cpu(cpu) {
mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).suspend_mutex));
mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex));
per_cpu(msm_mpdec_cpudata, cpu).device_suspended = false;
per_cpu(msm_mpdec_cpudata, cpu).online = true;
per_cpu(msm_mpdec_cpudata, cpu).on_time_total = 0;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged = 0;
per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged = 0;
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = CONFIG_MSM_CPU_FREQ_MIN;
switch (cpu) {
case 0:
case 1:
case 2:
boost_freq = msm_mpdec_tuners_ins.boost_freq[cpu];
break;
default:
boost_freq = msm_mpdec_tuners_ins.boost_freq[3];
break;
}
per_cpu(msm_mpdec_cpudata, cpu).boost_freq = boost_freq;
per_cpu(msm_mpdec_cpudata, cpu).is_boosted = false;
per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = false;
per_cpu(msm_mpdec_cpudata, cpu).boost_until = 0;
mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).boost_mutex));
mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex));
#endif
}
was_paused = true;
......@@ -762,6 +1118,25 @@ static int __init msm_mpdec_init(void) {
if (!msm_mpdec_workq)
return -ENOMEM;
INIT_DELAYED_WORK(&msm_mpdec_work, msm_mpdec_work_thread);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
mpdec_input_wq = create_workqueue("mpdeciwq");
if (!mpdec_input_wq) {
printk(KERN_ERR "%s: Failed to create mpdeciwq workqueue\n", __func__);
return -EFAULT;
}
msm_mpdec_revib_workq = create_workqueue("mpdecribwq");
if (!msm_mpdec_revib_workq) {
printk(KERN_ERR "%s: Failed to create mpdecrevibwq workqueue\n", __func__);
return -EFAULT;
}
for_each_possible_cpu(i) {
INIT_WORK(&per_cpu(mpdec_input_work, i), mpdec_input_callback);
INIT_DELAYED_WORK(&per_cpu(msm_mpdec_revib_work, i), msm_mpdec_revib_work_thread);
}
rc = input_register_handler(&mpdec_input_handler);
#endif
if (state != MSM_MPDEC_DISABLED)
queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work,
msecs_to_jiffies(msm_mpdec_tuners_ins.delay));
......@@ -790,6 +1165,10 @@ static int __init msm_mpdec_init(void) {
late_initcall(msm_mpdec_init);
void msm_mpdec_exit(void) {
destroy_workqueue(msm_mpdec_workq);
#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN
input_unregister_handler(&mpdec_input_handler);
destroy_workqueue(msm_mpdec_revib_workq);
destroy_workqueue(mpdec_input_wq);
#endif
destroy_workqueue(msm_mpdec_workq);
}
......@@ -24,6 +24,7 @@
#include <linux/msm_thermal.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/hrtimer.h>
#include <mach/cpufreq.h>
static DEFINE_MUTEX(emergency_shutdown_mutex);
......@@ -38,204 +39,244 @@ static int pre_throttled_max = 0;
static struct msm_thermal_data msm_thermal_info;
static struct msm_thermal_stat msm_thermal_stats = {
.time_low_start = 0,
.time_mid_start = 0,
.time_max_start = 0,
.time_low = 0,
.time_mid = 0,
.time_max = 0,
};
static struct delayed_work check_temp_work;
static struct workqueue_struct *check_temp_workq;
static void update_stats(void)
{
if (msm_thermal_stats.time_low_start > 0) {
msm_thermal_stats.time_low += (ktime_to_ms(ktime_get()) - msm_thermal_stats.time_low_start);
msm_thermal_stats.time_low_start = 0;
}
if (msm_thermal_stats.time_mid_start > 0) {
msm_thermal_stats.time_mid += (ktime_to_ms(ktime_get()) - msm_thermal_stats.time_mid_start);
msm_thermal_stats.time_mid_start = 0;
}
if (msm_thermal_stats.time_max_start > 0) {
msm_thermal_stats.time_max += (ktime_to_ms(ktime_get()) - msm_thermal_stats.time_max_start);
msm_thermal_stats.time_max_start = 0;
}
}
static void start_stats(int status)
{
switch (thermal_throttled) {
case 1:
msm_thermal_stats.time_low_start = ktime_to_ms(ktime_get());
break;
case 2:
msm_thermal_stats.time_mid_start = ktime_to_ms(ktime_get());
break;
case 3:
msm_thermal_stats.time_max_start = ktime_to_ms(ktime_get());
break;
}
}
static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
int cpu, int max_freq)
int cpu, int max_freq)
{
int ret = 0;
int ret = 0;
if (!cpu_policy)
return -EINVAL;
if (!cpu_policy)
return -EINVAL;
cpufreq_verify_within_limits(cpu_policy,
cpu_policy->min, max_freq);
cpu_policy->user_policy.max = max_freq;
cpufreq_verify_within_limits(cpu_policy, cpu_policy->min, max_freq);
cpu_policy->user_policy.max = max_freq;
ret = cpufreq_update_policy(cpu);
if (!ret)
pr_debug("msm_thermal: Setting CPU%d max frequency to %d\n",
cpu, max_freq);
return ret;
ret = cpufreq_update_policy(cpu);
if (!ret)
pr_debug("msm_thermal: Setting CPU%d max frequency to %d\n",
cpu, max_freq);
return ret;
}
static void check_temp(struct work_struct *work)
{
struct cpufreq_policy *cpu_policy = NULL;
struct tsens_device tsens_dev;
unsigned long temp = 0;
uint32_t max_freq = 0;
bool update_policy = false;
int i = 0, cpu = 0, ret = 0;
tsens_dev.sensor_num = msm_thermal_info.sensor_id;
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
pr_err("msm_thermal: FATAL: Unable to read TSENS sensor %d\n",
tsens_dev.sensor_num);
goto reschedule;
}
if (temp >= msm_thermal_info.shutdown_temp) {
mutex_lock(&emergency_shutdown_mutex);
pr_warn("################################\n");
pr_warn("################################\n");
pr_warn("- %u OVERTEMP! SHUTTING DOWN! -\n", msm_thermal_info.shutdown_temp);
pr_warn("- cur temp:%lu measured by:%u -\n", temp, msm_thermal_info.sensor_id);
pr_warn("################################\n");
pr_warn("################################\n");
/* orderly poweroff tries to power down gracefully
if it fails it will force it. */
orderly_poweroff(true);
for_each_possible_cpu(cpu) {
update_policy = true;
max_freq = msm_thermal_info.allowed_max_freq;
thermal_throttled = 3;
pr_warn("msm_thermal: Emergency throttled CPU%i to %u! temp:%lu\n",
cpu, msm_thermal_info.allowed_max_freq, temp);
}
mutex_unlock(&emergency_shutdown_mutex);
struct cpufreq_policy *cpu_policy = NULL;
struct tsens_device tsens_dev;
unsigned long temp = 0;
uint32_t max_freq = 0;
bool update_policy = false;
int i = 0, cpu = 0, ret = 0;
tsens_dev.sensor_num = msm_thermal_info.sensor_id;
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
pr_err("msm_thermal: FATAL: Unable to read TSENS sensor %d\n",
tsens_dev.sensor_num);
goto reschedule;
}
if (temp >= msm_thermal_info.shutdown_temp) {
mutex_lock(&emergency_shutdown_mutex);
pr_warn("################################\n");
pr_warn("################################\n");
pr_warn("- %u OVERTEMP! SHUTTING DOWN! -\n", msm_thermal_info.shutdown_temp);
pr_warn("- cur temp:%lu measured by:%u -\n", temp, msm_thermal_info.sensor_id);
pr_warn("################################\n");
pr_warn("################################\n");
/* orderly poweroff tries to power down gracefully
if it fails it will force it. */
orderly_poweroff(true);
for_each_possible_cpu(cpu) {
update_policy = true;
max_freq = msm_thermal_info.allowed_max_freq;
thermal_throttled = 3;
pr_warn("msm_thermal: Emergency throttled CPU%i to %u! temp:%lu\n",
cpu, msm_thermal_info.allowed_max_freq, temp);
}
mutex_unlock(&emergency_shutdown_mutex);
}
for_each_possible_cpu(cpu) {
update_policy = false;
cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) {
pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
continue;
}
for_each_possible_cpu(cpu) {
update_policy = false;
cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) {
pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
continue;
}
/* save pre-throttled max freq value */
if ((thermal_throttled == 0) && (cpu == 0))
pre_throttled_max = cpu_policy->max;
//low trip point
if ((temp >= msm_thermal_info.allowed_low_high) &&
(temp < msm_thermal_info.allowed_mid_high) &&
(thermal_throttled < 1)) {
update_policy = true;
max_freq = msm_thermal_info.allowed_low_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 1;
pr_warn("msm_thermal: Thermal Throttled (low)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//low clr point
} else if ((temp < msm_thermal_info.allowed_low_low) &&
(thermal_throttled > 0)) {
if (pre_throttled_max != 0)
max_freq = pre_throttled_max;
else {
max_freq = CONFIG_MSM_CPU_FREQ_MAX;
pr_warn("msm_thermal: ERROR! pre_throttled_max=0, falling back to %u\n", max_freq);
}
update_policy = true;
for (i = 1; i < CONFIG_NR_CPUS; i++) {
if (cpu_online(i))
continue;
cpu_up(i);
}
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 0;
pr_warn("msm_thermal: Low thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//mid trip point
} else if ((temp >= msm_thermal_info.allowed_mid_high) &&
(temp < msm_thermal_info.allowed_max_high) &&
(thermal_throttled < 2)) {
update_policy = true;
max_freq = msm_thermal_info.allowed_mid_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 2;
pr_warn("msm_thermal: Thermal Throttled (mid)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//mid clr point
} else if ((temp < msm_thermal_info.allowed_mid_low) &&
(thermal_throttled > 1)) {
max_freq = msm_thermal_info.allowed_low_freq;
update_policy = true;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 1;
pr_warn("msm_thermal: Mid thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//max trip point
} else if (temp >= msm_thermal_info.allowed_max_high) {
update_policy = true;
max_freq = msm_thermal_info.allowed_max_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 3;
pr_warn("msm_thermal: Thermal Throttled (max)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//max clr point
} else if ((temp < msm_thermal_info.allowed_max_low) &&
(thermal_throttled > 2)) {
max_freq = msm_thermal_info.allowed_mid_freq;
update_policy = true;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 2;
pr_warn("msm_thermal: Max thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
}
if (update_policy)
update_cpu_max_freq(cpu_policy, cpu, max_freq);
cpufreq_cpu_put(cpu_policy);
}
/* save pre-throttled max freq value */
if ((thermal_throttled == 0) && (cpu == 0))
pre_throttled_max = cpu_policy->max;
//low trip point
if ((temp >= msm_thermal_info.allowed_low_high) &&
(temp < msm_thermal_info.allowed_mid_high) &&
(thermal_throttled < 1)) {
update_policy = true;
max_freq = msm_thermal_info.allowed_low_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 1;
pr_warn("msm_thermal: Thermal Throttled (low)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//low clr point
} else if ((temp < msm_thermal_info.allowed_low_low) &&
(thermal_throttled > 0)) {
if (pre_throttled_max != 0)
max_freq = pre_throttled_max;
else {
max_freq = CONFIG_MSM_CPU_FREQ_MAX;
pr_warn("msm_thermal: ERROR! pre_throttled_max=0, falling back to %u\n", max_freq);
}
update_policy = true;
for (i = 1; i < CONFIG_NR_CPUS; i++) {
if (cpu_online(i))
continue;
cpu_up(i);
}
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 0;
pr_warn("msm_thermal: Low thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//mid trip point
} else if ((temp >= msm_thermal_info.allowed_mid_high) &&
(temp < msm_thermal_info.allowed_max_high) &&
(thermal_throttled < 2)) {
update_policy = true;
max_freq = msm_thermal_info.allowed_mid_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 2;
pr_warn("msm_thermal: Thermal Throttled (mid)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//mid clr point
} else if ((temp < msm_thermal_info.allowed_mid_low) &&
(thermal_throttled > 1)) {
max_freq = msm_thermal_info.allowed_low_freq;
update_policy = true;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 1;
pr_warn("msm_thermal: Mid thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//max trip point
} else if (temp >= msm_thermal_info.allowed_max_high) {
update_policy = true;
max_freq = msm_thermal_info.allowed_max_freq;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 3;
pr_warn("msm_thermal: Thermal Throttled (max)! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
//max clr point
} else if ((temp < msm_thermal_info.allowed_max_low) &&
(thermal_throttled > 2)) {
max_freq = msm_thermal_info.allowed_mid_freq;
update_policy = true;
if (cpu == (CONFIG_NR_CPUS-1)) {
thermal_throttled = 2;
pr_warn("msm_thermal: Max thermal throttle ended! temp:%lu by:%u\n",
temp, msm_thermal_info.sensor_id);
}
}
update_stats();
start_stats(thermal_throttled);
if (update_policy)
update_cpu_max_freq(cpu_policy, cpu, max_freq);
cpufreq_cpu_put(cpu_policy);
}
reschedule:
if (enabled)
queue_delayed_work(check_temp_workq, &check_temp_work,
msecs_to_jiffies(msm_thermal_info.poll_ms));
return;
if (enabled)
queue_delayed_work(check_temp_workq, &check_temp_work,
msecs_to_jiffies(msm_thermal_info.poll_ms));
return;
}
static void disable_msm_thermal(void)
{
int cpu = 0;
struct cpufreq_policy *cpu_policy = NULL;
/* make sure check_temp is no longer running */
cancel_delayed_work(&check_temp_work);
flush_scheduled_work();
if (pre_throttled_max != 0) {
for_each_possible_cpu(cpu) {
cpu_policy = cpufreq_cpu_get(cpu);
if (cpu_policy) {
if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
update_cpu_max_freq(cpu_policy, cpu,
pre_throttled_max);
cpufreq_cpu_put(cpu_policy);
}
}
int cpu = 0;
struct cpufreq_policy *cpu_policy = NULL;
/* make sure check_temp is no longer running */
cancel_delayed_work(&check_temp_work);
flush_scheduled_work();
if (pre_throttled_max != 0) {
for_each_possible_cpu(cpu) {
cpu_policy = cpufreq_cpu_get(cpu);
if (cpu_policy) {
if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
update_cpu_max_freq(cpu_policy, cpu, pre_throttled_max);
cpufreq_cpu_put(cpu_policy);
}
}
}
}
static int set_enabled(const char *val, const struct kernel_param *kp)
{
int ret = 0;
int ret = 0;
ret = param_set_bool(val, kp);
if (!enabled)
disable_msm_thermal();
else
pr_info("msm_thermal: no action for enabled = %d\n", enabled);
ret = param_set_bool(val, kp);
if (!enabled)
disable_msm_thermal();
else
pr_info("msm_thermal: no action for enabled = %d\n", enabled);
pr_info("msm_thermal: enabled = %d\n", enabled);
pr_info("msm_thermal: enabled = %d\n", enabled);
return ret;
return ret;
}
static struct kernel_param_ops module_ops = {
.set = set_enabled,
.get = param_get_bool,
.set = set_enabled,
.get = param_get_bool,
};
module_param_cb(enabled, &module_ops, &enabled, 0644);
......@@ -244,11 +285,11 @@ MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
/**************************** SYSFS START ****************************/
struct kobject *msm_thermal_kobject;
#define show_one(file_name, object) \
static ssize_t show_##file_name \
(struct kobject *kobj, struct attribute *attr, char *buf) \
{ \
return sprintf(buf, "%u\n", msm_thermal_info.object); \
#define show_one(file_name, object) \
static ssize_t show_##file_name \
(struct kobject *kobj, struct attribute *attr, char *buf) \
{ \
return sprintf(buf, "%u\n", msm_thermal_info.object); \
}
show_one(shutdown_temp, shutdown_temp);
......@@ -264,157 +305,157 @@ show_one(allowed_low_freq, allowed_low_freq);
show_one(poll_ms, poll_ms);
static ssize_t store_shutdown_temp(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.shutdown_temp = input;
msm_thermal_info.shutdown_temp = input;
return count;
return count;
}
static ssize_t store_allowed_max_high(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_max_high = input;
msm_thermal_info.allowed_max_high = input;
return count;
return count;
}
static ssize_t store_allowed_max_low(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_max_low = input;
msm_thermal_info.allowed_max_low = input;
return count;
return count;
}
static ssize_t store_allowed_max_freq(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_max_freq = input;
msm_thermal_info.allowed_max_freq = input;
return count;
return count;
}
static ssize_t store_allowed_mid_high(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_mid_high = input;
msm_thermal_info.allowed_mid_high = input;
return count;
return count;
}
static ssize_t store_allowed_mid_low(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_mid_low = input;
msm_thermal_info.allowed_mid_low = input;
return count;
return count;
}
static ssize_t store_allowed_mid_freq(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_mid_freq = input;
msm_thermal_info.allowed_mid_freq = input;
return count;
return count;
}
static ssize_t store_allowed_low_high(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_low_high = input;
msm_thermal_info.allowed_low_high = input;
return count;
return count;
}
static ssize_t store_allowed_low_low(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_low_low = input;
msm_thermal_info.allowed_low_low = input;
return count;
return count;
}
static ssize_t store_allowed_low_freq(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.allowed_low_freq = input;
msm_thermal_info.allowed_low_freq = input;
return count;
return count;
}
static ssize_t store_poll_ms(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
const char *buf, size_t count)
{
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
msm_thermal_info.poll_ms = input;
msm_thermal_info.poll_ms = input;
return count;
return count;
}
define_one_global_rw(shutdown_temp);
......@@ -430,52 +471,201 @@ define_one_global_rw(allowed_low_freq);
define_one_global_rw(poll_ms);
static struct attribute *msm_thermal_attributes[] = {
&shutdown_temp.attr,
&allowed_max_high.attr,
&allowed_max_low.attr,
&allowed_max_freq.attr,
&allowed_mid_high.attr,
&allowed_mid_low.attr,
&allowed_mid_freq.attr,
&allowed_low_high.attr,
&allowed_low_low.attr,
&allowed_low_freq.attr,
&poll_ms.attr,
NULL
&shutdown_temp.attr,
&allowed_max_high.attr,
&allowed_max_low.attr,
&allowed_max_freq.attr,
&allowed_mid_high.attr,
&allowed_mid_low.attr,
&allowed_mid_freq.attr,
&allowed_low_high.attr,
&allowed_low_low.attr,
&allowed_low_freq.attr,
&poll_ms.attr,
NULL
};
static struct attribute_group msm_thermal_attr_group = {
.attrs = msm_thermal_attributes,
.name = "conf",
.attrs = msm_thermal_attributes,
.name = "conf",
};
/********* STATS START *********/
static ssize_t show_throttle_times(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
if (thermal_throttled == 1) {
len += sprintf(buf + len, "%s %llu\n", "low",
(msm_thermal_stats.time_low +
(ktime_to_ms(ktime_get()) -
msm_thermal_stats.time_low_start)));
} else
len += sprintf(buf + len, "%s %llu\n", "low", msm_thermal_stats.time_low);
if (thermal_throttled == 2) {
len += sprintf(buf + len, "%s %llu\n", "mid",
(msm_thermal_stats.time_mid +
(ktime_to_ms(ktime_get()) -
msm_thermal_stats.time_mid_start)));
} else
len += sprintf(buf + len, "%s %llu\n", "mid", msm_thermal_stats.time_mid);
if (thermal_throttled == 3) {
len += sprintf(buf + len, "%s %llu\n", "max",
(msm_thermal_stats.time_max +
(ktime_to_ms(ktime_get()) -
msm_thermal_stats.time_max_start)));
} else
len += sprintf(buf + len, "%s %llu\n", "max", msm_thermal_stats.time_max);
return len;
}
define_one_global_ro(throttle_times);
static ssize_t show_is_throttled(struct kobject *a, struct attribute *b,
char *buf)
{
return sprintf(buf, "%u\n", thermal_throttled);
}
define_one_global_ro(is_throttled);
static struct attribute *msm_thermal_stats_attributes[] = {
&is_throttled.attr,
&throttle_times.attr,
NULL
};
static struct attribute_group msm_thermal_stats_attr_group = {
.attrs = msm_thermal_stats_attributes,
.name = "stats",
};
/**************************** SYSFS END ****************************/
int __devinit msm_thermal_init(struct msm_thermal_data *pdata)
{
int ret = 0, rc = 0;
BUG_ON(!pdata);
BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
enabled = 1;
check_temp_workq = alloc_workqueue(
"msm_thermal", WQ_UNBOUND | WQ_RESCUER, 1);
if (!check_temp_workq)
BUG_ON(ENOMEM);
INIT_DELAYED_WORK(&check_temp_work, check_temp);
queue_delayed_work(check_temp_workq, &check_temp_work, 0);
msm_thermal_kobject = kobject_create_and_add("msm_thermal", kernel_kobj);
if (msm_thermal_kobject) {
rc = sysfs_create_group(msm_thermal_kobject,
&msm_thermal_attr_group);
if (rc) {
pr_warn("msm_thermal: sysfs: ERROR, could not create sysfs group");
}
} else
pr_warn("msm_thermal: sysfs: ERROR, could not create sysfs kobj");
return ret;
int ret = 0, rc = 0;
BUG_ON(!pdata);
BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
enabled = 1;
check_temp_workq=alloc_workqueue("msm_thermal", WQ_UNBOUND | WQ_RESCUER, 1);
if (!check_temp_workq)
BUG_ON(ENOMEM);
INIT_DELAYED_WORK(&check_temp_work, check_temp);
queue_delayed_work(check_temp_workq, &check_temp_work, 0);
msm_thermal_kobject = kobject_create_and_add("msm_thermal", kernel_kobj);
if (msm_thermal_kobject) {
rc = sysfs_create_group(msm_thermal_kobject, &msm_thermal_attr_group);
if (rc) {
pr_warn("msm_thermal: sysfs: ERROR, could not create sysfs group");
}
rc = sysfs_create_group(msm_thermal_kobject,
&msm_thermal_stats_attr_group);
if (rc) {
pr_warn("msm_thermal: sysfs: ERROR, could not create sysfs stats group");
}
} else
pr_warn("msm_thermal: sysfs: ERROR, could not create sysfs kobj");
return ret;
}
static int __devinit msm_thermal_dev_probe(struct platform_device *pdev)
{
int ret = 0;
char *key = NULL;
struct device_node *node = pdev->dev.of_node;
struct msm_thermal_data data;
memset(&data, 0, sizeof(struct msm_thermal_data));
key = "qcom,sensor-id";
ret = of_property_read_u32(node, key, &data.sensor_id);
if (ret)
goto fail;
WARN_ON(data.sensor_id >= TSENS_MAX_SENSORS);
key = "qcom,poll-ms";
ret = of_property_read_u32(node, key, &data.poll_ms);
if (ret)
goto fail;
key = "qcom,shutdown_temp";
ret = of_property_read_u32(node, key, &data.shutdown_temp);
if (ret)
goto fail;
key = "qcom,allowed_max_high";
ret = of_property_read_u32(node, key, &data.allowed_max_high);
if (ret)
goto fail;
key = "qcom,allowed_max_low";
ret = of_property_read_u32(node, key, &data.allowed_max_low);
if (ret)
goto fail;
key = "qcom,allowed_max_freq";
ret = of_property_read_u32(node, key, &data.allowed_max_freq);
if (ret)
goto fail;
key = "qcom,allowed_mid_high";
ret = of_property_read_u32(node, key, &data.allowed_mid_high);
if (ret)
goto fail;
key = "qcom,allowed_mid_low";
ret = of_property_read_u32(node, key, &data.allowed_mid_low);
if (ret)
goto fail;
key = "qcom,allowed_mid_freq";
ret = of_property_read_u32(node, key, &data.allowed_mid_freq);
if (ret)
goto fail;
key = "qcom,allowed_low_high";
ret = of_property_read_u32(node, key, &data.allowed_low_high);
if (ret)
goto fail;
key = "qcom,allowed_low_low";
ret = of_property_read_u32(node, key, &data.allowed_low_low);
if (ret)
goto fail;
key = "qcom,allowed_low_freq";
ret = of_property_read_u32(node, key, &data.allowed_low_freq);
if (ret)
goto fail;
fail:
if (ret)
pr_err("%s: Failed reading node=%s, key=%s\n",
__func__, node->full_name, key);
else
ret = msm_thermal_init(&data);
return ret;
}
static struct of_device_id msm_thermal_match_table[] = {
{.compatible = "qcom,msm-thermal"},
{},
};
static struct platform_driver msm_thermal_device_driver = {
.probe = msm_thermal_dev_probe,
.driver = {
.name = "msm-thermal",
.owner = THIS_MODULE,
.of_match_table = msm_thermal_match_table,
},
};
int __init msm_thermal_device_init(void)
{
return platform_driver_register(&msm_thermal_device_driver);
}
......@@ -14,6 +14,8 @@
#ifndef __MSM_THERMAL_H
#define __MSM_THERMAL_H
#include <asm/cputime.h>
struct msm_thermal_data {
uint32_t sensor_id;
uint32_t poll_ms;
......@@ -32,6 +34,15 @@ struct msm_thermal_data {
uint32_t allowed_low_freq;
};
struct msm_thermal_stat {
cputime64_t time_low_start;
cputime64_t time_mid_start;
cputime64_t time_max_start;
cputime64_t time_low;
cputime64_t time_mid;
cputime64_t time_max;
};
#ifdef CONFIG_THERMAL_MONITOR
extern int msm_thermal_init(struct msm_thermal_data *pdata);
extern int msm_thermal_device_init(void);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment