summaryrefslogtreecommitdiff
path: root/drivers/misc/therm_est.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/therm_est.c')
-rw-r--r--drivers/misc/therm_est.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c
new file mode 100644
index 000000000000..e67fc664c7af
--- /dev/null
+++ b/drivers/misc/therm_est.c
@@ -0,0 +1,139 @@
+/*
+ * drivers/misc/therm_est.c
+ *
+ * Copyright (C) 2010-2012 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/slab.h>
+#include <linux/syscalls.h>
+#include <linux/therm_est.h>
+
+int therm_est_get_temp(struct therm_estimator *est, long *temp)
+{
+ *temp = est->cur_temp;
+ return 0;
+}
+
+int therm_est_set_limits(struct therm_estimator *est,
+ long lo_limit,
+ long hi_limit)
+{
+ est->therm_est_lo_limit = lo_limit;
+ est->therm_est_hi_limit = hi_limit;
+ return 0;
+}
+
+int therm_est_set_alert(struct therm_estimator *est,
+ void (*cb)(void *),
+ void *cb_data)
+{
+ if ((!cb) || est->callback)
+ BUG();
+
+ est->callback = cb;
+ est->callback_data = cb_data;
+
+ return 0;
+}
+
+static void therm_est_work_func(struct work_struct *work)
+{
+ int i, j, index, sum = 0;
+ long temp;
+ struct delayed_work *dwork = container_of (work,
+ struct delayed_work, work);
+ struct therm_estimator *est = container_of(
+ dwork,
+ struct therm_estimator,
+ therm_est_work);
+
+ for (i = 0; i < est->ndevs; i++) {
+ if (est->devs[i]->get_temp(est->devs[i]->dev_data, &temp))
+ continue;
+ est->devs[i]->hist[(est->ntemp % HIST_LEN)] = temp;
+ }
+
+ for (i = 0; i < est->ndevs; i++) {
+ for (j = 0; j < HIST_LEN; j++) {
+ index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
+ sum += est->devs[i]->hist[index] *
+ est->devs[i]->coeffs[j];
+ }
+ }
+
+ est->cur_temp = sum / 100 + est->toffset;
+
+ est->ntemp++;
+
+ if (est->callback && ((est->cur_temp >= est->therm_est_hi_limit) ||
+ (est->cur_temp <= est->therm_est_lo_limit)))
+ est->callback(est->callback_data);
+
+ queue_delayed_work(est->workqueue, &est->therm_est_work,
+ msecs_to_jiffies(est->polling_period));
+}
+
+struct therm_estimator *therm_est_register(
+ struct therm_est_subdevice **devs,
+ int ndevs,
+ long toffset,
+ long polling_period)
+{
+ int i, j;
+ long temp;
+ struct therm_estimator *est;
+ struct therm_est_subdevice *dev;
+
+ est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(est))
+ return ERR_PTR(-ENOMEM);
+
+ est->devs = devs;
+ est->ndevs = ndevs;
+ est->toffset = toffset;
+ est->polling_period = polling_period;
+
+ /* initialize history */
+ for (i = 0; i < ndevs; i++) {
+ dev = est->devs[i];
+
+ if (dev->get_temp(dev->dev_data, &temp)) {
+ kfree(est);
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (j = 0; j < HIST_LEN; j++) {
+ dev->hist[j] = temp;
+ }
+ }
+
+ est->workqueue = alloc_workqueue("therm_est",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+ INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func);
+
+ queue_delayed_work(est->workqueue,
+ &est->therm_est_work,
+ msecs_to_jiffies(est->polling_period));
+
+ return est;
+}