Skip to content

Port ROCKNIX deep-sleep and charger-wake kernel fixes - #1

Open
gh123man wants to merge 11 commits into
mainfrom
codex/rocknix-deep-sleep-kernel
Open

Port ROCKNIX deep-sleep and charger-wake kernel fixes#1
gh123man wants to merge 11 commits into
mainfrom
codex/rocknix-deep-sleep-kernel

Conversation

@gh123man

@gh123man gh123man commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • port the SM8550 suspend/resume fixes from SM8550 deep suspend/resume ROCKNIX/distribution#2954, pinned to PR head aa7d8320421bd44ec5b46b2d852b544fec237c54
  • retain Armada's existing equivalent QCOM IPCC wake patch
  • replace the resume-only rsinput workaround with the complete MCU suspend/resume implementation
  • defer the rsinput-to-QCOM haptics playback bridge to process context so force feedback cannot sleep while the input event lock is held
  • recover software clock-gating Hibern8-enter timeouts inline and disable runtime UFS clock gating for the remainder of that boot after a recovered failure
  • record failure-only UFS gate-race snapshots
  • prevent the captured UFS race by holding runtime clock gating across the existing system-PM prepare/complete window
  • propagate selected QCOM IPCC child wake configuration to the shared parent IRQ
  • replace unconditional GLINK IRQF_NO_SUSPEND handling with opt-in device-tree wake IRQs
  • mark the RP6 ADSP PMIC GLINK edge as wake-capable for charger attach
  • enable the standard /sys/power/pm_wakeup_irq interface used by Armada's userspace wake classifier
  • use Podman for carrier-image creation and make the kernel build entrypoint portable

Why

Armada already has a real-suspend path, but its Linux 7.0.11 patch series lacked the UFS, TSENS, GENI UART, controller lifecycle, haptics, and charger-wake fixes needed for reliable SM8550 deep sleep. The UFS interrupt hunk is rebased onto Armada's 7.0.11 base; imported changes preserve their ROCKNIX provenance, while Armada-authored patches are attributed to gh123man <gh123man@users.noreply.github.com>.

Hardware testing exposed three additional gaps:

  1. The rsinput force-feedback callback runs below input_event() with the input event spinlock held and interrupts disabled. Calling the QCOM haptics erase/playback callbacks directly from there can sleep and produced atomic-context failures. Playback is now recorded atomically and applied by a workqueue.
  2. ufshcd_gate_work() only restored its software state when DME_HIBER_ENTER timed out. During system PM, pm_op_in_progress suppresses generic fatal error handling, so the UFS link remained unusable and a later suspend could hang in filesystem sync after the screen had blanked. The failure is now recovered inline using the existing PM relink completion poller. After successful recovery, runtime UFS clock gating is pinned off for the rest of that boot as a circuit breaker; system deep sleep remains enabled.
  3. Failure-only diagnostics captured the ordering behind that timeout. The gate worker was admitted and entered DME_HIBER_ENTER before system PM took its hold. The root fix retains a clock-gating hold across the existing system-PM prepare/complete window, draining any pending gate worker while normal completion handling is available and preventing a new idle Hibern8 command during device suspend/resume callbacks.

The inline recovery and per-boot clock-gating circuit breaker remain as a storage-safety fallback; disabling clock gating is no longer the primary fix.

Charger wake architecture

On SM8550, charger and Type-C events are decoded by ADSP firmware and delivered through PMIC GLINK. The sleeping AP can only arm the broad IPCC/GLINK transport wake; it cannot select charger packets by opcode before resume.

ROCKNIX PR #2954 identifies the RP6's unsolicited BATTMGR_NOTIFICATION traffic and supplies the critical IPCC IRQF_NO_SUSPEND fix. This PR additionally makes the selected GLINK child an orderly wake source. The companion Armada userspace policy distinguishes a real offline-to-online charger transition from unrelated GLINK traffic before the graphical session is thawed.

CONFIG_PM_DEBUG=y derives CONFIG_PM_SLEEP_DEBUG=y and exposes the standard /sys/power/pm_wakeup_irq ABI used for that classification. It does not create a debug kernel or force verbose PM output; pm_debug_messages and pm_print_times remain off. No custom wake-reason ABI is added.

Captured UFS race

gate race snapshot: admitted state=2 active=0 pm=0 system=0;
h8-start state=2 active=0 pm=0 system=0;
failed state=3 active=1 enabled=1 suspended=1 pm=1 system=0
outstanding=0x0 gate-work=2 ungate-work=1

That failure recovered successfully:

ufshcd_gate_work: hibern8 enter failed -110, recovering link
ufshcd_gate_work: link recovered; runtime clock gating disabled

