[PATCH v2 5/9] irqchip/irq-qcom-mpm: Program wakeup timer when CPU cluster goes to LPM

From: Sneh Mankad

Date: Thu Oct 15 2026 - 06:10:52 EST


The next wakeup timer value needs to be set in MPM timer as the arch timer
interrupt can not wakeup the SoC if after the deepest CPUidle states the
SoC also enters deepest low power state.

To wakeup the SoC in such scenarios the earliest wakeup time is set in MPM
timer and the Resource Power Manager (RPM processor) takes care of setting
the timer in HW.

Add MPM timer programming when CPU cluster enters power collapse.

Signed-off-by: Sneh Mankad <sneh.mankad@xxxxxxxxxxxxxxxx>
---
drivers/irqchip/irq-qcom-mpm.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c
index 0de6ce818a506efc07913b16c479b5d5efb15108..676c45acd2716f474d474b1eb2feb61451404968 100644
--- a/drivers/irqchip/irq-qcom-mpm.c
+++ b/drivers/irqchip/irq-qcom-mpm.c
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
+#include <linux/ktime.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -23,6 +24,8 @@
#include <linux/soc/qcom/irq.h>
#include <linux/spinlock.h>

+#include <clocksource/arm_arch_timer.h>
+
/*
* This is the driver for Qualcomm MPM (MSM Power Manager) interrupt controller,
* which is commonly found on Qualcomm SoCs built on the RPM architecture.
@@ -82,6 +85,7 @@ struct mpm_gic_map {
};

struct qcom_mpm_priv {
+ struct device *dev;
void __iomem *base;
raw_spinlock_t lock;
struct mbox_client mbox_client;
@@ -312,6 +316,36 @@ static irqreturn_t qcom_mpm_handler(int irq, void *dev_id)
return ret;
}

+static void mpm_write_next_wakeup(struct qcom_mpm_priv *priv)
+{
+ ktime_t now, wakeup = KTIME_MAX;
+ u64 wakeup_us, wakeup_cycles = ~0;
+ u32 lo, hi;
+
+ /* Set highest time when system (timekeeping) is suspended */
+ if (system_state == SYSTEM_SUSPEND)
+ goto exit;
+
+ /* Find the relative wakeup in kernel time scale */
+ wakeup = dev_pm_genpd_get_next_hrtimer(priv->dev);
+
+ /* Find the relative wakeup in kernel time scale */
+ now = ktime_get();
+ wakeup = ktime_sub(wakeup, now);
+ wakeup_us = ktime_to_us(wakeup);
+
+ /* Convert the wakeup to arch timer scale */
+ wakeup_cycles = USECS_TO_CYCLES(wakeup_us);
+ wakeup_cycles += arch_timer_read_counter();
+
+exit:
+ lo = wakeup_cycles;
+ hi = wakeup_cycles >> 32;
+
+ qcom_mpm_write(priv, MPM_REG_TIMER, 0, lo);
+ qcom_mpm_write(priv, MPM_REG_TIMER, 1, hi);
+}
+
static int handle_rpm_notification(struct qcom_mpm_priv *priv)
{
int i, ret;
@@ -324,6 +358,7 @@ static int handle_rpm_notification(struct qcom_mpm_priv *priv)
if (ret < 0)
return ret;

+ mpm_write_next_wakeup(priv);
mbox_client_txdone(priv->mbox_chan, 0);
return 0;
}
@@ -399,6 +434,8 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare
if (!priv)
return -ENOMEM;

+ priv->dev = &pdev->dev;
+
ret = of_property_read_u32(np, "qcom,mpm-pin-count", &pin_cnt);
if (ret) {
dev_err(dev, "failed to read qcom,mpm-pin-count: %d\n", ret);

--
2.34.1