Repository navigation
Conversation
3926193 to
abc6ee8
Compare
d808c54 to
b189876
Compare
c99d8b7 to
6deb620
Compare
RyanTheTide
left a comment
There was a problem hiding this comment.
Before this can merge, please reword the comments that name macOS driver classes and methods, mark steps as RE or decompile-verified, or say where a value was read. That covers mainly t6040-sleep.c, plus t6040-usb2.c, dcp.c and macsmc.c. Each comment should say what the hardware or firmware does: the register, the bits, the order, and what you measured on the J616s. Values and code can stay as they are.
2ce0d80 to
7aefde0
Compare
|
Done in 7aefde0, thanks for the pointer to #1625.
Rebased onto aea2deb on the way. Tested on the J616s on this head (suspend-to-RAM, hibernation, suspend-to-RAM again; details in the description). That round was on battery without the dock, so the external displays have not run on this head yet; I will add that run. What is left in |
|
Tested on a second J616s (M4 Pro, 24 GB, firmware of a macOS 27.0 install), Linux at EL2 under aura main 27298d21 (includes aura#148), kernel = integration + this series:
Four commits on top of 7aefde0 are on
Checks on that branch: universal config builds (both controllers =y, and each one off), sleep KUnit 6 pass / 1 skip, checkpatch --strict clean, s2idle 3/3 with no kernel errors and the deep rows above on the J616s, s2idle 3/3 on an M1 Pro and an M2 with no new kernel errors. |
…uspend The T6041 (M4 Max) has the AICv3 of T8122 and now a suspend-to-RAM driver of its own. Give "apple,t6041-aic3" its own info, the T8122 one with fw_suspend, so the AIC state the sleep loses is saved and put back as on T8140. Nothing else in the driver looks at fw_suspend. Also in aurora-silicon#1477 (T6040 sleep), which admits apple,t6041-aic3 the same way; drop this one when that lands. Signed-off-by: AxxzyWasTaken <axxzytte@gmail.com>
apple_nvme_t6040_hw is only matched by "apple,t6041-nvme-ans2". Give it the sleep fields T8140 uses: register ANS with the sleep core (sleep_traced), flush namespace 1 only, the OS's own, and send the full shutdown notification at suspend. The T6041 sleep driver stops ANS through the sleep core's coprocessor stop. On T6041 the sleep core's last check (the shutdown notification has completed) does not run: the T6041 driver calls only the coprocessor stop. Also in aurora-silicon#1477, which sets the same fields with sleep_untraced on the same struct; drop this one when that lands. Signed-off-by: AxxzyWasTaken <axxzytte@gmail.com>
The PMP's SRAM loses its contents in a real sleep. On the J616C's 26.6.2 firmware its __DATA segment is at 0x500761000, 0x7d000 bytes: back up (0x61000, 0x7d000) of the SRAM, as T8140 does for its own. Also in aurora-silicon#1477, whose HW_CFG_T6041 has the same range; drop this one when that lands. Signed-off-by: AxxzyWasTaken <axxzytte@gmail.com>
On apple,t6041, write the keys macOS writes last before MBSE slpa in a trace of its sleep: WKTP = 2 (AppleSMCEmbedded, going to sleep), then pmVC. The value written is 0x101, as the bring-up kernel runs it; the comment, taken over from it, says the trace has 0x100. That difference is open. Also in aurora-silicon#1477, which writes WKTP through a sleep_wktp parameter; drop this one when that lands. Signed-off-by: AxxzyWasTaken <axxzytte@gmail.com>
A conventional host (internal ports with the radio and the SD reader) loses its power in a real sleep: the endpoints sit unpowered or in reset behind a link that is down. On a firmware suspend, save what only the port registers hold; on the noirq resume, note which ports lost their link and keep a copy of their functions saved state before the PCI core spends it on the down link; in the normal resume phase (after the SMC, which owns the PWREN GPIOs) bring each such port up again as probe does, then restore its functions. Only for hosts that are not tunneled, have no managed SIDs, no PIO DMA supplier and more than a root bus; s2r_conventional turns it off. The suspend-to-idle paths (domain hold, adopted links) are unchanged. Also in aurora-silicon#1477 (0ab1a66), which this follows; drop this one when that lands. Signed-off-by: AxxzyWasTaken <axxzytte@gmail.com>
1599341 to
9bee928
Compare
|
Picked up, thanks for the second machine and the four commits. Head is f259e90 on integration/aurora 107b361 (the brcmfmac
If you would rather merge 9bee928 first and take the 35 commits as a second pull request, say so and I will split it there; they sit on top and nothing below them changed. On the J616s here, kernel from this head with Not verified: T8140 on the merged driver (its object differs from before only in the functions that now go through the part; a sleep on a T8140 would be welcome), and external displays, the dock and the kept USB hosts on this head. The USB3 miss on your dock port ("AP idle not acknowledged: -62") is under Open; I could not try for it without a dock. |
… K116s deep-sleep base Base: Jens Axboe's K116s series of 2026-11-12 (40 patches on integration/aurora 2b4523c), the tree his K116s runs at EL2. aurora-silicon#1477 brings the generic Apple SoC sleep driver with per-SoC parts (sleep.c, sleep-t6040.c) to start a T6050 part from. Signed-off-by: Thomas Glanzmann <thomas@glanzmann.de>
|
@fbalazsy thanks for picking up the fixups and folding the T6040 controller into sleep.c. To merge this we need:
|
The CIO bring-up stored the plug orientation it had programmed in cio_flip, and nothing ever read it: the teardown of mode 2 is the same for both orientations, and the next bring-up takes the orientation from the Type-C switch again. Drop the field and its one store. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The PHY's tunable tables are applied by their position in one list, and three places depended on positions written out as numbers: the AUSPLL table that USB3 applies once more (7), the lane tables that DP writes again after each link power-up (4 and 5), and the generic tables DP starts with (the first 6). A static_assert checked that the last two agree with each other, not that the list has those tables at those positions. Name the positions in an enum and build the list with designated initialisers, so that a table that moves takes its users with it. The generated code doesn't change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
checkpatch --strict: "multiple assignments should be avoided" for the two DP tunnel rates that a restore from disk clears. Clear them one by one. The generated code doesn't change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The teardown of a CIO session and the error exit of the CIO bring-up run the same shutdown: both TX lanes and the common block down, the two receivers powered down, 200 us, the coprocessor stopped, the CFG block back in its boot state. Put it into one function for both. The register accesses of either path and their order are as before. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
When a write of the USB2 power-down sequence does not read back, the PHY marks itself failed and refuses every later operation: no power cycle of a PHY in an unknown state. A port that goes down for a sleep with its host kept logs that and lets the sleep go on, which is right, as the sleep powers the SoC down and the USB2 PHY with it. But the mark outlived the sleep. The wake refused to bring USB2 up on a PHY that had just been through its reset, the host was restarted, its stop failed on the same mark, and from then on the stop of that host failed before every sleep, which refuses the sleep: one write that did not land, and the machine did not sleep again until it was rebooted. A restore from disk already drops the mark, as the PHY comes back cold. A sleep that powers the SoC down is the same for the PHY: drop the mark there too, next to the coprocessor's pending power cycle, in one helper for both. After a sleep that left the SoC powered the mark stays, and the wake reports the port as before. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
…idle A suspend-to-idle leaves a USB host running: xHCI keeps going so that a device can wake the system, and the controller's glue keeps the controller's power domain on by being a syscore device. On T8132 the Type-C PHY shares that domain. On T6040 it does not: the PHY sits in atcN_common_dp, below atcN_phymxwrap, which powers the PHY's coprocessor and its mailbox, and in the noirq phase of every system sleep the PM domain core powers off the domain of each device that has suspended. So a suspend-to-idle took the power from a coprocessor whose firmware ran, and the driver went on taking it for running. The next stop of USB3 then asked a coprocessor that was no longer there: RTKit: AP idle not acknowledged: -62 USB3: PHY coprocessor idle failed: -62 USB3: no asynchronous Power Down acknowledge On a J616s this came with the first deep sleep after one suspend-to-idle of 15 s, on both ports with USB3 up at once, and with no sleep of a boot that had no suspend-to-idle before it. Keep the domain on for as long as the firmware runs: refuse the power-off in the domain's notifier while the coprocessor is between its start and the end of its stop. The mailbox's domain stays with it, as the parent of a domain that is on. A host that goes down for a deep sleep stops the coprocessor first, and so do CIO's teardown for every sleep and the recovery before its power cycle: none of them is held up. The other way, to stop the coprocessor for the suspend-to-idle and to start it again after it, would have the firmware tune the PHY once more under a link that is up, with devices that are only suspended. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
Across a T6040 suspend-to-RAM the stop of a display coprocessor saves the power words of its pipe's sub-blocks, which follow the CPU's power word, and its wake writes back those that were on and now read off. The loop always covered eleven words. That is the internal pipe's count (DISP_CPU +0x8 to +0x58). An external pipe has seven (DISPEXT0 +0x358 to +0x388, DISPEXT1 +0x4a8 to +0x4d8), as the sleep controller's apple,check-ps-b4-sleep lists them, and the test meant to keep the external processors out of the loop read a flag that is only set for the T6030 external processor. So the HDMI processor (DISPEXT0) also saved, and on a wake could write, ATC0_COMMON, ATC0_CIO, ATC0_PCIE and ATC0_CIO_PCIE at +0x390 to +0x3a8: the power domains of the first Type-C port, which belong to the PMGR power-state driver and the devices behind them. With one of them on at the stop and not yet on again when the display processor resumes, the display driver raises it behind genpd's back. The USB-C processor (DISPEXT1) did the same to the SYS, FE and CPU power words of DISPEXT2 at +0x4e0 to +0x4f0, the power of another display coprocessor on a board that uses it. The same flag chose the GAPF slice whose clock protection is cleared around a write of the CPU power word: every coprocessor cleared the slice of the internal one's DART. The release of all protected slices that follows hid that. Give each pipe its number of sub-blocks and its DART's slice, next to its power word offsets, and use them for the save, the restore and the slice. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
A display coprocessor whose firmware restart failed is given up on: its messages are dropped from then on and it drives no display until the reboot. The stop of the next T6040 suspend-to-RAM powers its CPU off like any other, and the wake then ran the warm restart on it again. If that handshake fails, the same error is logged at every wake. If it succeeds, a firmware runs that nobody listens to: the pause, resume and power calls to its endpoints time out one after the other, and an external processor is then reported back, with the HDMI converter, the DP PHY and the link set up for it. Raise its power words at the wake, so that the stop of the next sleep can reach the coprocessor's registers again, and leave its CPU stopped. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The stop of a T6040 suspend-to-RAM disables the link work of the internal display processor (Type-C reconnect and relink, HDMI settle, link recovery, Thunderbolt events), so that none of it reaches a firmware about to idle. Nothing enabled it again: the wake's restart of the internal firmware does not, and a stop that fails gets no resume at all. Each sleep disabled the work items once more, and from the first sleep of a boot on they never ran again. The internal pipe of the J616s has no external link, so nothing showed. Stop it once, under the flag the external processors already use for theirs, and enable it when the warm restart has the firmware back. A stop that fails or a firmware that does not return leaves it stopped, and the stop of the next sleep finds it so. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The internal display processor refused every later sleep of the boot once its firmware had crashed (-EBUSY from the stop). One whose restart had failed at an earlier wake ran the whole stop sequence against a firmware it no longer talks to, timed out in it and was refused the same way from then on. Only the external processors had a way through: their CPU powered off, nothing else. Take the internal one the same way when its pipe is down. The power word of its CPU must not go down over sub-blocks that are on, and only a firmware takes a lit pipe down, so a dead firmware that left its pipe powered still refuses the sleep, now with a message that says so. A crashed internal firmware is given up on at that stop and the wake leaves it stopped: started again, it could power the pipe and then fail, and the next sleep would be refused for good. The panel stays off until the reboot, as before, and the machine goes on sleeping and waking. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
pmc-dcp holds the number of powered display coprocessors. The wake from a T6040 suspend-to-RAM counts each one in when its domains are up and writes the count, but the stop never counted it out. After the first sleep of a boot all three stayed counted: the first wake wrote 1, 2, 3 as the processors returned, every later wake wrote 3 three times, the first two of them with one and two processors powered. Count a processor out when the power word of its CPU has gone down. Nothing is written then: the sleep controller zeroes the voter before the sleep, as it does on T8140, whose stop forgets the processor the same way. Every wake now writes what the first one did. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The warm restart after a T6040 suspend-to-RAM released the RTKit receive side in case it was still held for a hibernation image and, for an external processor, in case it was held for a sleep that keeps the firmware running. Neither can be held there. The hold for an image is taken at freeze and power-off only. It is dropped by the thaw, by the restore that follows a failed power-off, or by the cold start in the restored kernel, which the suspend notifier flushes before any later sleep. The other hold is taken by dcp_hold_for_sleep() for the H17P, 27.x and T8132 firmware only, not for the desktop 26.x firmware these processors run. No functional change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The stop of a T6040 suspend-to-RAM began with the backlight suspend of the 14.7 firmware and the drain of pending brightness presents. Only the desktop 26.x firmware reaches this function. The first acts on a 14.7 panel only. The second returns at once for a processor that was not adopted running or whose firmware does not present the level with its frames, and both hold for the two T6041 entries. No functional change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The T6040 suspend-to-RAM code asked the device tree twice for the compatible to learn whether a processor takes part, and told the two external processors apart by the physical base of their coprocessor registers, with the power word offsets, the number of sub-blocks and the GAPF slice of each in a switch. Put them in a table of the T6041 hw data, one entry per pipe, and take a processor's entry by its display endpoint (apple,dcp-endpoint: 0 the internal pipe, 1 DISPEXT0, 2 DISPEXT1), which the binding already gives every external processor. A table in the hw data is what says a processor takes part. An external processor without the property, or with an endpoint the table does not have, gets no entry and is left alone as one with an unknown base was. The three processors of the J616s get the offsets, counts and slices they had. No functional change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
Every stop and every wake of a T6040 suspend-to-RAM looked up the apple,display-power node and mapped and unmapped its "ps" and "pmc-dcp" windows, the GAPF bank and, separately, the one slice of it that belongs to the coprocessor's DART: four mappings per processor and cycle, in callbacks that have no use for a failed mapping. The stop also mapped "pmc-dcp" only to learn that the wake would find it. Look the pipe's power words up and map the three ranges once at probe, with the mapping types they had, and release them with the device. A processor without power words or without either window keeps nothing, and its stop and wake leave the power words alone as before. The slice is reached through the bank. The register accesses of the stop and the wake and their order stay as they were. No functional change. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
Every stop and every wake of a T6040 suspend-to-RAM warned once per display coprocessor that an AFK endpoint was "not paused" or "not resumed" with -95: endpoint 0x25 on the external processors, 0x2c on the internal one. The firmware advertises these endpoints and has sent no compact message on them. The pause and resume requests are compact packets and are not sent to such an endpoint at all: the error comes from the host side, before anything reaches the firmware. That made six warnings per sleep for endpoints left exactly as they were. Count these endpoints in the debug summary and keep the warning for requests that were sent and went unanswered. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The wake reads the lid's state for the display after it has told the SMC that the wake was taken, at every wake. The sleep order test still counted the commands of a wake without that read and has failed on its count since. Expect the read where it comes: after 'waka', before the key that ends the wake. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
A T6040 dry run of level 2 took the sleep up to the commit point with the steps counted as "can still be taken back", the SMC's sleep commit among them: the WKTP key, MBSE = slpa and the AP idle notice. It then refused, and the resume sent the SMC what a wake sends. That does not bring the SMC back. On a J616s every sensor the SMC reports stood still after such a run, the CPU die temperature at one value under load, until the next suspend that really lost power had woken; then the reports were live at once. A suspend to idle did not change it either way. So the commit belongs with the stops that only a power cycle undoes, which a dry run leaves out. Send it only when the suspend goes on to the power loss. A dry run of either level then warns the SMC and takes the warning back, as level 1 always did. apple_sleep_last_steps() had this one user and goes. A real sleep sends what it sent, in the same place. What a real sleep can still meet after the commit, a refusal at the commit point or a wake event before it, is not changed by this. The KUnit case runs a sleep that ends short of the power loss. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The SMC gets its sleep commit in the noirq suspend, as the last of the coprocessors and ahead of the CPUs' parking. What can still refuse the sleep comes after that: a later device, a wake event, the checks at the commit point. The resume then sends the notices of a wake, but an SMC that took the commit and saw no power loss stays in its sleep state until a sleep has completed: on a J616s its sensor reports stand still for that long. Nothing known to the driver takes the commit back, and sending it later would change the order a sleep that works depends on. Until one of the two is settled, say so in the log when it happens, so that reports which no longer move have their cause next to them. The KUnit case runs the commit, the refusal and the end of the sleep, and expects what a wake sends, once. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The driver wrote MBSE = slpw at .prepare and, when the suspend was called off before the commit, MBSE = waka at .complete. The SMC does not take the warning back. On a J616s, after a suspend that stopped before the commit (the SMC got slpw and then waka, about a second apart), the SMC no longer ran the fans: both stood at 0 rpm with the CPU die at 100 to 113 C under load, and a fan request was refused, until the reboot. Before that suspend, in the same boot, the SMC had started the fans by itself and had taken a request. The sensor reports stayed live, so this is not the standstill a refused commit leaves. A device that refuses and a wake event during the device suspend are ordinary ends of a suspend, and each left the machine without its fan policy. Send the warning in the noirq suspend, right before the commit and only when the suspend goes on to the power loss, which is the path a sleep that cuts the debug link already takes: slpw, 200 ms of polling, then the WKTP key, slpa and the AP idle notice. A suspend that ends before that sends the SMC nothing. What can refuse after the commit still leaves the SMC in its sleep state; the message for that case stays. The KUnit case for a suspend that stops short of the power loss now expects no command at all. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
apple_rtkit_ap_power_noirq() polls the mailbox, but it waits with usleep_range() and jiffies and takes the acknowledgment from the receive work. None of that is to be had at a sleep controller's commit, where interrupts are off, one CPU is up and timekeeping is suspended. apple_rtkit_sleep_idle_polled() already sends the AP and IOP idle notices there for a coprocessor that is then stopped: it polls a bounded number of times with udelay() and takes the acknowledgments in the poll. Add apple_rtkit_ap_idle_polled() for a coprocessor that stays up and is only told that the AP goes idle, as the SMC is. It shares the start of the polled idle (what is waiting is taken first, an owed OSLOG counter is echoed) and sends the AP notice alone. Such a coprocessor's client still has traffic on its own endpoints: a reply to a command it sent just before, or an event. While the notice is polled for, a message for an application endpoint therefore goes to recv_message_early as usual, and one that is not taken there is queued for the receive work, which runs again after the wake. Everything else is handled as in the polled idle. Compiled only: its first user is the SMC's sleep commit in the next patch, which has not run on hardware yet. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
A T6041 SMC takes neither MBSE = slpw nor the commit after it back, and
only a completed sleep or a reboot brings it out again. The driver sends
both in its noirq suspend. A sleep can still be refused after that: a
wake event that the PM core finds pending after the noirq phase, a CPU
that does not park, the sleep controller's checks at its commit, the
enclave before it sends its sleep request. The SMC then has slpw, WKTP,
slpa and the AP idle notice and no power loss, and its driver can only
say so ("sleep refused after the SMC's commit").
The T6040 sleep controller stops coprocessors at its commit: with
interrupts off, one CPU up and every refusal behind it, the enclave's
sleep request being the last of them. Register the SMC there, among the
stops that run first, and send the whole sequence from that stop: slpw,
200 ms in which the mailbox is polled, WKTP = 2, slpa, 6 ms, the AP
idle notice and 30 ms, as the noirq suspend sends them. The noirq
suspend then sends nothing, so a sleep refused anywhere before the last
stops leaves the SMC as it was, and so does a dry run, which never
reaches them. The wake is unchanged: it follows ap_notified, which the
last stop sets.
Against the noirq suspend the sequence now comes after the enclave's
sleep request instead of before it, because that request can still be
refused. It stays ahead of the other coprocessors' stops: those
registered as first run in the order of the registry's table, where the
SMC takes its place at probe and the only other one, the GPU, takes a
free place for each sleep. That place is a later one unless a driver
that registered before the SMC has been unbound since.
Nothing can sleep at the last stop and the RTKit worker does not run,
so the keys go through apple_smc_write_atomic() and the notice through
apple_rtkit_ap_idle_polled(). Atomic mode was only ever entered for a
shutdown; the last stop enters it and leaves it again, giving up an
answer that is still missing, so that the wake's commands are the usual
ones. apple_smc_write_atomic() timed its poll with ktime_get(), which
warns while timekeeping is suspended, as it is here: it reads the fast
monotonic clock instead. An event the SMC sends during the sequence is
taken from its outbox and reaches its handler after the wake.
A last stop cannot refuse, and the sleep goes on whatever it returns.
If the SMC fails a step, the step is logged and the rest is sent all
the same: an SMC that took the warning leaves its sleep state only
through the rest of the sequence and the power loss. The wake then
sends its part as after any sleep. With an SMC that answers nothing,
each write waits out its 5 s timeout with interrupts off.
Only the T6041 SMC registers. Every other SoC, a kernel that does not
run the last stops (below EL2, or without the sleep controller) and a
full registry keep the noirq suspend exactly as it is; T8140's order,
with the debug link's cut before the warning, was measured there. The
cut and its outbox poll stay in the noirq suspend for both paths: the
cut sleeps, and it has to precede the warning. The T6040 controller
has no cut, so that call does nothing there.
The existing KUnit cases describe the noirq path and pass unchanged.
New cases cover the last stop: nothing is sent in the noirq suspend
and the whole sequence comes from the stop, once; a refusal before the
stop sends nothing at the suspend and nothing at the resume; the wake's
sequence follows a stop, also one in which every step failed.
J616s, one boot: a dry run and a sleep refused before the enclave's
request leave the SMC without a single message, with its sensor
reports live and the fans under its control; four sleeps, one of them
right after the refusal and one after a hibernation, go through the
last stop (no message from the SMC in the 200 ms) and wake with the
usual record. A wake event during the entry was not tried.
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
A suspend gives up the link of an external display, and the resume
commit of its CRTC can come before the sink has answered again. The
firmware then fails the mode set ("setmode failed", "Modeset done, but
pipe not enabled"), which the driver logged as
[drm] *ERROR* [CRTC:65:crtc-1] mode set failed: -5
On a J616s with an HDMI display this came with every suspend to idle,
2 s after the wake, and now and then after a suspend to RAM. The
display was back 5 s later, when the firmware reported the sink and
the hotplug brought a mode set that worked.
Return the quiet -EPIPE of a pipe without a link when the connector of
an external pipe is reported disconnected. The hotplug that reports
the sink back sets the mode, as it did after the failed one.
J616s, HDMI and USB-C DP displays: two suspends to idle and two to RAM
without the error, both displays back after each.
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The glue device has a suspend callback and no resume callback, and was not marked asynchronous. Its xHCI child is, but an asynchronous device starts to resume only once the PM core has reached it in the list, and the core gets past the glue device only in list order. On a J616s wake from suspend-to-RAM the main PM thread waited 522 ms in a synchronous device ahead of the glue (an rfkill device behind the Wi-Fi function's restart), and both USB hosts with a dock's hub tree below them started only then. On the way in the two glue devices' suspends ran one after the other, 120 and 68 ms. Mark the glue asynchronous. What it depends on is given by its parent and its device links (PHY, DARTs, power domains), which the PM core keeps for asynchronous devices too. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
After a suspend-to-RAM the ports of the T6040 controller come back one after the other in the noirq resume, and each takes two waits of 100 ms: after the release of PERST# and after its link has trained. With two ports (Wi-Fi/Bluetooth and the card reader on a J616s) that is 414 ms of the 423 ms the whole noirq phase takes. Bring every port up to the release of PERST# first, wait once, then restore and train each, and wait once more. Each port still gets at least the same two waits; the phase loses about 200 ms. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The second 100 ms of a port's wake from suspend-to-RAM follows the link training. PCIe r5.0, 6.6.1 asks for it on a link that runs above 5 GT/s; up to 5 GT/s the 100 ms since PERST# are the whole requirement, and the ports have waited those. Both ports of a J616s have a target speed of 5 GT/s. Take the wait only when a trained port's target speed is above 5 GT/s. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
…d the resume A function that lost its power in a firmware suspend is found cold at the resume (interrupt mask 0), and the resume callback removes and probes it. On a J616s that takes 619 ms of the callback, about 550 ms of it for taking the interfaces and the wiphy down. Every device that follows in the resume order waits for it, and without USB devices it is what the whole resume phase ends with. Queue the remove and probe instead and return, on a freezable workqueue, so that the work starts once the resume of every device is over: run during it, the removal of the wiphy only moved the wait to the wiphy's own resume (522 ms there). The work takes the device lock, which an unbind holds too, and does nothing for a function that has no driver data any more. A PM notifier flushes the queue before the next suspend or hibernation, so that one never meets a function in the middle of its restart. User space sees what it saw before, the wiphy going away and a new one coming, a little later: after it is thawed. Hibernation and a suspend to idle keep the restart in the callback. J616s, suspend-to-RAM with the function associated, no USB hub attached: the callback returns after 24 us, the resume of the devices takes 453 ms, against 542 with the restart running during it, and the interface associates again. With the restart in the callback the callback alone took 619 ms. The host test of the PM callbacks gets the two new calls and a case for the queued restart and for the one that cannot be queued. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
4b0be95 to
3d13866
Compare
The T6041 driver idled the SIO firmware in the late phase of a suspend to RAM. An idled firmware does not come back with its power kept, so a sleep refused after that phase left it stopped until the next sleep that went through: on a J616s a refusal of the enclave's sleep request logged "sleep refused: firmware not back after a reset (-19), stopped until a real sleep", and the DMA service and the AES engine behind it were gone. Register the stop for the sleep's last step, as the T8140 does: the AP and IOP power idle with interrupts off, after the last refusal. A sleep refused before that finds the firmware running. A dry run of the T6040 sleep stops no coprocessor, and counted a registered one that was not ready as a check that a real commit would fail. Report the firmware ready in a dry run as well. On a J616s: after a sleep refused at the enclave's request and after a dry run the engine still encrypts and decrypts 16 MiB in each of CBC, CTR and ECB equal to the software implementation, the dry run reports no failed check, and suspends to RAM and to idle after them complete with the firmware back. Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
J616s (MacBook Pro 16", M4 Pro, T6040) running Linux at EL2 under aura: hibernation and suspend-to-RAM with the desktop session kept. Builds on #1445, which is merged.
Base
sleep.c) met three of the clean-up commits: the shared header now carries the base's definitions, the PMU write helper takes the USID as the base's does, and what the base reads from its match data (the PMU write command, the boot stage, the response vector's enable for the sleep) stays with the T6040 part as its own values. The 89 on top are new in this push and are listed under "New in this push".phy: apple: t6040: tolerate a busy recovery domain, AxxzyWasTaken'sspmi: apple: drop replies left by firmware at a firmware-driven resume) keep their authors and trailers.What is in it
pm_hibernate_is_restored()lets restore callbacks tell a restored image from an aborted hibernation; a boot chain with a fixed layout (aurahib=1) admits hibernation.sleep.c+sleep-t6040.c,struct apple_sleep_partinsleep-internal.h; one platform driverapple-sleep, the node enabled per board, on the J616s only). The entry takes the hardware down in a fixed order (CPU complexes, power gates, axi2af bridges, APCIe, DCS, the MasterMini S2R timer, then the commit word) and the wake brings the same blocks back, before syscore resume. The coprocessor and enclave registry stays insleep-registry.c; the driver binds it once in probe and each SoC's part gives the two answers (T8140's rules and T6040's are the ones they had).APPLE_T6040_SLEEPstays an option, now the switch of the T6040 part underAPPLE_SOC_SLEEP(withMFD_MACSMC=y). The part keeps its own suspend ops: suspend-to-idle passes through untouched and deep is offered only on a kernel booted witht6040_sleep.allow_entry=1. Four module parameters are left (allow_entry,dry_run,sleep_wd_secs,wd2_secs; 79 before); the progress marks, PMU records, probes, dumps and the experiments that were off by default are gone, the steps that were on by default are unconditional. Dry-run levels are 1 (every step that can be taken back, refused at the commit point) and 2 (also the last steps); the old level 1 never reached the commit point and is removed.hung_stopand session quarantine are used unchanged, the hibernation events go through its notifier, and an engine quarantined at bring-up is powered off for a sleep (and left off at the resume) instead of refusing every sleep of that boot.pm_sleep_ptr()instead of#ifdef CONFIG_HIBERNATIONin PM operations,dev_*messages from the T6040 part, the sleep node's unit address, bindings forapple,t6041-display-powerandaurora,s2r-mailbox. One of them fixes a build configuration: withCONFIG_HIBERNATION=na suspend-to-RAM stopped the display firmware and could not start it again; the restart is built for every system sleep now. For the comment, rename and constant commits the objects are identical before and after;sleep.obuilt without the T6040 part has every symbol unchanged from f259e90.New in this push (89 commits)
MBSE = slpwback: afterslpwand thenwakawith no sleep between, it stopped running the fans (0 rpm with the CPU die at 100 to 113 C) and refused a fan request until the reboot; after the whole commit (WKTP,slpa, the AP idle notice) and no power loss its sensor reports stood still as well, so the thermal zones read a constant. A dry run, a device that refuses, a wake event during the entry and a refusal at the commit point all ended there, on the previous head of this series too. On T6041 the whole sequence is now sent from the sleep controller's last stop, with interrupts off and every refusal behind it (apple_rtkit_ap_idle_polled()is new for that); on the other SoCsslpwmoves from.prepareto the noirq suspend, right before the commit. A suspend that ends short of the power loss sends the SMC nothing.AP idle not acknowledged: -62. The PHY now refuses that power-off while its firmware runs. With it: a powered-down sleep counts as the coprocessor's power cycle, a host whose USB3 is not back at a wake stays at USB2 and tries again at the next wake, and the CIO bring-up recovers the coprocessor and shuts the lanes down when it fails.mode set failed: -5of every suspend-to-idle with an HDMI display); the hotplug that reports the sink back sets the mode. A T6041 sleep keeps to the pipe's own power words; a firmware that was given up stays stopped across an S2R, and a dead internal processor no longer refuses every sleep; the sleep registers are mapped once at probe.sending frame failed (-19)at the first wake of a boot).roles.PM_POST_*, the LSTX level at a router stop keyed on T6040, the T6040 DP IN adapters kept disabled, no watch for a router whose node is disabled. Without a USB4 device here these ran as regressions only.timer_wakeupsinstead ofirq_timeoutsin the ANE's debugfs.macsmc_sleep_order_testexpects the lid read (it failed at the base); new KUnit cases for the SMC's last stop; the host tests of the PM callbacks follow the code they extract.Hardware results
J616s (48 GB), firmware 26.7.1, aura main (9887a6b5) at EL2, kernel built from this tree with
APPLE_SOC_SLEEPandAPPLE_T6040_SLEEPenabled.This head (278b7b9) is the rebase onto aa68b3c of the head the tables below were run on (4b0be95 on fdef45b), plus one commit (
dmaengine: apple-sio: stop the T6041 firmware at the sleep's commit). On the rebase (3d13866) with that commit's module: cold boots, three suspends to RAM, one suspend to idle, a level 2 dry run and two sleeps refused at the enclave's request, with the panel and both external displays, Wi-Fi and Bluetooth back each time. Hibernation and the tables below were not run again.On 4b0be95, one boot. Set-up: a Samsung G95NC in picture-by-picture with one half on the HDMI port and the other in DP alternate mode, a USB3 disk, charger; no dock. All three displays were seen lit after the boot and after every step.
mode set failed; the HDMI half is back with the firmware's hotplug, about 5 s after the wake.On the previous base (6970a51) the same commits ran over five kernels as they were written, most of it with a dock (hub, Ethernet, card reader), the USB3 disk and one USB-C DP display:
-62, 9 SuperSpeed links before and after. Before the fix: 2 of 2 lost USB3.drop_next_power_down_ack)apple_sepsleep_refuse_before_send), and after an RTC alarm that fired during the entry.Not run on either of these two heads: anything with the dock (it was run on the previous base only), the KVM guest across the sleeps, a USB4/Thunderbolt device (none here). T8140 was not run by me on any head, and the SMC and wake-time commits change paths it shares: the noirq
slpwon every SoC,device_enable_async_suspend()on the dwc3 glue, the BCM4388 restart, the skipped 100 ms insn201202x_resume.Review
The comments and commit messages of the 89 new commits follow the same rule as the rest since the last review: they say what the hardware or firmware does and what was measured on the J616s.
Checks
Run on 4b0be95 against its base fdef45b (not repeated after the rebase onto aa68b3c), on an aarch64 host with its own compilers (gcc 16, clang 23, rustc 1.99), not the pinned ones of the workflows.
tools/asahi/run-host-tests.pybrcmf-recovery-testsamong them). Without the shims 28 runs fail on this host's compilers; that run was not repeated at this base.apple-display-gate-t6041-maxcases that fail at the base as well (base: 1686 tests, those five andmacsmc_sleep_order_test, which passes with the series).boot.kunitconfigandtransport.kunitconfigdrivers/soc/apple/sep.kunitconfigapple_sep*) 301 of 301.scripts/dtc/apple-dt-check.sh(CHECK_DTBS on every Apple tree,dt_binding_checkon the changed bindings)sep-sourceworkflow: thetools/aurora-septests, the three DWC3/ATC compile profiles-Oones and the ABI step included); the threeapple-dwc3-atc-compile.shprofiles compile;apple-sep.obuilds.checkpatch.pl --stricton the commits written for this pushOpen
WARNINGattypec_altmode_update_active()came once in the same re-cabling and was not looked at.ignore_loglevel: unchanged from the previous description. Independent of sleep.usbcore.old_scheme_first=Yand the ports' fast-enumeration quirk took 0.64 s of it here with the same devices and device numbers; that is a setting, not part of this series.WKTPis written 0, and whether it loses its fan policy afterslpw+wakaas this one does (then its noirq path has the same hole after the noirq phase that the last stop closes on T6041); whethersn201202x_resumemay skip the 100 ms on every SoC with that controller; whether the AIC mask resync after a resume from disk should be registered for every SoC; whether a host left at USB2 by a wake should try a normal start at once instead of at the next wake. 263989e (DART in an off domain) is not in here.How to try
Deep sleep stays opt-in, on the kernel command line:
With
t6040_sleep.dry_run=1(writable at run time) the same request stops at the commit point and resumes warm.