Validation

  • applied the complete Armada patch series against Linux 7.0.11 with no failures
  • completed full arm64 kernel and module builds after the UFS serialization and charger-wake changes
  • passed checkpatch.pl --no-tree --strict for the UFS root-fix patch with zero errors, warnings, or checks
  • generated the initramfs off-device and matched its module manifest against the previously boot-tested safe image
  • verified qcom_battmgr is present and early remoteproc/GPU firmware plus rsinput/haptics modules are absent from the initramfs
  • built the derived Armada bootc image and passed required bootc container lint checks
  • booted the final kernel carrier kernel-glink-policy-33fd641b3c8e on a Retroid Pocket 6
  • completed repeated ordinary and in-game deep-suspend/resume cycles with active haptics and zero kernel suspend failures
  • exercised the real rsinput force-feedback path synthetically and repeatedly in No Man's Sky with no atomic-context warning or BUG
  • reproduced the original DME_HIBER_ENTER timeout; inline link recovery completed and diagnostics captured the race
  • completed further in-game deep-suspend/resume cycles after the prepare/complete serialization fix without UFS timeout, command abort, I/O error, or storage failure
  • verified /sys/power/pm_wakeup_irq reports the standard IPCC wake source required by the userspace classifier
  • verified charger attach wakes the sleeping AP, background charger-firmware notifications are returned to deep sleep by the companion policy, and charging remains active during deep sleep
  • confirmed battery health, PipeWire speaker output, ADSP/CDSP, and active game audio remained healthy after the test sequence

@gh123man
gh123man marked this pull request as ready for review July 31, 2026 21:44
@gh123man gh123man changed the title Port ROCKNIX deep-sleep kernel fixes Port ROCKNIX deep-sleep and charger-wake kernel fixes Aug 3, 2026
@gh123man
gh123man force-pushed the codex/rocknix-deep-sleep-kernel branch from 7341399 to 1fc6c5d Compare August 3, 2026 01:10
@gh123man

gh123man commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Cross-repository firmware-path dependency

Upstream commit 63010cc in the rebased kernel branch changes the RP6 DTB firmware path from qcom/sm8550/ayn/odin2 to qcom/sm8550/retroidpocket/rp6. The current Armada image did not yet provide that directory, which prevented ADSP startup and caused both null audio and unavailable battery readings in the first full snapshot.

The companion Armada PR now fixes the image side in commit 8936883 by providing a validated compatibility alias to the already-tested Odin 2 blobs. The kernel change remains device-specific and unchanged; native RP6 firmware can replace the alias later. See gh123man/armada#1.

@gh123man
gh123man force-pushed the codex/rocknix-deep-sleep-kernel branch from 1fc6c5d to 95e7854 Compare August 3, 2026 13:01
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 5, 2026
qcom_spmi_haptics_global_playback() is reached from the rsinput
force-feedback callback, which runs below input_event() with the input
device event lock held and interrupts disabled. It calls
haptics_erase() directly, which takes a mutex and synchronously
cancels delayed work, so it can sleep: "BUG: scheduling while atomic"
during game rumble.

Record the latest request atomically and apply it from a workqueue,
serialise upload, playback and gain with a mutex in process context,
and drain both the playback work and the delayed stop work before the
haptics device suspends.

Ported from gh123man/armada-packages#1, where it was found,
root-caused and validated on hardware while testing the SM8550
deep-suspend series. Not a suspend fix in itself: the crash is
reachable on any device carrying 1003-rsinput-add-ff.patch whenever a
game rumbles, no suspend involved.
@gh123man

gh123man commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Seems like folks are discovering this branch.
Sleep is mostly stabilized this branch. The UFS race fix prevents a rare and occasional hang during sleep, this is a critical fix to stabilize deep sleep.

however it's worth noting that while CPU sleep works, there is still somewhat significant battery draw due to CX/AOSS/DDR remaining on. Not sure if there are reference patches to fix this.

Additionally - this series of patches wakes the device when the USB charger is plugged in. However this implementation also causes the device to randomly wake when other operations happen. This requires a userspace wake management routine in this PR: gh123man/armada#1 which is not totally stable yet.

Cherrypick at your own risk 😄

xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 5, 2026
qcom_spmi_haptics_global_playback() is reached from the rsinput
force-feedback callback, which runs below input_event() with the input
device event lock held and interrupts disabled. It calls
haptics_erase() directly, which takes a mutex and synchronously
cancels delayed work, so it can sleep: "BUG: scheduling while atomic"
during game rumble.

Record the latest request atomically and apply it from a workqueue,
serialise upload, playback and gain with a mutex in process context,
and drain both the playback work and the delayed stop work before the
haptics device suspends.

