Repository navigation
Conversation
erikwb
force-pushed
the
j713/typec-idle-sleep
branch
from
October 30, 2026 14:32
4af54db to
aa728be
Compare
On the J713 the SuperSpeed port refuses system sleep for as long as any USB device is attached: atcphy_suspend() returns -EBUSY while the PHY owns its common block, so a laptop with a USB keyboard on that port never suspends: PM: dpm_run_callback(): atcphy_suspend returns -16 PM: Some devices failed to suspend, or early wake event detected Suspend-to-idle does not take the link down. While its core runs, DWC3 is a syscore device and keeps the shared power domain on across system sleep, and xHCI keeps running so that a USB device can wake the system. The PHY keeps its state with the domain, and resume restores it only if the domain went off. Allow suspend-to-idle with an active common epoch while a host runs on it. Keep refusing one without a host, as in a USB3 device role, where nothing holds the domain; an incomplete one; and any deeper sleep state, which would lose the link. Warn on resume if the domain went off under an active host link after all. A KUnit case covers these outcomes. Signed-off-by: Erik Bourget <erik@ebourget.net>
On the J713 a SuperSpeed link does not come back from U3 once the core has suspended the USB3 PHY with it (GUSB3PIPECTL.SUSPHY): resuming the port drops the link, and the whole tree behind it disconnects. This happens when a USB3 hub autosuspends, and on every resume from suspend-to-idle, where the USB core suspends all ports. A mounted disk behind a USB3 hub is removed under its filesystem. Clear SUSPHY for the USB3 PHY once the T8132 PIPE switch to USB3 is done, by setting dis_u3_susphy_quirk, which dwc3_enable_susphy() then honours when the xHCI host starts. The switch itself still runs with SUSPHY set, as it requires. Restore the device tree's setting before the host is stopped, which sets SUSPHY again: the PHY hands the PIPE back in P3, and fails to lock the PIPE handler otherwise. The core stop does the same for the error paths. USB2 PHY suspend is unchanged. The port routes the USB3 PIPE for any USB-C device in USB mode, so the USB3 PHY stays out of P3 while any USB device is attached, also a USB 2.0 one. The idle USB-C ports are powered down, so it costs nothing with nothing attached. The host test stubs of struct dwc3 and struct dwc3_apple gain dis_u3_susphy_quirk and dt_dis_u3_susphy, and the teardown test checks that the device tree's setting is back before the host exits. Signed-off-by: Erik Bourget <erik@ebourget.net>
The USB 2.0 hub of the VIA Labs VL822 (2109:2822), as in Anker USB-C hubs, does not pass on a remote wakeup from a device below it while it is suspended itself, like the VL122. A USB keyboard behind it cannot wake the system from suspend-to-idle. Give it USB_QUIRK_HUB_NO_SUSPENDED_WAKEUP, so that it is kept awake in suspend-to-idle while a device below it may wake the system. Its autosuspend is unchanged. A KUnit case checks the entry. Signed-off-by: Erik Bourget <erik@ebourget.net>
…a plug The resume revalidates the port and reports a cable removed in system sleep, but reads the power status only while a plug is present. A power source removed in sleep then leaves the cached power status, and with it the port's USB power supply, online until the next power event, and upower reports AC power while on battery. Read the power status unconditionally. Without a plug it reads as no connection. This matters once the events of a sleeping controller are acknowledged in sleep, before resume could handle them, which the following changes do. Signed-off-by: Erik Bourget <erik@ebourget.net>
As on ti,tps6598x, the controller may wake the system from sleep for a change on its port. Signed-off-by: Erik Bourget <erik@ebourget.net>
…link A USB device plugged into a J713 in suspend-to-idle cannot wake it, and cannot wake it later either: an idle USB-C port is powered down, so the device is never enumerated. Only the Type-C controller notices it: its event interrupt fires for the attach even while it sleeps. As a plain wakeup source that interrupt would also wake the system for a charger, on every plug, unplug and power negotiation. Filter the events instead. With wakeup-source set, the event interrupt stays live in system sleep (IRQF_NO_SUSPEND) and sn201202x_sleep_event() handles it there: it wakes the controller, acknowledges its events, reads its data status and status, and wakes the system with pm_wakeup_dev_event() only for a link that was not there before: - a USB2 or USB3 link with the port as the host: a port attaches as a USB device, as to a charger, before any data role swap; - from a power source, only one capable of USB communication, as its first source capability PDO says: a charger may still report a USB link as host for a moment after the swap; - DisplayPort, Thunderbolt or USB4. A charger plugged in or unplugged leaves the system asleep. A read that fails wakes it. The controller goes back to sleep either way, and the resume revalidates the whole port as before. A controller reset seen in sleep is set up again on resume, as cd321x_check_setup() latches it. The interrupt is not armed for wakeup, so IRQF_NO_SUSPEND is used as Documentation/power/suspend-and-interrupts.rst describes for a handler that decides itself whether to wake the system, with pm_system_wakeup() underneath. The watch is set up for suspend-to-idle only. In suspend-to-idle the tick is frozen, so a sleeping wait does not end until something else wakes the system: the controller is polled with busy waits there, within a fixed budget, and WAKEUP is repeated in case it reached a controller still going to sleep. A resume whose WAKEUP is not confirmed now falls back to polling as well. Its other interrupts are suspended, so this needs a controller without select notification, as on T8132; others keep their behaviour. A transfer the SPMI controller never answers still waits on its timer, so it ends only once the system is awake again. The port's power/wakeup setting turns the wakeup off. KUnit cases cover the link decision with the data status and status as read on a J713 for a charger as it attaches, swaps and settles, and for a USB keyboard. The tipd_init() host test stubs gain sleep_event and IRQF_NO_SUSPEND, and the SN201202x transport test covers the atomic selection too. Signed-off-by: Erik Bourget <erik@ebourget.net>
…ttach Mark both USB-C port controllers as wakeup sources. A USB device, dock or display plugged in while the J713 is in suspend-to-idle wakes it; a charger does not. Signed-off-by: Erik Bourget <erik@ebourget.net>
An SN201202x stays awake for as long as the system is, even with nothing attached and nothing to do. On a MacBook Air (13-inch, M4) the SMC reads about 190 mW on the 3.3 V rail that feeds the two controllers while they are awake, and about 1 mW once they sleep. Let an idle controller sleep, with runtime PM: a register access wakes it, and it sleeps again a second after the last one. Its event interrupt stays live while it sleeps, so an attach or a power change still reaches the handler, whose first read wakes it. Nothing else reads the controller on its own, so it sleeps through charging and with a USB device attached too. A display, Thunderbolt or USB4 link keeps it awake, as nothing was tested with one while it sleeps, and so does Apple's DBMa debug mode. After a few failed tries to sleep, it stays awake until the next access. System sleep wakes an idle controller before its own SLEEP handshake, and resume hands it back to runtime PM; one that does not wake sleeps on through system sleep, without the wakeup watch, and resume wakes it. The SPMI bus holds a runtime PM reference while the driver is bound. Probe gives it to idle sleep only on the T8132 controllers, where the event interrupt is known to stay live while they sleep, as the system sleep event filter relies on. Elsewhere it is kept and the controller stays awake as before. power/autosuspend_delay_ms and power/control change the delay or turn idle sleep off. The SN201202x transport test now takes __regmap_sn201202x_read(), the transfer itself, rather than its new wrapper. Signed-off-by: Erik Bourget <erik@ebourget.net>
erikwb
force-pushed
the
j713/typec-idle-sleep
branch
from
October 30, 2026 20:27
aa728be to
8e872a1
Compare
Contributor
Author
|
Rebased onto the current integration/aurora together with #574. host-tests failed here because the new host harnesses (neo-stability, apple-dcp) compile the dwc3-apple and tipd functions that #574 changes against stubs that lacked its new fields. #574 now updates those stubs in the commits that need them, and host-tests pass on both PRs. The sn201202x transport selftest is updated as well. The idle-sleep change itself is unchanged. |
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #574 (the first 7 commits here are #574; the new commit is the last one).
The two SN201202x USB-C controllers on the MacBook Air (13-inch, M4) stay awake as long as the system does, even with nothing attached. The SMC reads about 200 mW on the 3.3 V rail that feeds them while they are awake, and about 1 mW once they sleep.
This lets an idle controller sleep, with runtime PM:
power/autosuspend_delay_ms).power/control=onturns idle sleep off.Testing
On a MacBook Air (13-inch, M4):
power/controlonagainstauto:--strictis clean, and so is a W=1 build.Known limitation
If a wake fails while runtime PM still counts the controller as active, register access fails until the next successful wake. Before this change that state was likewise unrecoverable.
This change was developed and tested by Claude (Anthropic), working with me on the J713.