--- de69ebac30d545c60ed7bc2d5ad0257785739c53 +++ f39b330b391ab1390e78c038ef65bf3f70644945 @@ -19,23 +19,74 @@ #include #include #include +#include #define DEF_TEMP_SENSOR 0 -#define DEF_THERMAL_CHECK_MS 1000 -#define DEF_ALLOWED_MAX_HIGH 60 -#define DEF_ALLOWED_MAX_FREQ 918000 + +//shutdown temp +#define DEF_SHUTDOWNTEMP 80 + +//max thermal limit +#define DEF_ALLOWED_MAX_HIGH 76 +#define DEF_ALLOWED_MAX_FREQ 384000 + +//mid thermal limit +#define DEF_ALLOWED_MID_HIGH 72 +#define DEF_ALLOWED_MID_FREQ 1026000 + +//low thermal limit +#define DEF_ALLOWED_LOW_HIGH 70 +#define DEF_ALLOWED_LOW_FREQ 1350000 + +//Sampling interval +#define DEF_THERMAL_CHECK_MS 250 + +static DEFINE_MUTEX(emergency_shutdown_mutex); static int enabled; -static int allowed_max_high = DEF_ALLOWED_MAX_HIGH; -static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10); -static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ; -static int check_interval_ms = DEF_THERMAL_CHECK_MS; - -module_param(allowed_max_high, int, 0); -module_param(allowed_max_freq, int, 0); -module_param(check_interval_ms, int, 0); + +//Throttling indicator, 0=not throttled, 1=low, 2=mid, 3=max +static int thermal_throttled = 0; + +//Safe the cpu max freq before throttling +static int pre_throttled_max = 0; static struct delayed_work check_temp_work; +static struct workqueue_struct *check_temp_workq; + +static struct msm_thermal_tuners { + unsigned int shutdown_temp; + + unsigned int allowed_max_high; + unsigned int allowed_max_low; + unsigned int allowed_max_freq; + + unsigned int allowed_mid_high; + unsigned int allowed_mid_low; + unsigned int allowed_mid_freq; + + unsigned int allowed_low_high; + unsigned int allowed_low_low; + unsigned int allowed_low_freq; + + unsigned int check_interval_ms; +} msm_thermal_tuners_ins = { + .shutdown_temp = DEF_SHUTDOWNTEMP, + + .allowed_max_high = DEF_ALLOWED_MAX_HIGH, + .allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 5), + .allowed_max_freq = DEF_ALLOWED_MAX_FREQ, + + .allowed_mid_high = DEF_ALLOWED_MID_HIGH, + .allowed_mid_low = (DEF_ALLOWED_MID_HIGH - 5), + .allowed_mid_freq = DEF_ALLOWED_MID_FREQ, + + .allowed_low_high = DEF_ALLOWED_LOW_HIGH, + .allowed_low_low = (DEF_ALLOWED_LOW_HIGH - 6), + .allowed_low_freq = DEF_ALLOWED_LOW_FREQ, + + .check_interval_ms = DEF_THERMAL_CHECK_MS, +}; static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy, int cpu, int max_freq) @@ -47,7 +98,6 @@ static int update_cpu_max_freq(struct cp cpufreq_verify_within_limits(cpu_policy, cpu_policy->min, max_freq); - if (max_freq > 1512000) max_freq = 1512000; cpu_policy->user_policy.max = max_freq; ret = cpufreq_update_policy(cpu); @@ -76,6 +126,20 @@ static void check_temp(struct work_struc goto reschedule; } + if (temp >= (msm_thermal_tuners_ins.shutdown_temp)) { + mutex_lock(&emergency_shutdown_mutex); + pr_warn("################################\n"); + pr_warn("################################\n"); + pr_warn("- %u OVERTEMP! SHUTTING DOWN! -\n", msm_thermal_tuners_ins.shutdown_temp); + pr_warn("################################\n"); + pr_warn("################################\n"); + /* orderly poweroff tries to power down gracefully + if it fails it will force it. */ + orderly_poweroff(true); + cancel_delayed_work_sync(&check_temp_work); + mutex_unlock(&emergency_shutdown_mutex); + } + for_each_possible_cpu(cpu) { update_policy = 0; cpu_policy = cpufreq_cpu_get(cpu); @@ -83,32 +147,72 @@ static void check_temp(struct work_struc pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu); continue; } - if (temp >= allowed_max_high) { - if (cpu_policy->max > allowed_max_freq) { + + /* save pre-throttled max freq value */ + if (thermal_throttled == 0) + pre_throttled_max = cpu_policy->max; + + //low trip point + if ((temp >= msm_thermal_tuners_ins.allowed_low_high) && + (temp < msm_thermal_tuners_ins.allowed_mid_high) && + (cpu_policy->max > msm_thermal_tuners_ins.allowed_low_freq)) { + update_policy = 1; + max_freq = msm_thermal_tuners_ins.allowed_low_freq; + thermal_throttled = 1; + pr_warn("msm_thermal: Thermal Throttled (low)! temp: %lu\n", temp); + //low clr point + } else if ((temp < msm_thermal_tuners_ins.allowed_low_low) && + (thermal_throttled > 0)) { + if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) { + if (pre_throttled_max != 0) + max_freq = pre_throttled_max; + else { + max_freq = 1728000; + pr_warn("msm_thermal: ERROR! pre_throttled_max=0, falling back to %u\n", max_freq); + } update_policy = 1; - max_freq = allowed_max_freq; - } else { - pr_debug("msm_thermal: policy max for cpu %d " - "already < allowed_max_freq\n", cpu); + /* wait until 4th core is unthrottled */ + if (cpu == 3) + thermal_throttled = 0; + pr_warn("msm_thermal: Low Thermal Throttling Ended! temp: %lu\n", temp); } - } else if (temp < allowed_max_low) { -#ifdef CONFIG_SEC_DVFS - if (cpufreq_get_dvfs_state() != 1) { - if (cpu_policy->max < 1512000) { - max_freq = 1512000; - update_policy = 1; - } - } else - update_policy = 0; -#else + //mid trip point + } else if ((temp >= msm_thermal_tuners_ins.allowed_low_high) && + (temp < msm_thermal_tuners_ins.allowed_mid_low) && + (cpu_policy->max > msm_thermal_tuners_ins.allowed_mid_freq)) { + update_policy = 1; + max_freq = msm_thermal_tuners_ins.allowed_low_freq; + thermal_throttled = 2; + pr_warn("msm_thermal: Thermal Throttled (mid)! temp: %lu\n", temp); + //mid clr point + } else if ( (temp < msm_thermal_tuners_ins.allowed_mid_low) && + (thermal_throttled > 1)) { + if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) { + max_freq = msm_thermal_tuners_ins.allowed_low_freq; + update_policy = 1; + /* wait until 4th core is unthrottled */ + if (cpu == 3) + thermal_throttled = 1; + pr_warn("msm_thermal: Mid Thermal Throttling Ended! temp: %lu\n", temp); + } + //max trip point + } else if ((temp >= msm_thermal_tuners_ins.allowed_max_high) && + (cpu_policy->max > msm_thermal_tuners_ins.allowed_max_freq)) { + update_policy = 1; + max_freq = msm_thermal_tuners_ins.allowed_max_freq; + thermal_throttled = 3; + pr_warn("msm_thermal: Thermal Throttled (max)! temp: %lu\n", temp); + //max clr point + } else if ((temp < msm_thermal_tuners_ins.allowed_max_low) && + (thermal_throttled > 2)) { if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) { - max_freq = cpu_policy->cpuinfo.max_freq; + max_freq = msm_thermal_tuners_ins.allowed_mid_freq; update_policy = 1; - } else { - pr_debug("msm_thermal: policy max for cpu %d " - "already at max allowed\n", cpu); + /* wait until 4th core is unthrottled */ + if (cpu == 3) + thermal_throttled = 2; + pr_warn("msm_thermal: Max Thermal Throttling Ended! temp: %lu\n", temp); } -#endif } if (update_policy) @@ -119,8 +223,8 @@ static void check_temp(struct work_struc reschedule: if (enabled) - schedule_delayed_work(&check_temp_work, - msecs_to_jiffies(check_interval_ms)); + queue_delayed_work(check_temp_workq, &check_temp_work, + msecs_to_jiffies(msm_thermal_tuners_ins.check_interval_ms)); } static void disable_msm_thermal(void) @@ -128,6 +232,10 @@ 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_sync(&check_temp_work); + flush_scheduled_work(); + for_each_possible_cpu(cpu) { cpu_policy = cpufreq_cpu_get(cpu); if (cpu_policy) { @@ -163,14 +271,237 @@ static struct kernel_param_ops module_op module_param_cb(enabled, &module_ops, &enabled, 0644); 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_tuners_ins.object); \ +} + +show_one(shutdown_temp, shutdown_temp); +show_one(allowed_max_high, allowed_max_high); +show_one(allowed_max_low, allowed_max_low); +show_one(allowed_max_freq, allowed_max_freq); +show_one(allowed_mid_high, allowed_mid_high); +show_one(allowed_mid_low, allowed_mid_low); +show_one(allowed_mid_freq, allowed_mid_freq); +show_one(allowed_low_high, allowed_low_high); +show_one(allowed_low_low, allowed_low_low); +show_one(allowed_low_freq, allowed_low_freq); +show_one(check_interval_ms, check_interval_ms); + +static ssize_t store_shutdown_temp(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_thermal_tuners_ins.shutdown_temp = input; + + return count; +} + +static ssize_t store_allowed_max_high(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_thermal_tuners_ins.allowed_max_high = input; + + return count; +} + +static ssize_t store_allowed_max_low(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_thermal_tuners_ins.allowed_max_low = input; + + return count; +} + +static ssize_t store_allowed_max_freq(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_thermal_tuners_ins.allowed_max_freq = input; + + return count; +} + +static ssize_t store_allowed_mid_high(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_thermal_tuners_ins.allowed_mid_high = input; + + return count; +} + +static ssize_t store_allowed_mid_low(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_thermal_tuners_ins.allowed_mid_low = input; + + return count; +} + +static ssize_t store_allowed_mid_freq(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_thermal_tuners_ins.allowed_mid_freq = input; + + return count; +} + +static ssize_t store_allowed_low_high(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_thermal_tuners_ins.allowed_low_high = input; + + return count; +} + +static ssize_t store_allowed_low_low(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_thermal_tuners_ins.allowed_low_low = input; + + return count; +} + +static ssize_t store_allowed_low_freq(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_thermal_tuners_ins.allowed_low_freq = input; + + return count; +} + +static ssize_t store_check_interval_ms(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_thermal_tuners_ins.check_interval_ms = input; + + return count; +} + +define_one_global_rw(shutdown_temp); +define_one_global_rw(allowed_max_high); +define_one_global_rw(allowed_max_low); +define_one_global_rw(allowed_max_freq); +define_one_global_rw(allowed_mid_high); +define_one_global_rw(allowed_mid_low); +define_one_global_rw(allowed_mid_freq); +define_one_global_rw(allowed_low_high); +define_one_global_rw(allowed_low_low); +define_one_global_rw(allowed_low_freq); +define_one_global_rw(check_interval_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, + &check_interval_ms.attr, + NULL +}; + + +static struct attribute_group msm_thermal_attr_group = { + .attrs = msm_thermal_attributes, + .name = "conf", +}; +/**************************** SYSFS END ****************************/ + static int __init msm_thermal_init(void) { - int ret = 0; + int rc, ret = 0; enabled = 1; - INIT_DELAYED_WORK(&check_temp_work, check_temp); - - schedule_delayed_work(&check_temp_work, 0); + 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; }