Ported from gh123man/armada-packages#1, where it was found,
root-caused and validated on hardware while testing the SM8550
deep-suspend series. Not a suspend fix in itself: the crash is
reachable on any device carrying 1003-rsinput-add-ff.patch whenever a
game rumbles, no suspend involved.
@dcu

dcu commented Aug 5, 2026

Copy link
Copy Markdown

there's more patches from the original developer of the patch here: https://github.com/thorch-os/thorch/pulls?q=is%3Apr+is%3Aclosed+sleep

including 10x lower power consumption

@gh123man
gh123man force-pushed the codex/rocknix-deep-sleep-kernel branch from 82991f3 to 4a28583 Compare August 5, 2026 20:42
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 6, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 6, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 6, 2026
qcom_glink_smem_register() requests every transport interrupt with
IRQF_NO_SUSPEND, so GLINK client callbacks can run against already-
suspended devices, and IPCC children advertise IRQCHIP_SKIP_SET_WAKE, so
no client can arm the GIC summary interrupt as a wake source.

Drop IRQF_NO_SUSPEND, let an IPCC child propagate its wake configuration
to the shared parent, and register the existing transport interrupt as
the device wake IRQ when the edge is described as "wakeup-source".

Ported from gh123man/armada-packages#1, renumbered from 0506/0507
because those numbers are taken here. No node is marked wakeup-source in
this branch, so nothing gains a wake source: arming the ADSP edge would
undo 0502 unless userspace can separate a real charger transition from
background BATTMGR chatter, which is what Armada's companion policy does
and ROCKNIX has no equivalent of yet.
hackbar pushed a commit to hackbar/rocknix that referenced this pull request Aug 6, 2026
qcom_spmi_haptics_global_playback() is reached from the rsinput
force-feedback callback, which runs below input_event() with the input
device event lock held and interrupts disabled. It calls
haptics_erase() directly, which takes a mutex and synchronously
cancels delayed work, so it can sleep: "BUG: scheduling while atomic"
during game rumble.

Record the latest request atomically and apply it from a workqueue,
serialise upload, playback and gain with a mutex in process context,
and drain both the playback work and the delayed stop work before the
haptics device suspends.

Ported from gh123man/armada-packages#1, where it was found,
root-caused and validated on hardware while testing the SM8550
deep-suspend series. Not a suspend fix in itself: the crash is
reachable on any device carrying 1003-rsinput-add-ff.patch whenever a
game rumbles, no suspend involved.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 7, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 7, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 7, 2026
qcom_glink_smem_register() requests every transport interrupt with
IRQF_NO_SUSPEND, so GLINK client callbacks can run against already-
suspended devices, and IPCC children advertise IRQCHIP_SKIP_SET_WAKE, so
no client can arm the GIC summary interrupt as a wake source.

Drop IRQF_NO_SUSPEND, let an IPCC child propagate its wake configuration
to the shared parent, and register the existing transport interrupt as
the device wake IRQ when the edge is described as "wakeup-source".

Ported from gh123man/armada-packages#1, renumbered from 0506/0507
because those numbers are taken here. No node is marked wakeup-source in
this branch, so nothing gains a wake source: arming the ADSP edge would
undo 0502 unless userspace can separate a real charger transition from
background BATTMGR chatter, which is what Armada's companion policy does
and ROCKNIX has no equivalent of yet.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 10, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 10, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 11, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 11, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 22, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 22, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 23, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 23, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 27, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 27, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 27, 2026
ufshcd_gate_work() only restores the software gating state when
DME_HIBER_ENTER times out; it does not repair the link. During system PM
pm_op_in_progress suppresses the generic fatal error handler, so the
controller is left operational in software but unable to complete device
commands, and a following suspend can block in the filesystem sync after
userspace has already blanked the display. That is the shape of the 54s
"Filesystems sync" tiopex reported on the Thor.

Recover the failed enter inline, using the out-of-band relink completion
poller added by 0201, then pin runtime clock gating off for the rest of
the boot so the same idle Hibern8 is not retried. System PM keeps
working and can still transition the link for suspend.

Ported from gh123man/armada-packages#1, written against this same
series.
xiaodoudou pushed a commit to xiaodoudou/distribution that referenced this pull request Aug 27, 2026
The failure-only diagnostics in the patch above captured the ordering
behind that timeout: the gate worker had been admitted and had entered
DME_HIBER_ENTER before system PM took its hold, so ufshcd_hold() changed
the gating state under a running UIC command and the PM path then waited
on that worker.

Take a clock-gating hold in ufshcd_suspend_prepare() and release it from
ufshcd_resume_complete(). That drains a pending gate worker while normal
completion handling is still available, and stops a new idle Hibern8
starting inside the device suspend and resume callbacks.

This is the root fix; the inline recovery stays as the storage-safety
fallback. Ported from gh123man/armada-packages#1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants