[v2] thermal/drivers/tsens: Add suspend to RAM support for tsens

Message ID 20240124105945.5902-1-quic_priyjain@quicinc.com
State New
Headers
Series [v2] thermal/drivers/tsens: Add suspend to RAM support for tsens |

Commit Message

Priyansh Jain Jan. 24, 2024, 10:59 a.m. UTC
  As part of suspend to RAM, system doesn't want to abort suspend
to RAM due to tsens interrupts since system is already going
into lowest power state. Hence disable tsens interrupt during
suspend to RAM callback.

Once it enters to suspend to RAM, tsens hardware will be turned off.
While resume, re-initialize tsens hardware and re-enable tsens
interrupts back.

Signed-off-by: Priyansh Jain <quic_priyjain@quicinc.com>
---
 drivers/thermal/qcom/tsens-v2.c |  2 +
 drivers/thermal/qcom/tsens.c    | 93 +++++++++++++++++++++++++++++++--
 drivers/thermal/qcom/tsens.h    |  7 +++
 3 files changed, 98 insertions(+), 4 deletions(-)
  

Patch

diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index 29a61d2d6ca3..1b74db6299c4 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -107,6 +107,8 @@  static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
 static const struct tsens_ops ops_generic_v2 = {
 	.init		= init_common,
 	.get_temp	= get_temp_tsens_valid,
+	.suspend	= tsens_suspend_common,
+	.resume		= tsens_resume_common,
 };
 
 struct tsens_plat_data data_tsens_v2 = {
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index 6d7c16ccb44d..603ccb91009d 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -17,6 +17,7 @@ 
 #include <linux/pm.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
 #include <linux/thermal.h>
 #include "../thermal_hwmon.h"
 #include "tsens.h"
@@ -1153,7 +1154,7 @@  static const struct thermal_zone_device_ops tsens_of_ops = {
 };
 
 static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
-			      irq_handler_t thread_fn)
+			      irq_handler_t thread_fn, int *irq_num)
 {
 	struct platform_device *pdev;
 	int ret, irq;
@@ -1169,6 +1170,7 @@  static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
 		if (irq == -ENXIO)
 			ret = 0;
 	} else {
+		*irq_num = irq;
 		/* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
 		if (tsens_version(priv) == VER_0)
 			ret = devm_request_threaded_irq(&pdev->dev, irq,
@@ -1193,6 +1195,85 @@  static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
 	return ret;
 }
 
+static int tsens_reinit(struct tsens_priv *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->ul_lock, flags);
+
+	/* in VER_0 TSENS need to be explicitly enabled */
+	if (tsens_version(priv) == VER_0)
+		regmap_field_write(priv->rf[TSENS_EN], 1);
+
+	/*
+	 * Re-enable the watchdog, unmask the bark.
+	 * Disable cycle completion monitoring
+	 */
+	if (priv->feat->has_watchdog) {
+		regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
+		regmap_field_write(priv->rf[CC_MON_MASK], 1);
+	}
+
+	/* Re-enable interrupts */
+	if (tsens_version(priv) >= VER_0_1)
+		tsens_enable_irq(priv);
+
+	spin_unlock_irqrestore(&priv->ul_lock, flags);
+
+	return 0;
+}
+
+int tsens_suspend_common(struct tsens_priv *priv)
+{
+	switch (pm_suspend_target_state) {
+	case PM_SUSPEND_MEM:
+		if (priv->combo_irq > 0) {
+			disable_irq_nosync(priv->combo_irq);
+			disable_irq_wake(priv->combo_irq);
+		}
+
+		if (priv->uplow_irq > 0) {
+			disable_irq_nosync(priv->uplow_irq);
+			disable_irq_wake(priv->uplow_irq);
+		}
+
+		if (priv->crit_irq > 0) {
+			disable_irq_nosync(priv->crit_irq);
+			disable_irq_wake(priv->crit_irq);
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int tsens_resume_common(struct tsens_priv *priv)
+{
+	switch (pm_suspend_target_state) {
+	case PM_SUSPEND_MEM:
+		tsens_reinit(priv);
+		if (priv->combo_irq > 0) {
+			enable_irq(priv->combo_irq);
+			enable_irq_wake(priv->combo_irq);
+		}
+
+		if (priv->uplow_irq > 0) {
+			enable_irq(priv->uplow_irq);
+			enable_irq_wake(priv->uplow_irq);
+		}
+
+		if (priv->crit_irq > 0) {
+			enable_irq(priv->crit_irq);
+			enable_irq_wake(priv->crit_irq);
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
 static int tsens_register(struct tsens_priv *priv)
 {
 	int i, ret;
@@ -1227,15 +1308,19 @@  static int tsens_register(struct tsens_priv *priv)
 
 	if (priv->feat->combo_int) {
 		ret = tsens_register_irq(priv, "combined",
-					 tsens_combined_irq_thread);
+					 tsens_combined_irq_thread,
+					 &priv->combo_irq);
 	} else {
-		ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
+		ret = tsens_register_irq(priv, "uplow",
+					 tsens_irq_thread,
+					 &priv->uplow_irq);
 		if (ret < 0)
 			return ret;
 
 		if (priv->feat->crit_int)
 			ret = tsens_register_irq(priv, "critical",
-						 tsens_critical_irq_thread);
+						 tsens_critical_irq_thread,
+						 &priv->crit_irq);
 	}
 
 	return ret;
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index cb637fa289ca..268bf56105be 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -582,6 +582,11 @@  struct tsens_priv {
 	const struct reg_field		*fields;
 	const struct tsens_ops		*ops;
 
+	/* For saving irq number to re-use later */
+	int				uplow_irq;
+	int				crit_irq;
+	int				combo_irq;
+
 	struct dentry			*debug_root;
 	struct dentry			*debug;
 
@@ -634,6 +639,8 @@  void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mo
 int init_common(struct tsens_priv *priv);
 int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
 int get_temp_common(const struct tsens_sensor *s, int *temp);
+int tsens_suspend_common(struct tsens_priv *priv);
+int tsens_resume_common(struct tsens_priv *priv);
 
 /* TSENS target */
 extern struct tsens_plat_data data_8960;