Re: [PATCH 1/2] thermal: qcom: tsens: switch wake IRQ handling to PM callbacks

From: Priyansh Jain

Date: Wed May 27 2026 - 01:00:34 EST




On 27-05-2026 12:05 am, Daniel Lezcano wrote:
On 5/26/26 12:18, Priyansh Jain wrote:
This change improves power management by using the standardized PM
framework for wake IRQ handling.

Move wake IRQ control to the PM suspend/resume path:
- store uplow/critical IRQ numbers in struct tsens_priv
- enable wake IRQs in tsens_suspend_common() when wakeup is allowed
- disable wake IRQs in tsens_resume_common()
- mark the device wakeup-capable during probe

This aligns TSENS wake behavior with suspend flow and avoids keeping
wake IRQs permanently enabled during runtime.

Signed-off-by: Priyansh Jain <priyansh.jain@xxxxxxxxxxxxxxxx>
---

[ ... ]

  static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
@@ -1172,7 +1180,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;
@@ -1204,8 +1212,8 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
          if (ret)
              dev_err(&pdev->dev, "%s: failed to get irq\n",
                  __func__);
-        else
-            enable_irq_wake(irq);
+        else if (irq_num)
+            *irq_num = irq;

You can remove this check, it is a static function called inside the driver which is supposed to know what it does (like not passing a NULL pointer parameter).

ACK, will update this in next revision
      }
      put_device(&pdev->dev);
@@ -1232,11 +1240,44 @@ static int tsens_reinit(struct tsens_priv *priv)
      return 0;
  }
+int tsens_suspend_common(struct tsens_priv *priv)
+{
+    if (!device_may_wakeup(priv->dev))
+        return 0;
+
+    if (priv->feat->combo_int) {
+        if (priv->combined_irq > 0)
+            enable_irq_wake(priv->combined_irq);
+    } else {
+        if (priv->uplow_irq > 0)
+            enable_irq_wake(priv->uplow_irq);
+
+        if (priv->crit_irq > 0)
+            enable_irq_wake(priv->crit_irq);
+    }
+
+    return 0;
+}
+
  int tsens_resume_common(struct tsens_priv *priv)
  {
      if (pm_suspend_target_state == PM_SUSPEND_MEM)
          tsens_reinit(priv);
+    if (!device_may_wakeup(priv->dev))
+        return 0;
+
+    if (priv->feat->combo_int) {
+        if (priv->combined_irq > 0)
+            disable_irq_wake(priv->combined_irq);
+    } else {
+        if (priv->uplow_irq > 0)
+            disable_irq_wake(priv->uplow_irq);
+
+        if (priv->crit_irq > 0)
+            disable_irq_wake(priv->crit_irq);
+    }
+
      return 0;
  }

If tsens_register_irq() fails, tsens_register() fails, then tsens_probe() fails and tsens_suspend_common() / tsens_resume_common() can not be called because the driver failed to start.

With the version of old DT without irq, the device_may_wakeup() should always return false (no irq == no wakeup)

Using the check on combined_irq / uplow_irq / crit_irq is not necessary because if the code goes after the device_may_wakeup() block, it is because interrupts are set.


Thanks for pointing this out, will update this in next revision
@@ -1276,15 +1317,18 @@ 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->combined_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)
+        if (priv->feat->crit_int) {
              ret = tsens_register_irq(priv, "critical",
-                         tsens_critical_irq_thread);
+                         tsens_critical_irq_thread,
+                         &priv->crit_irq);
+        }
      }
      return ret;
@@ -1343,6 +1387,8 @@ static int tsens_probe(struct platform_device *pdev)
      platform_set_drvdata(pdev, priv);
+    device_init_wakeup(dev, true);
+
      if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
          return -EINVAL;
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 2a7afa4c899b..83a8f3580ed0 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -567,6 +567,9 @@ struct tsens_context {
   * @ops: pointer to list of callbacks supported by this device
   * @debug_root: pointer to debugfs dentry for all tsens
   * @debug: pointer to debugfs dentry for tsens controller
+ * @uplow_irq: IRQ number for uplow (upper/lower) threshold interrupts
+ * @crit_irq: IRQ number for critical threshold interrupts
+ * @combined_irq: IRQ number for combined threshold interrupts
   * @sensor: list of sensors attached to this device
   */
  struct tsens_priv {
@@ -588,6 +591,10 @@ struct tsens_priv {
      struct dentry            *debug_root;
      struct dentry            *debug;
+    int                uplow_irq;
+    int                crit_irq;
+    int                combined_irq;
+
      struct tsens_sensor        sensor[] __counted_by(num_sensors);
  };
@@ -639,8 +646,17 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
  int get_temp_common(const struct tsens_sensor *s, int *temp);
  #ifdef CONFIG_SUSPEND
  int tsens_resume_common(struct tsens_priv *priv);
+int tsens_suspend_common(struct tsens_priv *priv);
  #else
-#define tsens_resume_common            NULL
+static inline int tsens_resume_common(struct tsens_priv *priv)
+{
+        return 0;

Strange indentation

ACK, will update this in next revision
+}
+
+static inline int tsens_suspend_common(struct tsens_priv *priv)
+{
+        return 0;

Ditto

ACK , will update this in next revision

Regards,
Priyansh
+}
  #endif
  /* TSENS target */