[PATCH v5] arm64: dts: qcom: x1e80100: Add deepest idle state

From: Maulik Shah

Date: Wed Jul 15 2026 - 09:06:08 EST


CPU subsystem deepest idle state can lead SoC to low power mode like CxPC
(chip rail power collapse). SoC deepest low power mode requires the PDC
interrupt controller to wake up the SoC as the GIC interrupt controller
is on Cx (chip rail) and will not be functional to wake up the CPU.

PDC monitors both direct SPI interrupts and GPIOs used as interrupts
in SoC low power mode. Further all wake up capable interrupts gets routed
via PDC to GIC when SoC is out of low power mode.

The wake up capable GPIOs used as interrupts were by passing PDC on x1e
with commit 602cb14e310a ("pinctrl: qcom: x1e80100: Bypass PDC wakeup
parent for now") as a result CPU deepest idle state was not added so
far.

The blocker for enabling deepest idle state was GPIO IRQ wakeup support.
The x1e80100 PDC can operate in two modes: pass-through mode (GPIO
IRQs forwarded directly to GIC without latching at PDC) and secondary
controller mode (PDC latches GPIO IRQs and sends them as level IRQs to
GIC).

All the SoCs on Linux so far worked in pass-through mode of PDC.
On some x1e boards the firmware configures the PDC in secondary controller
mode, which caused GPIO interrupt storms as the level latched at PDC gets
never cleared.

The secondary mode is originally a requirement for windows SW platforms
(which could not work with all types of interrupts and require level
interrupts for GPIOs). Initially shipped windows firmware did not have
SCM interface to configure PDC back to pass-through mode if with such
firmware Linux is booted up.

PDC irqchip has got secondary mode and SCM interface support to configure
PDC irqchip mode to pass-through mode on newer firmware where SCM interface
is available and for older firmware on which SCM interface is not present
PDC irqchip works in secondary irqchip mode [1]. Above mentioned commit is
also reverted via [1].

With PDC now working on older firmware as well add the CPU deepest idle
state domain_ss3.

The newly added domain_ss3 state has entry/exit latencies of 2500 us.
The shallower cluster_cl5 state had entry/exit latencies of 2200/4000 us
which are higher than the deeper state. This create an inconsistency that
would confuse the idle governor. Correct cluster_cl5 latencies to 2000 us
each to match production configuration values.

[1] https://lore.kernel.org/linux-arm-msm/20260707-hamoa_pdc_v3-v4-0-dfd1f4a3ae89@xxxxxxxxxxxxxxxx/#t

Tested-by: Daniel J Blueman <daniel@xxxxxxxxx>
Signed-off-by: Maulik Shah <maulik.shah@xxxxxxxxxxxxxxxx>
---
There are two modes PDC irqchip can work in
- pass through mode
- secondary controller mode

Secondary mode is supported depending on SoC using PDC HW Version v3.0
or higher.

+------------------------------------------------------------------------+
| SoC | SM8350, SM8450 | SM8550, Hamoa | SM8650, SM8750 |
|----------------------------------------------------------- ------------|
| Version | v2.7 | v3.0 | v3.2 |
|------------------------------------------------------------------------|
| Pass through | Yes | Yes | Yes |
|------------------------------------------------------------------------|
| Secondary | No | Yes | Yes |
+------------------------------------------------------------------------+

All PDC irqchip supports pass through mode in which both Direct SPIs and
GPIO IRQs (as SPIs) are sent to GIC without latching at PDC, PDC only does
inversion when needed for falling edge to rising edge or level low to level
high, as the GIC do not support falling edge/level low interrupts.

Newer PDCs (v3.0 onwards) also support additional secondary controller mode
where PDC latches GPIO IRQs and sends to GIC as level type IRQ. Direct SPIs
still works same as pass through mode without latching at PDC even in
secondary controller mode.

All the SoCs defaulted to pass through mode with the exception of some x1e.

x1e PDC may be set to secondary controller mode for builds on CRD boards
whereas it may be set to pass through mode for IoT-EVK boards. The mode
configuration is done in firmware and initially shipped windows firmware
did not have SCM interface to read or modify the PDC configuration.
Later only write access is opened up for non secure world.

Using the write access available add changes to modify the PDC mode to
pass through mode via SCM write. When the write fails (on older firmware)
assume to work in secondary mode.

As the deepest idle state as the PDC can now wake up SoC from GPIOs and
revert commit 602cb14e310a ("pinctrl: qcom: x1e80100: Bypass PDC wakeup
parent for now").

The series has been tested on x1e80100 CRD with both old and new firmware
and also on kaanapali. Test conducted with tlmm-test module after
applying [3] as test module needed to be fixed first.

All 17/17 passes in pass through mode and 16/17 passes in secondary mode.
Failing test tlmm_test_rising_while_disabled seems to be because when in
irq disabled state PDC is not latching the edge interrupt.

Test #1: Pass through mode on x1e80100 CRD (New firmware)

root@qcom-armv8a:~# insmod tlmm-test.ko gpio=91

KTAP version 1
1..1
KTAP version 1
# Subtest: tlmm-test
# module: tlmm_test
1..17
ok 1 tlmm_test_silent_rising
ok 2 tlmm_test_silent_falling
ok 3 tlmm_test_silent_low
ok 4 tlmm_test_silent_high
ok 5 tlmm_test_rising
ok 6 tlmm_test_falling
ok 7 tlmm_test_high
ok 8 tlmm_test_low
ok 9 tlmm_test_rising_in_handler
ok 10 tlmm_test_falling_in_handler
ok 11 tlmm_test_thread_rising
ok 12 tlmm_test_thread_falling
ok 13 tlmm_test_thread_high
ok 14 tlmm_test_thread_low
ok 15 tlmm_test_thread_rising_in_handler
ok 16 tlmm_test_thread_falling_in_handler
ok 17 tlmm_test_rising_while_disabled

ok 1 tlmm-test

Test #2: Secondary mode on x1e80100 CRD (Old firmware)

root@qcom-armv8a:~# insmod tlmm-test.ko gpio=91

KTAP version 1
1..1
    KTAP version 1
    # Subtest: tlmm-test
    # module: tlmm_test
    1..17
    ok 1 tlmm_test_silent_rising
    ok 2 tlmm_test_silent_falling
    ok 3 tlmm_test_silent_low
    ok 4 tlmm_test_silent_high
    ok 5 tlmm_test_rising
    ok 6 tlmm_test_falling
    ok 7 tlmm_test_high
    ok 8 tlmm_test_low
    ok 9 tlmm_test_rising_in_handler
    ok 10 tlmm_test_falling_in_handler
    ok 11 tlmm_test_thread_rising
    ok 12 tlmm_test_thread_falling
    ok 13 tlmm_test_thread_high
    ok 14 tlmm_test_thread_low
    ok 15 tlmm_test_thread_rising_in_handler
    ok 16 tlmm_test_thread_falling_in_handler
    # tlmm_test_rising_while_disabled: ASSERTION FAILED at drivers/pinctrl/qcom/tlmm-test.c:545
    Expected atomic_read(&priv->intr_count) == 1, but
        atomic_read(&priv->intr_count) == 0 (0x0)
    not ok 17 tlmm_test_rising_while_disabled

not ok 1 tlmm-test
root@qcom-armv8a:~#

v2 series is dependent on [1] as mostly all changes are already reviewed.
v3 series is dependent on [2] which is already merged in linux-next

[1] https://lore.kernel.org/linux-arm-msm/20260410184124.1068210-1-mukesh.ojha@xxxxxxxxxxxxxxxx/
[2] https://lore.kernel.org/linux-arm-msm/20260527095426.2324504-1-mukesh.ojha@xxxxxxxxxxxxxxxx/
[3] https://lore.kernel.org/linux-arm-msm/20260529-tlmm_test_changes-v1-0-88bfdccb4369@xxxxxxxxxxxxxxxx/
---
Changes in v5:
- Drop already merged v4 changes 1 to 6
- Add detailed commit message for adding deepest idle state
- Add Tested-by: Daniel J Blueman <daniel@xxxxxxxxx>
- Link to v4: https://patch.msgid.link/20260707-hamoa_pdc_v3-v4-0-dfd1f4a3ae89@xxxxxxxxxxxxxxxx

Changes in v4:
- pdc_setup_pin_mapping() use dev argument and use dev->of_node within
- Merge v3 patch 2 and 3 into single change
- guard pdc->lock for all pdc->enable_intr() function call for HW v2.7,3.0
- Move lock init before pdc_setup_pin_mapping() for bad spinlock on v3
- Removed unused num_gpios
- Add comment in qcom_pdc_gic_secondary_set_type() from v2
- Modify primary case in qcom_pdc_alloc()
- Remove pdc->version checks from pdc_unmask/clear_gpio_cfg()
- Formatting and commit text updates
- Link to v3: https://patch.msgid.link/20260616-hamoa_pdc_v3-v3-0-4d8e1504ea75@xxxxxxxxxxxxxxxx

Changes in v3:
- Add test results in cover letter with tlmm-test module
- Fix coding style and struct defination
- Convert raw_spin_lock to guard(raw_spin_lock) and remove _irqsave
- Use bit number instead of GENMASK() for single bit fields
- Use __assign_bit() and __clear_bit() APIs for single bit modifications
- Use devm_ioremap() instead of ioremap()
- Use devm_kcalloc() instead of kzalloc_objs()
- Add separate irq chips for pass through and secondary mode IRQs
- Add IRQCHIP_EOI_THREADED flag for threaded IRQ on pinctrl irqchip
- Link to v2: https://patch.msgid.link/20260526-hamoa_pdc-v2-0-f6857af1ce91@xxxxxxxxxxxxxxxx

Changes in v2:
- Update to mention SoC names along with PDC versions in cover letter
- Drop devicetree change to remove scm interconnects
- Use qcom_scm_is_available() to wait for dependency on SCM
- Drop binding change mentioning qcom,qmp and PDC config reg
- Restructure version support and move all statics to struct pdc_desc
- Remove pdc_enable_intr() wrapper
- Differentiate direct SPI and GPIOs as SPI using PDC IRQ PARAM reg
- Add changes to make PDC work in secondary controller mode
- Rework and include Stephan's change to invoke irq_ack() for edge interrupt
- Mention dependency via b4 prerequisites and cover letter
- Link to v1: https://lore.kernel.org/r/20260312-hamoa_pdc-v1-0-760c8593ce50@xxxxxxxxxxxxxxxx
---
arch/arm64/boot/dts/qcom/hamoa.dtsi | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi
index 09527dcf9576..f01eccb67ee2 100644
--- a/arch/arm64/boot/dts/qcom/hamoa.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi
@@ -300,10 +300,18 @@ cluster_cl4: cluster-sleep-0 {
cluster_cl5: cluster-sleep-1 {
compatible = "domain-idle-state";
arm,psci-suspend-param = <0x01000054>;
- entry-latency-us = <2200>;
- exit-latency-us = <4000>;
+ entry-latency-us = <2000>;
+ exit-latency-us = <2000>;
min-residency-us = <7000>;
};
+
+ domain_ss3: domain-sleep-0 {
+ compatible = "domain-idle-state";
+ arm,psci-suspend-param = <0x0200c354>;
+ entry-latency-us = <2500>;
+ exit-latency-us = <2500>;
+ min-residency-us = <9000>;
+ };
};
};

@@ -462,7 +470,7 @@ cluster_pd2: power-domain-cpu-cluster2 {

system_pd: power-domain-system {
#power-domain-cells = <0>;
- /* TODO: system-wide idle states */
+ domain-idle-states = <&domain_ss3>;
};
};


---
base-commit: cc2b5f627e8ccbae1188ef2d8be3e451d7f933a5
change-id: 20260605-hamoa_pdc_v3-6dabc845021c

Best regards,
--
Maulik Shah <maulik.shah@xxxxxxxxxxxxxxxx>