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");