drivers: misc: Thermal estimator driver
Joshua Primero [Fri, 1 Jun 2012 01:11:23 +0000 (18:11 -0700)]
Added driver which estimates temperature based on
a linear formula from other temperature sensors.

bug 1007726

Change-Id: Ic0d3ba7f0d369d4321f55b03e6326ff4efbb512e
Signed-off-by: Joshua Primero <jprimero@nvidia.com>
Reviewed-on: http://git-master/r/105988
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sachin Nikam <snikam@nvidia.com>

drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/therm_est.c [new file with mode: 0644]
include/linux/therm_est.h [new file with mode: 0644]

index 7b7e948..0e9992f 100644 (file)
@@ -562,6 +562,12 @@ config MAX1749_VIBRATOR
        ---help---
        Adds a timed output vibrator device node for MAX1749 vibrator motor
 
+config THERM_EST
+       bool "Thermal estimator driver"
+       default n
+       ---help---
+       Thermal driver which estimates temperature based of other sensors.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index 5b3728e..9ba46db 100644 (file)
@@ -58,3 +58,4 @@ obj-$(CONFIG_INV_SENSORS)      += inv_mpu/
 obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o
 obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/
 obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o
+obj-$(CONFIG_THERM_EST)                += therm_est.o
diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c
new file mode 100644 (file)
index 0000000..fde61bc
--- /dev/null
@@ -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_hi_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;
+}
diff --git a/include/linux/therm_est.h b/include/linux/therm_est.h
new file mode 100644 (file)
index 0000000..035b08f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * include/linux/therm_est.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_THERM_EST_H
+#define _LINUX_THERM_EST_H
+
+#include <linux/workqueue.h>
+
+#define HIST_LEN (20)
+
+struct therm_est_subdevice {
+       void *dev_data;
+       int (*get_temp)(void *, long *);
+       long coeffs[HIST_LEN];
+       long hist[HIST_LEN];
+};
+
+struct therm_estimator {
+       long therm_est_lo_limit;
+       long therm_est_hi_limit;
+       void (*callback)(void *);
+       void *callback_data;
+       long cur_temp;
+       long polling_period;
+       struct workqueue_struct *workqueue;
+       struct delayed_work therm_est_work;
+       long toffset;
+       int ntemp;
+       int ndevs;
+       struct therm_est_subdevice **devs;
+};
+
+#ifdef CONFIG_THERM_EST
+struct therm_estimator *therm_est_register(
+                       struct therm_est_subdevice **devs,
+                       int ndevs,
+                       long toffset,
+                       long pperiod);
+int therm_est_get_temp(struct therm_estimator *est, long *temp);
+int therm_est_set_limits(struct therm_estimator *est,
+                       long lo_limit,
+                       long hi_limit);
+int therm_est_set_alert(struct therm_estimator *est,
+                       void (*cb)(void *),
+                       void *cb_data);
+#else
+static inline struct therm_estimator *therm_est_register(
+                       struct therm_est_subdevice **devs,
+                       int ndevs,
+                       long toffset,
+                       long pperiod)
+{ return NULL; }
+static inline int therm_est_get_temp(struct therm_estimator *est, long *temp)
+{ return -EINVAL; }
+static inline int therm_est_set_limits(struct therm_estimator *est,
+                       long lo_limit,
+                       long hi_limit)
+{ return -EINVAL; }
+static inline int therm_est_set_alert(struct therm_estimator *est,
+                       void (*cb)(void *),
+                       void *cb_data)
+{ return -EINVAL; }
+#endif
+#endif /* _LINUX_THERM_EST_H */