summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c5
-rw-r--r--arch/arm/mach-tegra/include/mach/thermal.h60
-rw-r--r--arch/arm/mach-tegra/tegra3_thermal.c286
4 files changed, 352 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 5ef2a5173cd3..7cf036210170 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -86,6 +86,7 @@ ifeq ($(CONFIG_TEGRA_THERMAL_THROTTLE),y)
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_throttle.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_throttle.o
endif
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_thermal.o
obj-$(CONFIG_TEGRA_IOVMM) += iovmm.o
obj-$(CONFIG_TEGRA_IOVMM_GART) += iovmm-gart.o
obj-$(CONFIG_TEGRA_IOVMM_SMMU) += iovmm-smmu.o
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index d6793f87cc5f..598c6b0408a9 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -38,6 +38,7 @@
#include <mach/clk.h>
#include <mach/edp.h>
+#include <mach/thermal.h>
#include "clock.h"
#include "cpu-tegra.h"
@@ -647,6 +648,10 @@ static int __init tegra_cpufreq_init(void)
suspend_index = table_data->suspend_index;
+ ret = tegra_thermal_init();
+ if (ret)
+ return ret;
+
ret = tegra_throttle_init(&tegra_cpu_lock);
if (ret)
return ret;
diff --git a/arch/arm/mach-tegra/include/mach/thermal.h b/arch/arm/mach-tegra/include/mach/thermal.h
new file mode 100644
index 000000000000..0d25c307f94e
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/thermal.h
@@ -0,0 +1,60 @@
+/*
+ * arch/arm/mach-tegra/thermal.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_THERMAL_H
+#define __MACH_THERMAL_H
+
+struct tegra_thermal_ops {
+ int (*get_temp) (void *, long *);
+ int (*set_limits) (void *, long, long);
+};
+
+struct tegra_thermal {
+ void *data;
+ struct tegra_thermal_ops *ops;
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ struct thermal_zone_device *thz;
+#endif
+};
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_thermal_init(void);
+int tegra_thermal_exit(void);
+
+struct tegra_thermal
+ *tegra_thermal_register(void *data, struct tegra_thermal_ops *ops);
+int tegra_thermal_unregister(struct tegra_thermal *thermal);
+int tegra_thermal_alert(struct tegra_thermal *thermal);
+#else
+static inline int tegra_thermal_init(void)
+{ return 0; }
+static inline int tegra_thermal_exit(void)
+{ return 0; }
+static inline struct tegra_thermal
+ *tegra_thermal_register(void *data, struct tegra_thermal_ops *ops)
+{ return NULL; }
+static inline int tegra_thermal_unregister(struct tegra_thermal *thermal)
+{ return 0; }
+static inline int tegra_thermal_alert(struct tegra_thermal *thermal)
+{ return 0; }
+#endif
+
+#define CELSIUS_TO_MILLICELSIUS(x) ((x)*1000)
+#define MILLICELSIUS_TO_CELSIUS(x) ((x)/1000)
+
+
+
+#endif /* __MACH_THERMAL_H */
diff --git a/arch/arm/mach-tegra/tegra3_thermal.c b/arch/arm/mach-tegra/tegra3_thermal.c
new file mode 100644
index 000000000000..30f8d7d8d800
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_thermal.c
@@ -0,0 +1,286 @@
+/*
+ * arch/arm/mach-tegra/tegra3_thermal.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/thermal.h>
+#include <mach/thermal.h>
+#include <mach/edp.h>
+#include <linux/slab.h>
+
+
+#include "clock.h"
+#include "cpu-tegra.h"
+#include "dvfs.h"
+
+#define MAX_ZONES (16)
+
+/* Thermal sysfs handles hysteresis */
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+#define ALERT_HYSTERESIS_THROTTLE 1
+#endif
+
+#define ALERT_HYSTERESIS_EDP 3
+
+#define THROTTLING_LIMIT (85000)
+#define MAX_LIMIT (90000)
+
+u8 thermal_zones[MAX_ZONES];
+int thermal_zones_sz;
+static int edp_thermal_zone_val = -1;
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+static bool throttle_enb;
+struct mutex mutex;
+#endif
+
+
+int __init tegra_thermal_init()
+{
+ const struct tegra_edp_limits *z;
+ int zones_sz;
+ int i;
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+ mutex_init(&mutex);
+#endif
+ tegra_get_cpu_edp_limits(&z, &zones_sz);
+ zones_sz = min(zones_sz, MAX_ZONES);
+
+ for (i = 0; i < zones_sz; i++)
+ thermal_zones[i] = z[i].temperature;
+
+ thermal_zones_sz = zones_sz;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+
+static int tegra_thermal_zone_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdevice) {
+ /* Support only Thermal Throttling (1 trip) for now */
+ return thermal_zone_bind_cooling_device(thermal, 0, cdevice);
+}
+
+static int tegra_thermal_zone_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdevice) {
+ /* Support only Thermal Throttling (1 trip) for now */
+ return thermal_zone_unbind_cooling_device(thermal, 0, cdevice);
+}
+
+static int tegra_thermal_zone_get_temp(struct thermal_zone_device *thz,
+ long *temp)
+{
+ struct tegra_thermal *thermal = thz->devdata;
+ thermal->ops->get_temp(thermal->data, temp);
+
+ return 0;
+}
+
+static int tegra_thermal_zone_get_trip_type(
+ struct thermal_zone_device *thermal,
+ int trip,
+ enum thermal_trip_type *type) {
+
+ /* Support only Thermal Throttling (1 trip) for now */
+ if (trip != 0)
+ return -EINVAL;
+
+ *type = THERMAL_TRIP_PASSIVE;
+
+ return 0;
+}
+
+static int tegra_thermal_zone_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip,
+ long *temp) {
+ /* Support only Thermal Throttling (1 trip) for now */
+ if (trip != 0)
+ return -EINVAL;
+
+ *temp = THROTTLING_LIMIT;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops tegra_thermal_zone_ops = {
+ .bind = tegra_thermal_zone_bind,
+ .unbind = tegra_thermal_zone_unbind,
+ .get_temp = tegra_thermal_zone_get_temp,
+ .get_trip_type = tegra_thermal_zone_get_trip_type,
+ .get_trip_temp = tegra_thermal_zone_get_trip_temp,
+};
+#endif
+
+
+
+struct tegra_thermal
+*tegra_thermal_register(void *data, struct tegra_thermal_ops *thermal_ops)
+{
+ long temp_milli;
+ struct tegra_thermal *thermal;
+#ifdef CONFIG_THERMAL_SYSFS
+ struct thermal_zone_device *thz;
+#endif
+
+ thermal = kzalloc(sizeof(struct tegra_thermal), GFP_KERNEL);
+ if (!thermal)
+ return ERR_PTR(-ENOMEM);
+
+ thermal->ops = thermal_ops;
+ thermal->data = data;
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ thz = thermal_zone_device_register("nct1008",
+ 1, /* trips */
+ thermal,
+ &tegra_thermal_zone_ops,
+ 2, /* tc1 */
+ 1, /* tc2 */
+ 2000, /* passive delay */
+ 0); /* polling delay */
+
+ if (IS_ERR(thz)) {
+ thz = NULL;
+ kfree(thermal);
+ return ERR_PTR(-ENODEV);
+ }
+
+ thermal->thz = thz;
+#endif
+
+ thermal->ops->get_temp(thermal->data, &temp_milli);
+ tegra_edp_update_thermal_zone(MILLICELSIUS_TO_CELSIUS(temp_milli));
+
+ return thermal;
+}
+
+int tegra_thermal_unregister(struct tegra_thermal *thermal)
+{
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ if (thermal->thz)
+ thermal_zone_device_unregister(thermal->thz);
+#endif
+
+ kfree(thermal);
+
+ return 0;
+}
+
+/* The thermal sysfs handles notifying the throttling
+ * cooling device */
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+static void tegra_therm_throttle(bool enable)
+{
+ if (throttle_enb != enable) {
+ mutex_lock(&mutex);
+ tegra_throttling_enable(enable);
+ throttle_enb = enable;
+ mutex_unlock(&mutex);
+ }
+}
+#endif
+
+int tegra_thermal_alert(struct tegra_thermal *thermal)
+{
+ int err;
+ int hysteresis;
+ long temp, tzone1, tzone2;
+ int lo_limit = 0, hi_limit = 0;
+ int nentries = thermal_zones_sz;
+ int i;
+
+ err = thermal->ops->get_temp(thermal->data, &temp);
+ if (err) {
+ pr_err("%s: get temp fail(%d)", __func__, err);
+ return err;
+ }
+
+ hysteresis = ALERT_HYSTERESIS_EDP;
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+ if (temp >= THROTTLING_LIMIT) {
+ /* start throttling */
+ tegra_therm_throttle(true);
+ hysteresis = ALERT_HYSTERESIS_THROTTLE;
+ } else if (temp <=
+ (THROTTLING_LIMIT -
+ ALERT_HYSTERESIS_THROTTLE)) {
+ /* switch off throttling */
+ tegra_therm_throttle(false);
+ }
+#endif
+
+ if (temp < CELSIUS_TO_MILLICELSIUS(thermal_zones[0])) {
+ lo_limit = 0;
+ hi_limit = thermal_zones[0];
+ } else if (temp >=
+ CELSIUS_TO_MILLICELSIUS(thermal_zones[nentries-1])) {
+ lo_limit = thermal_zones[nentries-1] - hysteresis;
+ hi_limit = MILLICELSIUS_TO_CELSIUS(MAX_LIMIT);
+ } else {
+ for (i = 0; (i + 1) < nentries; i++) {
+ tzone1 = thermal_zones[i];
+ tzone2 = thermal_zones[i + 1];
+
+ if (temp >= CELSIUS_TO_MILLICELSIUS(tzone1) &&
+ temp < CELSIUS_TO_MILLICELSIUS(tzone2)) {
+ lo_limit = tzone1 - hysteresis;
+ hi_limit = tzone2;
+ break;
+ }
+ }
+ }
+
+ err = thermal->ops->set_limits(thermal->data, lo_limit, hi_limit);
+
+ if (err)
+ return err;
+
+ /* inform edp governor */
+ if (edp_thermal_zone_val != temp)
+ /*
+ * FIXME: Move this direct tegra_ function call to be called
+ * via a pointer in 'struct nct1008_data' (like 'alarm_fn')
+ */
+ tegra_edp_update_thermal_zone(MILLICELSIUS_TO_CELSIUS(temp));
+
+ edp_thermal_zone_val = temp;
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ if (thermal->thz) {
+ if (!thermal->thz->passive)
+ thermal_zone_device_update(thermal->thz);
+ }
+#endif
+
+ return 0;
+}
+
+int tegra_thermal_exit(void)
+{
+ return 0;
+}