thermal: tsens8960: Add suspend/resume for TSENS

TSENS does not operate reliably during VDD_CX minimization.

Incorrect temperature readings are reported on some instances
when Apps processor comes out of suspend. This leads the
TSENS reporting incorrect temperature during system resume.

Change-Id: I882b37fbe290477926c3a242d3092be8b950dfab
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index 71df297..fbb377e 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -24,6 +24,7 @@
 #include <linux/msm_tsens.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/pm.h>
 
 #include <mach/msm_iomap.h>
 #include <mach/socinfo.h>
@@ -134,6 +135,9 @@
 #define TSENS_8064_S5_STATUS_ADDR		(MSM_CLK_CTL_BASE + 0x00003664)
 #define TSENS_8064_SEQ_SENSORS				5
 #define TSENS_8064_S4_S5_OFFSET				40
+#define TSENS_CNTL_RESUME_MASK				0xfffffff9
+#define TSENS_8960_SENSOR_MASK				0xf8
+#define TSENS_8064_SENSOR_MASK				0x3ff8
 
 static int tsens_status_cntl_start;
 
@@ -153,6 +157,8 @@
 	int				tsens_factor;
 	uint32_t			tsens_num_sensor;
 	enum platform_type		hw_type;
+	int				pm_tsens_thr_data;
+	int				pm_tsens_cntl;
 	struct tsens_tm_device_sensor	sensor[0];
 };
 
@@ -265,9 +271,6 @@
 		return -EINVAL;
 
 	if (mode != tm_sensor->mode) {
-		pr_info("%s: mode: %d --> %d\n", __func__, tm_sensor->mode,
-									 mode);
-
 		reg = readl_relaxed(TSENS_CNTL_ADDR);
 
 		mask = 1 << (tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT);
@@ -686,6 +689,83 @@
 	}
 }
 
+#ifdef CONFIG_PM
+static int tsens_suspend(struct device *dev)
+{
+	int i = 0;
+
+	tmdev->pm_tsens_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	tmdev->pm_tsens_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(tmdev->pm_tsens_cntl &
+		~(TSENS_8960_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	tmdev->prev_reading_avail = 0;
+	for (i = 0; i < tmdev->tsens_num_sensor; i++)
+		tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+	disable_irq_nosync(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static int tsens_resume(struct device *dev)
+{
+	unsigned int reg_cntl = 0, reg_cfg = 0, reg_sensor_mask = 0;
+	unsigned int reg_status_cntl = 0, reg_thr_data = 0, i = 0;
+
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
+
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+			SENSORS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+	} else if (tmdev->hw_type == APQ_8064) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			TSENS_8064_SENSORS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+		reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		reg_status_cntl |= TSENS_MIN_STATUS_MASK |
+			TSENS_MAX_STATUS_MASK;
+		writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
+	}
+
+	reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
+	reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
+		(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
+	writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
+
+	writel_relaxed((tmdev->pm_tsens_cntl & TSENS_CNTL_RESUME_MASK),
+						TSENS_CNTL_ADDR);
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(tmdev->pm_tsens_thr_data, TSENS_THRESHOLD_ADDR);
+	reg_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615)
+		reg_sensor_mask = ((reg_cntl & TSENS_8960_SENSOR_MASK)
+				>> TSENS_SENSOR0_SHIFT);
+	else {
+		reg_sensor_mask = ((reg_cntl & TSENS_8064_SENSOR_MASK)
+				>> TSENS_SENSOR0_SHIFT);
+	}
+
+	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		if (reg_sensor_mask & TSENS_MASK1)
+			tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+		reg_sensor_mask >>= 1;
+	}
+
+	enable_irq(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static const struct dev_pm_ops tsens_pm_ops = {
+	.suspend	= tsens_suspend,
+	.resume		= tsens_resume,
+};
+#endif
+
 static void tsens_disable_mode(void)
 {
 	unsigned int reg_cntl = 0;
@@ -717,8 +797,7 @@
 			(TSENS_MEASURE_PERIOD << 18) |
 			TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
 			TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
-			(((1 << tmdev->tsens_num_sensor) - 1) <<
-			TSENS_SENSOR0_SHIFT);
+			SENSORS_EN;
 		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
 		reg_cntl |= TSENS_EN;
 		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
@@ -744,8 +823,7 @@
 	} else if (tmdev->hw_type == APQ_8064) {
 		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
 			(TSENS_MEASURE_PERIOD << 18) |
-			(((1 << tmdev->tsens_num_sensor) - 1) <<
-			 TSENS_SENSOR0_SHIFT);
+			TSENS_8064_SENSORS_EN;
 		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
 		reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
 		reg_status_cntl |= TSENS_LOWER_STATUS_CLR |
@@ -910,7 +988,7 @@
 	return rc;
 }
 
-static int __init tsens_tm_init(void)
+static int __devinit tsens_tm_probe(struct platform_device *pdev)
 {
 	int rc, i;
 
@@ -958,7 +1036,7 @@
 	return rc;
 }
 
-static void __exit tsens_tm_remove(void)
+static int __devexit tsens_tm_remove(struct platform_device *pdev)
 {
 	int i;
 
@@ -969,10 +1047,32 @@
 		thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
 	kfree(tmdev);
 	tmdev = NULL;
+	return 0;
 }
 
-module_init(tsens_tm_init);
-module_exit(tsens_tm_remove);
+static struct platform_driver tsens_tm_driver = {
+	.probe = tsens_tm_probe,
+	.remove = tsens_tm_remove,
+	.driver = {
+		.name = "tsens8960-tm",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &tsens_pm_ops,
+#endif
+	},
+};
+
+static int __init _tsens_tm_init(void)
+{
+	return platform_driver_register(&tsens_tm_driver);
+}
+module_init(_tsens_tm_init);
+
+static void __exit _tsens_tm_remove(void)
+{
+	platform_driver_unregister(&tsens_tm_driver);
+}
+module_exit(_tsens_tm_remove);
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("MSM8960 Temperature Sensor driver");