summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/Kconfig19
-rw-r--r--drivers/thermal/Makefile2
-rw-r--r--drivers/thermal/cpu_cooling.c302
-rw-r--r--drivers/thermal/exynos_thermal.c334
-rw-r--r--drivers/thermal/thermal_sys.c27
5 files changed, 683 insertions, 1 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f7f71b2d310..938953fd1c2 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -18,3 +18,22 @@ config THERMAL_HWMON
depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
default y
+
+config CPU_THERMAL
+ bool "generic cpu cooling support"
+ depends on THERMAL
+ help
+ This implements the generic cpu cooling mechanism through frequency
+ reduction, cpu hotplug and any other ways of reducing temperature. An
+ ACPI version of this already exists(drivers/acpi/processor_thermal.c).
+ This will be useful for platforms using the generic thermal interface
+ and not the ACPI interface.
+ If you want this support, you should say Y or M here.
+
+menuconfig SAMSUNG_THERMAL_INTERFACE
+ bool "Samsung Thermal support"
+ depends on THERMAL && CPU_THERMAL
+ help
+ This is a samsung thermal interface which will be used as
+ a link between sensors and cooling devices with linux thermal
+ framework.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 31108a01c22..c67b6b222be 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
+obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += exynos_thermal.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
new file mode 100644
index 00000000000..cdd148c0036
--- /dev/null
+++ b/drivers/thermal/cpu_cooling.c
@@ -0,0 +1,302 @@
+/*
+ * linux/drivers/thermal/cpu_cooling.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
+ * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * 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
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/cpu_cooling.h>
+
+#ifdef CONFIG_CPU_FREQ
+struct cpufreq_cooling_device {
+ struct thermal_cooling_device *cool_dev;
+ struct freq_pctg_table *tab_ptr;
+ unsigned int tab_size;
+ unsigned int cpufreq_state;
+ const struct cpumask *allowed_cpus;
+};
+
+static struct cpufreq_cooling_device *cpufreq_device;
+
+/*Below codes defines functions to be used for cpufreq as cooling device*/
+static bool is_cpufreq_valid(int cpu)
+{
+ struct cpufreq_policy policy;
+ if (!cpufreq_get_policy(&policy, cpu))
+ return true;
+ return false;
+}
+
+static int cpufreq_apply_cooling(int cooling_state)
+{
+ int cpuid, this_cpu = smp_processor_id();
+
+ if (!is_cpufreq_valid(this_cpu))
+ return 0;
+
+ if (cooling_state > cpufreq_device->tab_size)
+ return -EINVAL;
+
+ /*Check if last cooling level is same as current cooling level*/
+ if (cpufreq_device->cpufreq_state == cooling_state)
+ return 0;
+
+ cpufreq_device->cpufreq_state = cooling_state;
+
+ for_each_cpu(cpuid, cpufreq_device->allowed_cpus) {
+ if (is_cpufreq_valid(cpuid))
+ cpufreq_update_policy(cpuid);
+ }
+
+ return 0;
+}
+
+static int thermal_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_policy *policy = data;
+ struct freq_pctg_table *th_table;
+ unsigned long max_freq = 0;
+ unsigned int cpu = policy->cpu, th_pctg = 0, level;
+
+ if (event != CPUFREQ_ADJUST)
+ return 0;
+
+ level = cpufreq_device->cpufreq_state;
+
+ if (level > 0) {
+ th_table =
+ &(cpufreq_device->tab_ptr[level - 1]);
+ th_pctg = th_table->freq_clip_pctg[cpu];
+ }
+
+ max_freq =
+ (policy->cpuinfo.max_freq * (100 - th_pctg)) / 100;
+
+ cpufreq_verify_within_limits(policy, 0, max_freq);
+
+ return 0;
+}
+
+/*
+ * cpufreq cooling device callback functions
+ */
+static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cpufreq_device->tab_size;
+ return 0;
+}
+
+static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cpufreq_device->cpufreq_state;
+ return 0;
+}
+
+/*This cooling may be as PASSIVE/STATE_ACTIVE type*/
+static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ cpufreq_apply_cooling(state);
+ return 0;
+}
+
+/* bind cpufreq callbacks to cpufreq cooling device */
+static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
+ .get_max_state = cpufreq_get_max_state,
+ .get_cur_state = cpufreq_get_cur_state,
+ .set_cur_state = cpufreq_set_cur_state,
+};
+
+static struct notifier_block thermal_cpufreq_notifier_block = {
+ .notifier_call = thermal_cpufreq_notifier,
+};
+
+struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size,
+ const struct cpumask *mask_val)
+{
+ struct thermal_cooling_device *cool_dev;
+
+ if (tab_ptr == NULL || tab_size == 0)
+ return ERR_PTR(-EINVAL);
+
+ cpufreq_device =
+ kzalloc(sizeof(struct cpufreq_cooling_device), GFP_KERNEL);
+
+ if (!cpufreq_device)
+ return ERR_PTR(-ENOMEM);
+
+ cool_dev = thermal_cooling_device_register("thermal-cpufreq", NULL,
+ &cpufreq_cooling_ops);
+ if (!cool_dev) {
+ kfree(cpufreq_device);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cpufreq_device->tab_ptr = tab_ptr;
+ cpufreq_device->tab_size = tab_size;
+ cpufreq_device->cool_dev = cool_dev;
+ cpufreq_device->allowed_cpus = mask_val;
+
+ cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ return cool_dev;
+}
+EXPORT_SYMBOL(cpufreq_cooling_register);
+
+void cpufreq_cooling_unregister(void)
+{
+ cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ thermal_cooling_device_unregister(cpufreq_device->cool_dev);
+ kfree(cpufreq_device);
+}
+EXPORT_SYMBOL(cpufreq_cooling_unregister);
+#else /*!CONFIG_CPU_FREQ*/
+struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size)
+{
+ return NULL;
+}
+EXPORT_SYMBOL(cpufreq_cooling_register);
+void cpufreq_cooling_unregister(void)
+{
+ return;
+}
+EXPORT_SYMBOL(cpufreq_cooling_unregister);
+#endif /*CONFIG_CPU_FREQ*/
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+struct hotplug_cooling_device {
+ struct thermal_cooling_device *cool_dev;
+ unsigned int hotplug_state;
+ const struct cpumask *allowed_cpus;
+};
+static struct hotplug_cooling_device *hotplug_device;
+
+/*
+ * cpu hotplug cooling device callback functions
+ */
+static int cpuhotplug_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = 1;
+ return 0;
+}
+
+static int cpuhotplug_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ /*This cooling device may be of type ACTIVE, so state field
+ can be 0 or 1*/
+ *state = hotplug_device->hotplug_state;
+ return 0;
+}
+
+/*This cooling may be as PASSIVE/STATE_ACTIVE type*/
+static int cpuhotplug_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ int cpuid, this_cpu = smp_processor_id();
+
+ if (hotplug_device->hotplug_state == state)
+ return 0;
+
+ /*This cooling device may be of type ACTIVE, so state field
+ can be 0 or 1*/
+ if (state == 1) {
+ for_each_cpu(cpuid, hotplug_device->allowed_cpus) {
+ if (cpu_online(cpuid) && (cpuid != this_cpu))
+ cpu_down(cpuid);
+ }
+ } else if (state == 0) {
+ for_each_cpu(cpuid, hotplug_device->allowed_cpus) {
+ if (!cpu_online(cpuid) && (cpuid != this_cpu))
+ cpu_up(cpuid);
+ }
+ } else
+ return -EINVAL;
+
+ hotplug_device->hotplug_state = state;
+
+ return 0;
+}
+/* bind hotplug callbacks to cpu hotplug cooling device */
+static struct thermal_cooling_device_ops cpuhotplug_cooling_ops = {
+ .get_max_state = cpuhotplug_get_max_state,
+ .get_cur_state = cpuhotplug_get_cur_state,
+ .set_cur_state = cpuhotplug_set_cur_state,
+};
+
+struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val)
+{
+ struct thermal_cooling_device *cool_dev;
+
+ hotplug_device =
+ kzalloc(sizeof(struct hotplug_cooling_device), GFP_KERNEL);
+
+ if (!hotplug_device)
+ return ERR_PTR(-ENOMEM);
+
+ cool_dev = thermal_cooling_device_register("thermal-cpuhotplug", NULL,
+ &cpuhotplug_cooling_ops);
+ if (!cool_dev) {
+ kfree(hotplug_device);
+ return ERR_PTR(-EINVAL);
+ }
+
+ hotplug_device->cool_dev = cool_dev;
+ hotplug_device->hotplug_state = 0;
+ hotplug_device->allowed_cpus = mask_val;
+
+ return cool_dev;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_register);
+
+void cpuhotplug_cooling_unregister(void)
+{
+ thermal_cooling_device_unregister(hotplug_device->cool_dev);
+ kfree(hotplug_device);
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_unregister);
+#else /*!CONFIG_HOTPLUG_CPU*/
+struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val)
+{
+ return NULL;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_register);
+void cpuhotplug_cooling_unregister(void)
+{
+ return;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_unregister);
+#endif /*CONFIG_HOTPLUG_CPU*/
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644
index 00000000000..ff189b145ad
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,334 @@
+/* linux/drivers/staging/thermal_exynos4/thermal_interface.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu_cooling.h>
+#include <linux/platform_data/exynos4_tmu.h>
+#include <linux/exynos_thermal.h>
+
+static unsigned int verbose;
+
+struct exynos4_thermal_zone {
+ unsigned int idle_interval;
+ unsigned int active_interval;
+ struct thermal_zone_device *therm_dev;
+ struct thermal_cooling_device *cool_dev;
+ struct platform_device *exynos4_dev;
+ struct exynos4_tmu_platform_data *sensor_data;
+};
+static struct thermal_sensor_info *exynos4_sensor_info;
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ if (exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ *mode = THERMAL_DEVICE_DISABLED;
+ } else
+ *mode = THERMAL_DEVICE_ENABLED;
+ return 0;
+}
+
+/*
+ * set operation mode;
+ * enabled: the thermal layer of the kernel takes care about
+ * the temperature.
+ * disabled: temperature sensor is not enabled.
+ */
+static int exynos4_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ if (!th_zone->therm_dev) {
+ pr_notice("thermal zone not registered\n");
+ return 0;
+ }
+ if (mode == THERMAL_DEVICE_ENABLED)
+ th_zone->therm_dev->polling_delay =
+ th_zone->active_interval*1000;
+ else
+ th_zone->therm_dev->polling_delay =
+ th_zone->idle_interval*1000;
+
+ thermal_zone_device_update(th_zone->therm_dev);
+ pr_info("thermal polling set for duration=%d sec\n",
+ th_zone->therm_dev->polling_delay/1000);
+ return 0;
+}
+
+/*This may be called from interrupt based temperature sensor*/
+void exynos4_report_trigger(void)
+{
+ unsigned int th_temp = th_zone->sensor_data->threshold;
+ unsigned int monitor_temp = th_temp +
+ th_zone->sensor_data->trigger_levels[1];
+
+ thermal_zone_device_update(th_zone->therm_dev);
+
+ if (th_zone->therm_dev->last_temperature > monitor_temp)
+ th_zone->therm_dev->polling_delay =
+ th_zone->active_interval*1000;
+ else
+ th_zone->therm_dev->polling_delay =
+ th_zone->idle_interval*1000;
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+ enum thermal_trip_type *type)
+{
+ if (verbose)
+ pr_info("%s, trip no=%d\n", __func__, trip);
+ if (trip == 0 || trip == 1)
+ *type = THERMAL_TRIP_STATE_ACTIVE;
+ else if (trip == 2)
+ *type = THERMAL_TRIP_CRITICAL;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+ unsigned long *temp)
+{
+ unsigned int th_temp = th_zone->sensor_data->threshold;
+
+ /*Monitor zone*/
+ if (trip == 0)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[1];
+ /*Warn zone*/
+ else if (trip == 1)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[2];
+ /*Panic zone*/
+ else if (trip == 2)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[3];
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ unsigned int th_temp = th_zone->sensor_data->threshold;
+ /*Panic zone*/
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[3];
+ return 0;
+}
+
+static int exynos4_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ /* if the cooling device is the one from exynos4 bind it */
+ if (cdev != th_zone->cool_dev)
+ return 0;
+
+ if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+ pr_err("error binding cooling dev\n");
+ return -EINVAL;
+ }
+ if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+ pr_err("error binding cooling dev\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int exynos4_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ if (cdev != th_zone->cool_dev)
+ return 0;
+ if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+ pr_err("error unbinding cooling dev\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *t)
+{
+ int temp = 0;
+ void *data;
+
+ if (!exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = exynos4_sensor_info->private_data;
+ temp = exynos4_sensor_info->read_temperature(data);
+
+ if (verbose)
+ pr_notice("temp %d\n", temp);
+
+ *t = temp;
+ return 0;
+}
+
+/* bind callback functions to thermalzone */
+static struct thermal_zone_device_ops exynos4_dev_ops = {
+ .bind = exynos4_bind,
+ .unbind = exynos4_unbind,
+ .get_temp = exynos4_get_temp,
+ .get_mode = exynos4_get_mode,
+ .set_mode = exynos4_set_mode,
+ .get_trip_type = exynos4_get_trip_type,
+ .get_trip_temp = exynos4_get_trip_temp,
+ .get_crit_temp = exynos4_get_crit_temp,
+};
+
+int exynos4_register_temp_sensor(struct thermal_sensor_info *sensor_info)
+{
+ exynos4_sensor_info = sensor_info;
+ return 0;
+}
+
+
+static int __devinit exynos4_probe(struct platform_device *device)
+{
+ return 0;
+}
+
+static int exynos4_remove(struct platform_device *device)
+{
+ return 0;
+}
+
+static struct platform_driver exynos4_driver = {
+ .driver = {
+ .name = "exynos4",
+ .owner = THIS_MODULE,
+ },
+ .probe = exynos4_probe,
+ .remove = exynos4_remove,
+};
+
+static int exynos4_register_platform(void)
+{
+ int err = 0;
+
+ th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+ if (!th_zone)
+ return -ENOMEM;
+
+ err = platform_driver_register(&exynos4_driver);
+ if (err)
+ return err;
+
+ th_zone->exynos4_dev = platform_device_alloc("exynos4", -1);
+ if (!th_zone->exynos4_dev) {
+ err = -ENOMEM;
+ goto err_device_alloc;
+ }
+ err = platform_device_add(th_zone->exynos4_dev);
+ if (err)
+ goto err_device_add;
+
+ return 0;
+
+err_device_add:
+ platform_device_put(th_zone->exynos4_dev);
+err_device_alloc:
+ platform_driver_unregister(&exynos4_driver);
+ return err;
+}
+
+static void exynos4_unregister_platform(void)
+{
+ platform_device_unregister(th_zone->exynos4_dev);
+ platform_driver_unregister(&exynos4_driver);
+ kfree(th_zone);
+}
+
+static int exynos4_register_thermal(void)
+{
+ if (!exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+
+ th_zone->sensor_data = exynos4_sensor_info->sensor_data;
+ if (!th_zone->sensor_data) {
+ pr_info("Temperature sensor data not initialised\n");
+ return -EINVAL;
+ }
+
+ th_zone->cool_dev = cpufreq_cooling_register(
+ (struct freq_pctg_table *)th_zone->sensor_data->freq_tab,
+ th_zone->sensor_data->level_count);
+
+ if (IS_ERR(th_zone->cool_dev))
+ return -EINVAL;
+
+ th_zone->therm_dev = thermal_zone_device_register("exynos4-therm",
+ 3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+ if (IS_ERR(th_zone->therm_dev))
+ return -EINVAL;
+
+ th_zone->active_interval = 1;
+ th_zone->idle_interval = 10;
+ exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+ return 0;
+}
+
+static void exynos4_unregister_thermal(void)
+{
+ if (th_zone->cool_dev)
+ cpufreq_cooling_unregister();
+
+ if (th_zone->therm_dev)
+ thermal_zone_device_unregister(th_zone->therm_dev);
+}
+
+static int __init exynos4_thermal_init(void)
+{
+ int err = 0;
+
+ err = exynos4_register_platform();
+ if (err)
+ goto out_err;
+
+ err = exynos4_register_thermal();
+ if (err)
+ goto err_unreg;
+
+ return 0;
+
+err_unreg:
+ exynos4_unregister_thermal();
+ exynos4_unregister_platform();
+
+out_err:
+ return err;
+}
+
+static void __exit exynos4_thermal_exit(void)
+{
+ exynos4_unregister_thermal();
+ exynos4_unregister_platform();
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amit Daniel <amit.kachhap@linaro.org>");
+MODULE_DESCRIPTION("samsung Exynos4 thermal monitor driver");
+
+module_init(exynos4_thermal_init);
+module_exit(exynos4_thermal_exit);
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 708f8e92771..2a42296a1f6 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -192,6 +192,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "passive\n");
case THERMAL_TRIP_ACTIVE:
return sprintf(buf, "active\n");
+ case THERMAL_TRIP_STATE_ACTIVE:
+ return sprintf(buf, "state-active\n");
default:
return sprintf(buf, "unknown\n");
}
@@ -1035,7 +1037,7 @@ EXPORT_SYMBOL(thermal_cooling_device_unregister);
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count, ret = 0;
- long temp, trip_temp;
+ long temp, trip_temp, max_state, last_trip_change = 0;
enum thermal_trip_type trip_type;
struct thermal_cooling_device_instance *instance;
struct thermal_cooling_device *cdev;
@@ -1086,6 +1088,29 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
cdev->ops->set_cur_state(cdev, 0);
}
break;
+ case THERMAL_TRIP_STATE_ACTIVE:
+ list_for_each_entry(instance, &tz->cooling_devices,
+ node) {
+ if (instance->trip != count)
+ continue;
+
+ if (temp <= last_trip_change)
+ continue;
+
+ cdev = instance->cdev;
+ cdev->ops->get_max_state(cdev, &max_state);
+
+ if ((temp >= trip_temp) &&
+ ((count + 1) <= max_state))
+ cdev->ops->set_cur_state(cdev,
+ count + 1);
+ else if ((temp < trip_temp) &&
+ (count <= max_state))
+ cdev->ops->set_cur_state(cdev, count);
+
+ last_trip_change = trip_temp;
+ }
+ break;
case THERMAL_TRIP_PASSIVE:
if (temp >= trip_temp || tz->passive)
thermal_zone_device_passive(tz, temp,