From 2a85d16de8bf7f331225e79a68e6e3016aa94a14 Mon Sep 17 00:00:00 2001 From: Ram Sargar Date: Thu, 10 Sep 2026 02:50:00 +0530 Subject: [PATCH 1/8] QCLINUX: arm64: dts: qcom: monaco: Add IPCC, syscon, and memory nodes for RTSS Mailbox Add ipcc1-4 mailbox nodes, widen the tcsr syscon's reg range to cover both TCSR request/response registers, and add the apss_shared_ipc syscon node for the Monaco platform. These back the syscon/mbox phandle references used by the Monaco RTSS mailbox node (qcom,rtss-mailbox). Reserve the memory regions used by the SAIL/RTSS subsystem for its TZ mailbox, shared-memory mailbox, and OTA buffer on Monaco. These regions back the memory-region references used by the Monaco RTSS mailbox node. Signed-off-by: Ram Sargar --- arch/arm64/boot/dts/qcom/monaco.dtsi | 58 +++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 3d1f3c647425e..e63c5e2848061 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -784,6 +784,21 @@ hwlocks = <&tcsr_mutex 3>; }; + tz_sail_mailbox_mem: tz-sail-mailbox@90c00000 { + reg = <0x0 0x90c00000 0x0 0x100000>; + no-map; + }; + + sail_mailbox_mem: sail-ss@90d00000 { + reg = <0x0 0x90d00000 0x0 0x100000>; + no-map; + }; + + sail_ota_mem: sail-ss@90e00000 { + reg = <0x0 0x90e00000 0x0 0x300000>; + no-map; + }; + gunyah_md_mem: gunyah-md-region@91a80000 { reg = <0x0 0x91a80000 0x0 0x80000>; no-map; @@ -959,6 +974,42 @@ #mbox-cells = <2>; }; + ipcc1: mailbox@488000 { + compatible = "qcom,qcs8300-ipcc", "qcom,ipcc"; + reg = <0x0 0x00488000 0x0 0x1000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + #mbox-cells = <2>; + }; + + ipcc2: mailbox@4a1000 { + compatible = "qcom,qcs8300-ipcc", "qcom,ipcc"; + reg = <0x0 0x004a1000 0x0 0x1000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + #mbox-cells = <2>; + }; + + ipcc3: mailbox@4a2000 { + compatible = "qcom,qcs8300-ipcc", "qcom,ipcc"; + reg = <0x0 0x004a2000 0x0 0x1000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + #mbox-cells = <2>; + }; + + ipcc4: mailbox@4a3000 { + compatible = "qcom,qcs8300-ipcc", "qcom,ipcc"; + reg = <0x0 0x004a3000 0x0 0x1000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + #mbox-cells = <2>; + }; + qfprom: efuse@784000 { compatible = "qcom,qcs8300-qfprom", "qcom,qfprom"; reg = <0x0 0x00784000 0x0 0x2410>; @@ -2809,7 +2860,7 @@ tcsr: syscon@1fc0000 { compatible = "qcom,qcs8300-tcsr", "syscon"; - reg = <0x0 0x1fc0000 0x0 0x30000>; + reg = <0x0 0x1fc0000 0x0 0x40000>; }; remoteproc_adsp: remoteproc@3000000 { @@ -7525,6 +7576,11 @@ #address-cells = <0>; }; + apsshm_shared_intu: syscon@17c00000 { + compatible = "qcom,qcs8300-apsshm-shared-intu", "syscon"; + reg = <0x0 0x17c00000 0x0 0x38>; + }; + watchdog@17c10000 { compatible = "qcom,apss-wdt-qcs8300", "qcom,kpss-wdt"; reg = <0x0 0x17c10000 0x0 0x1000>; From 81a348dfbfa17f7b133e58ee26a744d68a24adda Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Sat, 31 Oct 2026 00:33:14 +0000 Subject: [PATCH 2/8] Revert "FROMLIST: arm64: dts: qcom: shikra: Add CPU idle states" This reverts commit 3812298f2a34bcfb6ea1039462033e268573e476. This is v1 of the patch, which places MPM under cluster_pd using the v1 MPM binding. It is reverted together with the rest of the v1 MPM series, which is replaced with v3, and the v3 version of this patch is picked again on top. Link: https://github.com/qualcomm-linux/kernel/issues/1371 Signed-off-by: Ricardo Salveti --- arch/arm64/boot/dts/qcom/shikra.dtsi | 94 +--------------------------- 1 file changed, 1 insertion(+), 93 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi index ad76c37e6c4da..19e8d1afcc3ef 100644 --- a/arch/arm64/boot/dts/qcom/shikra.dtsi +++ b/arch/arm64/boot/dts/qcom/shikra.dtsi @@ -52,8 +52,6 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x0>; enable-method = "psci"; - power-domains = <&cpu_pd0>; - power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -72,8 +70,6 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x100>; enable-method = "psci"; - power-domains = <&cpu_pd1>; - power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -92,8 +88,6 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x200>; enable-method = "psci"; - power-domains = <&cpu_pd2>; - power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -112,8 +106,6 @@ compatible = "arm,cortex-a78c"; reg = <0x0 0x300>; enable-method = "psci"; - power-domains = <&cpu_pd3>; - power-domain-names = "psci"; next-level-cache = <&l2_3>; capacity-dmips-mhz = <1946>; dynamic-power-coefficient = <489>; @@ -157,61 +149,6 @@ }; }; - idle-states { - entry-method = "psci"; - - little_cpu_sleep_0: cpu-sleep-0-0 { - compatible = "arm,idle-state"; - idle-state-name = "little-power-down"; - arm,psci-suspend-param = <0x40000003>; - entry-latency-us = <549>; - exit-latency-us = <901>; - min-residency-us = <1774>; - local-timer-stop; - }; - - little_cpu_sleep_1: cpu-sleep-0-1 { - compatible = "arm,idle-state"; - idle-state-name = "little-pll-power-down"; - arm,psci-suspend-param = <0x40000004>; - entry-latency-us = <702>; - exit-latency-us = <915>; - min-residency-us = <4001>; - local-timer-stop; - }; - - big_cpu_sleep_0: cpu-sleep-1-0 { - compatible = "arm,idle-state"; - idle-state-name = "big-power-down"; - arm,psci-suspend-param = <0x40000003>; - entry-latency-us = <523>; - exit-latency-us = <1244>; - min-residency-us = <2207>; - local-timer-stop; - }; - - big_cpu_sleep_1: cpu-sleep-1-1 { - compatible = "arm,idle-state"; - idle-state-name = "big-pll-power-down"; - arm,psci-suspend-param = <0x40000004>; - entry-latency-us = <526>; - exit-latency-us = <1854>; - min-residency-us = <5555>; - local-timer-stop; - }; - }; - - domain_idle_states: domain-idle-states { - cluster_sleep_apss_off: cluster-sleep-0 { - compatible = "domain-idle-state"; - idle-state-name = "cluster-power-down"; - arm,psci-suspend-param = <0x41000044>; - entry-latency-us = <2752>; - exit-latency-us = <3038>; - min-residency-us = <6118>; - }; - }; - l3: l3-cache { compatible = "cache"; cache-level = <3>; @@ -380,35 +317,6 @@ psci: psci { compatible = "arm,psci-1.0"; method = "smc"; - - cpu_pd0: power-domain-cpu0 { - #power-domain-cells = <0>; - power-domains = <&cluster_pd>; - domain-idle-states = <&little_cpu_sleep_0 &little_cpu_sleep_1>; - }; - - cpu_pd1: power-domain-cpu1 { - #power-domain-cells = <0>; - power-domains = <&cluster_pd>; - domain-idle-states = <&little_cpu_sleep_0 &little_cpu_sleep_1>; - }; - - cpu_pd2: power-domain-cpu2 { - #power-domain-cells = <0>; - power-domains = <&cluster_pd>; - domain-idle-states = <&little_cpu_sleep_0 &little_cpu_sleep_1>; - }; - - cpu_pd3: power-domain-cpu3 { - #power-domain-cells = <0>; - power-domains = <&cluster_pd>; - domain-idle-states = <&big_cpu_sleep_0 &big_cpu_sleep_1>; - }; - - cluster_pd: power-domain-cluster { - #power-domain-cells = <0>; - domain-idle-states = <&cluster_sleep_apss_off>; - }; }; rpm: remoteproc { @@ -529,8 +437,8 @@ mboxes = <&apcs_glb 1>; interrupt-controller; #interrupt-cells = <2>; + #power-domain-cells = <0>; interrupt-parent = <&intc>; - power-domains = <&cluster_pd>; qcom,mpm-pin-count = <96>; qcom,mpm-pin-map = <2 275>, /* TSENS0 uplow */ <12 422>, /* DWC3 ss_phy_irq */ From a66b743307dd4d0a9802e35a1da9ab6625538a0f Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Sat, 31 Oct 2026 00:33:15 +0000 Subject: [PATCH 3/8] Revert "FROMLIST: irqchip/irq-qcom-mpm: Program wakeup timer when CPU cluster goes to LPM" This reverts commit 93ac4522f5dd7ad35221bbaeca2ec7f0368d33f4. v3 of the MPM series drops the wakeup timer programming for now, until MPM can be registered under the CPU cluster power domain without blocking its idle states. Revert it together with the rest of the v1 series. Link: https://github.com/qualcomm-linux/kernel/issues/1371 Signed-off-by: Ricardo Salveti --- drivers/irqchip/irq-qcom-mpm.c | 44 ---------------------------------- 1 file changed, 44 deletions(-) diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index 2d3460e2aa659..7b27c78419d0d 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -26,8 +25,6 @@ #include #include -#include - /* * This is the driver for Qualcomm MPM (MSM Power Manager) interrupt controller, * which is commonly found on Qualcomm SoCs built on the RPM architecture. @@ -80,13 +77,6 @@ enum qcom_mpm_reg { MPM_REG_STATUS, }; -#define USECS_TO_CYCLES(time_usecs) xloops_to_cycles((time_usecs) * 0x10C7UL) - -static inline unsigned long xloops_to_cycles(u64 xloops) -{ - return (xloops * loops_per_jiffy * HZ) >> 32; -} - /* MPM pin map to GIC hwirq */ struct mpm_gic_map { int pin; @@ -94,7 +84,6 @@ struct mpm_gic_map { }; struct qcom_mpm_priv { - struct device *dev; void __iomem *base; raw_spinlock_t lock; struct mbox_client mbox_client; @@ -331,36 +320,6 @@ 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; @@ -373,7 +332,6 @@ 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; } @@ -454,8 +412,6 @@ 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); From d9fd7e2d531aae23c4c042d296650ad4eecc2f38 Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Sat, 31 Oct 2026 00:33:15 +0000 Subject: [PATCH 4/8] Revert "FROMLIST: irqchip/irq-qcom-mpm: Prepare common access path for timer and pin regs" This reverts commit d403cded7f4bb7d9b4e2424e7e327aa3ac11e28d. This only prepares the wakeup timer programming, which v3 of the MPM series drops for now. Revert it together with the rest of the v1 series. Link: https://github.com/qualcomm-linux/kernel/issues/1371 Signed-off-by: Ricardo Salveti --- drivers/irqchip/irq-qcom-mpm.c | 48 +++++++++------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index 7b27c78419d0d..c099304d5af13 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -66,16 +66,11 @@ * */ -#define MPM_TIMER_REGS 2 - -enum qcom_mpm_reg { - MPM_REG_TIMER = 0, - MPM_REG_ENABLE, - MPM_REG_FALLING_EDGE, - MPM_REG_RISING_EDGE, - MPM_REG_POLARITY, - MPM_REG_STATUS, -}; +#define MPM_REG_ENABLE 0 +#define MPM_REG_FALLING_EDGE 1 +#define MPM_REG_RISING_EDGE 2 +#define MPM_REG_POLARITY 3 +#define MPM_REG_STATUS 4 /* MPM pin map to GIC hwirq */ struct mpm_gic_map { @@ -97,36 +92,18 @@ struct qcom_mpm_priv { atomic_t cpus_in_pm; }; -static unsigned int qcom_mpm_offset(struct qcom_mpm_priv *priv, enum qcom_mpm_reg reg, - unsigned int index) -{ - unsigned int reg_offset; - - /* - * Per the vMPM register map, TIMER[0..1] starts at register index 0 and all pin-specific - * registers start after the two TIMER regs. Pin-specific register IDs start at - * MPM_REG_ENABLE, so subtract it to convert to a zero-based pin-register group index. - */ - if (reg == MPM_REG_TIMER) - reg_offset = index; - else - reg_offset = MPM_TIMER_REGS + - (reg - MPM_REG_ENABLE) * priv->reg_stride + index; - - return reg_offset * sizeof(u32); -} - -static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, enum qcom_mpm_reg reg, unsigned int index) +static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, unsigned int reg, + unsigned int index) { - unsigned int offset = qcom_mpm_offset(priv, reg, index); + unsigned int offset = (reg * priv->reg_stride + index + 2) * 4; return readl_relaxed(priv->base + offset); } -static void qcom_mpm_write(struct qcom_mpm_priv *priv, enum qcom_mpm_reg reg, +static void qcom_mpm_write(struct qcom_mpm_priv *priv, unsigned int reg, unsigned int index, u32 val) { - unsigned int offset = qcom_mpm_offset(priv, reg, index); + unsigned int offset = (reg * priv->reg_stride + index + 2) * 4; writel_relaxed(val, priv->base + offset); @@ -167,7 +144,7 @@ static void qcom_mpm_unmask(struct irq_data *d) irq_chip_unmask_parent(d); } -static void mpm_set_type(struct qcom_mpm_priv *priv, bool set, enum qcom_mpm_reg reg, +static void mpm_set_type(struct qcom_mpm_priv *priv, bool set, unsigned int reg, unsigned int index, unsigned int shift) { unsigned long flags, val; @@ -350,7 +327,8 @@ static int mpm_pd_power_cb(struct notifier_block *nb, unsigned long action, void return NOTIFY_OK; } -static int mpm_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) +static int mpm_cpu_pm_callback(struct notifier_block *nfb, + unsigned long action, void *v) { struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); int cpus_in_pm; From d8fb59bb062a6a2764dcc9c8c8f7c79600fca214 Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Sat, 31 Oct 2026 00:33:15 +0000 Subject: [PATCH 5/8] Revert "FROMLIST: irqchip/irq-qcom-mpm: Register MPM under CPU cluster power domain" This reverts commit 98374f5f5681c7a03f07b24d3026d7d4dfcbf883. This is v1 of the patch. It stops registering MPM as a genpd provider and expects the DT to place MPM under the CPU cluster power domain instead, but only shikra.dtsi was converted, as v1 5/7 (sm6375) and 6/7 (agatti) were not picked. On Agatti boards that keep cluster_pd, such as the Arduino UNO Q, cluster_pd still uses <&mpm> as its parent domain, so the PSCI domain driver defers forever: CPUidle PSCI: failed to create CPU PM domains ret=-517 and the board never reaches a login prompt. The QRB2210 RB1 is not affected only because qrb2210-rb1.dts deletes cluster_pd. The series was reworked upstream. v3 keeps the genpd provider when the MPM node has #power-domain-cells and uses CPU PM notifications otherwise, so existing DTs keep working, and it is picked in place of v1. Link: https://github.com/qualcomm-linux/kernel/issues/1371 Signed-off-by: Ricardo Salveti --- drivers/irqchip/irq-qcom-mpm.c | 97 ++++++++++------------------------ 1 file changed, 28 insertions(+), 69 deletions(-) diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index c099304d5af13..8c575d7ce8a17 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -4,8 +4,6 @@ * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ -#include -#include #include #include #include @@ -20,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -87,9 +84,7 @@ struct qcom_mpm_priv { unsigned int map_cnt; unsigned int reg_stride; struct irq_domain *domain; - struct notifier_block genpd_nb; - struct notifier_block mpm_pm; - atomic_t cpus_in_pm; + struct generic_pm_domain genpd; }; static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, unsigned int reg, @@ -297,8 +292,10 @@ static irqreturn_t qcom_mpm_handler(int irq, void *dev_id) return ret; } -static int handle_rpm_notification(struct qcom_mpm_priv *priv) +static int mpm_pd_power_off(struct generic_pm_domain *genpd) { + struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, + genpd); int i, ret; for (i = 0; i < priv->reg_stride; i++) @@ -310,57 +307,8 @@ static int handle_rpm_notification(struct qcom_mpm_priv *priv) return ret; mbox_client_txdone(priv->mbox_chan, 0); - return 0; -} - -static int mpm_pd_power_cb(struct notifier_block *nb, unsigned long action, void *d) -{ - struct qcom_mpm_priv *priv = container_of(nb, struct qcom_mpm_priv, - genpd_nb); - - switch (action) { - case GENPD_NOTIFY_PRE_OFF: - if (handle_rpm_notification(priv)) - return NOTIFY_BAD; - } - - return NOTIFY_OK; -} -static int mpm_cpu_pm_callback(struct notifier_block *nfb, - unsigned long action, void *v) -{ - struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); - int cpus_in_pm; - - switch (action) { - case CPU_PM_ENTER: - cpus_in_pm = atomic_inc_return(&priv->cpus_in_pm); - /* - * NOTE: comments for num_online_cpus() point out that it's - * only a snapshot so we need to be careful. It should be OK - * for us to use, though. It's important for us not to miss - * if we're the last CPU going down so it would only be a - * problem if a CPU went offline right after we did the check - * AND that CPU was not idle AND that CPU was the last non-idle - * CPU. That can't happen. CPUs would have to come out of idle - * before the CPU could go offline. - */ - if (cpus_in_pm < num_online_cpus()) - return NOTIFY_OK; - break; - case CPU_PM_ENTER_FAILED: - case CPU_PM_EXIT: - atomic_dec(&priv->cpus_in_pm); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - - if (handle_rpm_notification(priv)) - return NOTIFY_BAD; - - return NOTIFY_OK; + return 0; } static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq) @@ -379,6 +327,7 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct irq_domain *parent_domain; + struct generic_pm_domain *genpd; struct device_node *msgram_np; struct qcom_mpm_priv *priv; unsigned int pin_cnt; @@ -466,6 +415,26 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare if (irq < 0) return irq; + genpd = &priv->genpd; + genpd->flags = GENPD_FLAG_IRQ_SAFE; + genpd->power_off = mpm_pd_power_off; + + genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); + if (!genpd->name) + return -ENOMEM; + + ret = pm_genpd_init(genpd, NULL, false); + if (ret) { + dev_err(dev, "failed to init genpd: %d\n", ret); + return ret; + } + + ret = of_genpd_add_provider_simple(np, genpd); + if (ret) { + dev_err(dev, "failed to add genpd provider: %d\n", ret); + goto remove_genpd; + } + priv->mbox_client.dev = dev; priv->mbox_client.knows_txdone = true; priv->mbox_chan = mbox_request_channel(&priv->mbox_client, 0); @@ -498,24 +467,14 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare if (ret) goto remove_domain; - if (of_find_property(np, "power-domains", NULL)) { - devm_pm_runtime_enable(dev); - priv->genpd_nb.notifier_call = mpm_pd_power_cb; - ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); - } else { - priv->mpm_pm.notifier_call = mpm_cpu_pm_callback; - ret = cpu_pm_register_notifier(&priv->mpm_pm); - } - - if (ret) - goto remove_domain; - return 0; remove_domain: irq_domain_remove(priv->domain); free_mbox: mbox_free_channel(priv->mbox_chan); +remove_genpd: + pm_genpd_remove(genpd); return ret; } From 08f99198010af6efae34dd1c97e8922a11979569 Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Sat, 31 Oct 2026 00:33:15 +0000 Subject: [PATCH 6/8] Revert "FROMLIST: dt-bindings: interrupt-controller: mpm: Document power-domains property" This reverts commit b3120eabda5403111990b96d2b4a8f546422dbb5. v3 of the MPM series keeps the existing binding: #power-domain-cells stays optional and Shikra simply leaves it out, so the binding change is no longer needed. Replacing #power-domain-cells also breaks the DT ABI for existing users, which was rejected in the upstream review of v1. Link: https://github.com/qualcomm-linux/kernel/issues/1371 Signed-off-by: Ricardo Salveti --- .../devicetree/bindings/interrupt-controller/qcom,mpm.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,mpm.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,mpm.yaml index 3f9645fbc3c20..ebb40c48950ab 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qcom,mpm.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,mpm.yaml @@ -68,8 +68,8 @@ properties: - description: MPM pin number - description: GIC SPI number for the MPM pin - power-domains: - maxItems: 1 + '#power-domain-cells': + const: 0 required: - compatible @@ -113,6 +113,6 @@ examples: <24 79>, <86 183>, <91 260>; - power-domains = <&cluster_pd>; + #power-domain-cells = <0>; }; }; From 72a7af82e52909dfa38a956ac7732defa515a65a Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Tue, 27 Oct 2026 18:41:09 +0530 Subject: [PATCH 7/8] FROMLIST: irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification MPM irqchip needs to notify RPM (Resource Power Manager) processor to read the latest wake up capable interrupts when the CPU cluster is entering the deepest idle state. This is done by sending IPC interrupt to RPM and is implemented as .power_off() callback by registering MPM as parent power domain to CPU cluster. Such implementation earlier introduced a hard probe dependency between MPM irqchip and CPU cluster power domains. That is MPM irqchip needs to finish probe before PSCI power domains are probed. The above problem has since been resolved after bd7f488a8e5d ("cpuidle: psci: Fix support for probe deferral by dropping the faux device") / [1]. However, it makes sense to remodel the RPM notification in MPM irqchip due to general consistency. MPM is not really a power domain provider, all it needs is a notification when CPU cluster is going to power collapse to carry out the RPM notification. We can register for CPU_PM notifications for that purpose. This way we can avoid such unrelated dependencies. We cannot register MPM under CPU cluster power domain and rely on GENPD notifications here, since that way cluster low power modes will be blocked whenever some consumer of MPM, and in turn MPM itself, are runtime active. This will add another unnecessary dependency for CPU cluster low power modes. For now as first step, register with cpu_pm as a fallback to manage RPM notification. Trigger the RPM notification when the last CPU in CPU cluster goes to power collapse. [1] https://lore.kernel.org/all/20260909134240.685115374@linuxfoundation.org Signed-off-by: Sneh Mankad Link: https://lore.kernel.org/all/20261027-b4-shikra_lpm_addition-v3-1-424abcdcf824@oss.qualcomm.com Signed-off-by: Ricardo Salveti --- drivers/irqchip/irq-qcom-mpm.c | 104 ++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index 8c575d7ce8a17..b6caea90e9f41 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -4,6 +4,7 @@ * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -85,6 +86,8 @@ struct qcom_mpm_priv { unsigned int reg_stride; struct irq_domain *domain; struct generic_pm_domain genpd; + struct notifier_block mpm_pm; + atomic_t cpus_in_pm; }; static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, unsigned int reg, @@ -292,10 +295,8 @@ static irqreturn_t qcom_mpm_handler(int irq, void *dev_id) return ret; } -static int mpm_pd_power_off(struct generic_pm_domain *genpd) +static int handle_rpm_notification(struct qcom_mpm_priv *priv) { - struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, - genpd); int i, ret; for (i = 0; i < priv->reg_stride; i++) @@ -307,10 +308,52 @@ static int mpm_pd_power_off(struct generic_pm_domain *genpd) return ret; mbox_client_txdone(priv->mbox_chan, 0); - return 0; } +static int mpm_pd_power_off(struct generic_pm_domain *genpd) +{ + struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, + genpd); + + return handle_rpm_notification(priv); +} + +static int mpm_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) +{ + struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); + int cpus_in_pm; + + switch (action) { + case CPU_PM_ENTER: + cpus_in_pm = atomic_inc_return(&priv->cpus_in_pm); + /* + * NOTE: comments for num_online_cpus() point out that it's + * only a snapshot so we need to be careful. It should be OK + * for us to use, though. It's important for us not to miss + * if we're the last CPU going down so it would only be a + * problem if a CPU went offline right after we did the check + * AND that CPU was not idle AND that CPU was the last non-idle + * CPU. That can't happen. CPUs would have to come out of idle + * before the CPU could go offline. + */ + if (cpus_in_pm < num_online_cpus()) + return NOTIFY_OK; + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + atomic_dec(&priv->cpus_in_pm); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } + + if (handle_rpm_notification(priv)) + return NOTIFY_BAD; + + return NOTIFY_OK; +} + static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq) { int i; @@ -415,26 +458,6 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare if (irq < 0) return irq; - genpd = &priv->genpd; - genpd->flags = GENPD_FLAG_IRQ_SAFE; - genpd->power_off = mpm_pd_power_off; - - genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); - if (!genpd->name) - return -ENOMEM; - - ret = pm_genpd_init(genpd, NULL, false); - if (ret) { - dev_err(dev, "failed to init genpd: %d\n", ret); - return ret; - } - - ret = of_genpd_add_provider_simple(np, genpd); - if (ret) { - dev_err(dev, "failed to add genpd provider: %d\n", ret); - goto remove_genpd; - } - priv->mbox_client.dev = dev; priv->mbox_client.knows_txdone = true; priv->mbox_chan = mbox_request_channel(&priv->mbox_client, 0); @@ -444,6 +467,35 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare return ret; } + if (of_find_property(np, "#power-domain-cells", NULL)) { + genpd = &priv->genpd; + genpd->flags = GENPD_FLAG_IRQ_SAFE; + genpd->power_off = mpm_pd_power_off; + + genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); + if (!genpd->name) + return -ENOMEM; + + ret = pm_genpd_init(genpd, NULL, false); + if (ret) { + dev_err(dev, "failed to init genpd: %d\n", ret); + return ret; + } + + ret = of_genpd_add_provider_simple(np, genpd); + if (ret) { + dev_err(dev, "failed to add genpd provider: %d\n", ret); + goto remove_genpd; + } + } else { + priv->mpm_pm.notifier_call = mpm_cpu_pm_callback; + ret = cpu_pm_register_notifier(&priv->mpm_pm); + if (ret) { + dev_err(dev, "failed to register for CPU_PM notifications: %d\n", ret); + return ret; + } + } + parent_domain = irq_find_host(parent); if (!parent_domain) { dev_err(dev, "failed to find MPM parent domain\n"); @@ -471,10 +523,10 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare remove_domain: irq_domain_remove(priv->domain); -free_mbox: - mbox_free_channel(priv->mbox_chan); remove_genpd: pm_genpd_remove(genpd); +free_mbox: + mbox_free_channel(priv->mbox_chan); return ret; } From 3d71e56eeb0d5ac2974266dcb7ec673ef0da4706 Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Tue, 27 Oct 2026 18:41:10 +0530 Subject: [PATCH 8/8] FROMLIST: arm64: dts: qcom: shikra: Add CPU idle states Add idle states for the CPUs as well as the whole cluster. This enables deeper-than-WFI cpuidle. Remove #power-domain-cells from MPM device so that the driver follows the cpu_pm notifications path to handle RPM notification. This avoids other probe dependencies that arise due to MPM being a power domain provider. Signed-off-by: Sneh Mankad Link: https://lore.kernel.org/all/20261027-b4-shikra_lpm_addition-v3-2-424abcdcf824@oss.qualcomm.com Signed-off-by: Ricardo Salveti --- arch/arm64/boot/dts/qcom/shikra.dtsi | 93 +++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi index 19e8d1afcc3ef..1db81afaaa034 100644 --- a/arch/arm64/boot/dts/qcom/shikra.dtsi +++ b/arch/arm64/boot/dts/qcom/shikra.dtsi @@ -52,6 +52,8 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x0>; enable-method = "psci"; + power-domains = <&cpu_pd0>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -70,6 +72,8 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x100>; enable-method = "psci"; + power-domains = <&cpu_pd1>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -88,6 +92,8 @@ compatible = "arm,cortex-a55"; reg = <0x0 0x200>; enable-method = "psci"; + power-domains = <&cpu_pd2>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -106,6 +112,8 @@ compatible = "arm,cortex-a78c"; reg = <0x0 0x300>; enable-method = "psci"; + power-domains = <&cpu_pd3>; + power-domain-names = "psci"; next-level-cache = <&l2_3>; capacity-dmips-mhz = <1946>; dynamic-power-coefficient = <489>; @@ -149,6 +157,61 @@ }; }; + idle-states { + entry-method = "psci"; + + little_cpu_sleep_0: cpu-sleep-0-0 { + compatible = "arm,idle-state"; + idle-state-name = "little-power-down"; + arm,psci-suspend-param = <0x40000003>; + entry-latency-us = <549>; + exit-latency-us = <901>; + min-residency-us = <1774>; + local-timer-stop; + }; + + little_cpu_sleep_1: cpu-sleep-0-1 { + compatible = "arm,idle-state"; + idle-state-name = "little-pll-power-down"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <702>; + exit-latency-us = <915>; + min-residency-us = <4001>; + local-timer-stop; + }; + + big_cpu_sleep_0: cpu-sleep-1-0 { + compatible = "arm,idle-state"; + idle-state-name = "big-power-down"; + arm,psci-suspend-param = <0x40000003>; + entry-latency-us = <523>; + exit-latency-us = <1244>; + min-residency-us = <2207>; + local-timer-stop; + }; + + big_cpu_sleep_1: cpu-sleep-1-1 { + compatible = "arm,idle-state"; + idle-state-name = "big-pll-power-down"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <526>; + exit-latency-us = <1854>; + min-residency-us = <5555>; + local-timer-stop; + }; + }; + + domain_idle_states: domain-idle-states { + cluster_sleep_apss_off: cluster-sleep-0 { + compatible = "domain-idle-state"; + idle-state-name = "cluster-power-down"; + arm,psci-suspend-param = <0x41000044>; + entry-latency-us = <2752>; + exit-latency-us = <3038>; + min-residency-us = <6118>; + }; + }; + l3: l3-cache { compatible = "cache"; cache-level = <3>; @@ -317,6 +380,35 @@ psci: psci { compatible = "arm,psci-1.0"; method = "smc"; + + cpu_pd0: power-domain-cpu0 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd1: power-domain-cpu1 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd2: power-domain-cpu2 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd3: power-domain-cpu3 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&big_cpu_sleep_0>, <&big_cpu_sleep_1>; + }; + + cluster_pd: power-domain-cluster { + #power-domain-cells = <0>; + domain-idle-states = <&cluster_sleep_apss_off>; + }; }; rpm: remoteproc { @@ -437,7 +529,6 @@ mboxes = <&apcs_glb 1>; interrupt-controller; #interrupt-cells = <2>; - #power-domain-cells = <0>; interrupt-parent = <&intc>; qcom,mpm-pin-count = <96>; qcom,mpm-pin-map = <2 275>, /* TSENS0 uplow */