Repository navigation
Deep sleep (suspend-to-RAM) for the MacBook Neo (T8140) at EL2 - #1208
Closed
DjDeveloperr wants to merge 82 commits into
Closed
DjDeveloperr wants to merge 82 commits into
DjDeveloperr wants to merge 82 commits into
Conversation
DjDeveloperr
force-pushed
the
feat/t8140-s2r
branch
from
November 8, 2026 11:03
bab0092 to
08ae55d
Compare
A kernel that runs directly at EL2 on T8140 has no monitor to suspend the system or power a CPU off for it, so it writes the hardware itself. The registers it needs belong to several blocks, the PMGR, the watchdog, the wake controller, the AMCC, the PMC, SPMI, the memory controller and the clock blocks among them, and other drivers use some of them too. Describe them as one node whose driver doesn't claim its ranges. The node also names what the sleep depends on elsewhere: the wake sources to leave enabled, the display coprocessors that must be off before the commit, the port controller that carries the debug link, the PCIe host whose shared block ANS's link runs on, and the AP pin controller whose pins a deep sleep resets, with the pins to leave alone. Signed-off-by: Diljit Singh <hey@djdev.me>
Each CPU cluster has a PMGR word whose top bit lets its cores power down in WFI. A core that PMGR powers on comes up allowed to, but the boot CPU only follows the word, and iBoot leaves it clear for T8140's P cluster. Describe the words, so that a driver can set the bit before the secondaries start. Signed-off-by: Diljit Singh <hey@djdev.me>
After a firmware-driven suspend the power controller puts each domain back where genpd's books have it. Some domains need their driver's order on the way back instead: the GPU's needs its target, then its bridge, and only then automatic power management. Let the tree mark such a domain, so that the power controller leaves it to its driver after a wake. Signed-off-by: Diljit Singh <hey@djdev.me>
A coprocessor can have a second mailbox for its secure side, the ADT's iop,secure-rtbuddy-proxy. It has the registers of the AOP setup port, and its interrupts aren't described yet, so its consumer polls it. Add its compatible, and require interrupts for every mailbox but this one. Signed-off-by: Diljit Singh <hey@djdev.me>
… sleep A sleep that powers the SoC down resets every DART, and some of their state comes from the boot loader, not from Linux: - apple,dapf-ranges and apple,dapf-r20: the address filter the loader programs, to be written back before the consumer's firmware starts. - A GAPF slice in place of the DAPF registers: the part of the fabric filter that guards a coprocessor CPU's power state, cleared to power that CPU down. - apple,sleep-prepare, apple,sleep-restore and apple,sleep-restore-late: register writes around a sleep for a DART whose consumer gates itself and saves and restores it in its own order. - apple,resume-tunable: the loader's tunables for a DART whose loader has already applied them, to be written back where a power-off lost them. Signed-off-by: Diljit Singh <hey@djdev.me>
ANS's NAND link runs on the shared PCIe block whether or not the PCIe host is bound, and after a deep sleep that block has to be brought back from power-off with the boot loader's settings. On T8140 the host node has five more register blocks (the AXI bridge, the CIO3 PLL, the clock generator, the PHY and its IP block) and the loader publishes six tunables for them, the PHY's PLL and lane tables among them. Allow them, for T8140 only. Signed-off-by: Diljit Singh <hey@djdev.me>
A T8140 display coprocessor's driver writes three words around a restart of its firmware, as macOS does: the display PMC word that counts the powered DCPs, and the power manager's power state and power gate words. They belong to the power manager and the display block, so no driver binds the node that holds them. A DCP names it in its apple,display-power property. Describe the node, so it has a compatible, and the PMC word's address as its own rather than the power manager's. Signed-off-by: Diljit Singh <hey@djdev.me>
The DCP's firmware takes an IOP idle only with its secure RTBuddy port answered, so the driver needs that second mailbox. Allow it as "secure". To restart its firmware after a sleep, the driver writes the display's power words itself, in the order macOS does. apple,display-power points at the node whose ps, pwrgate and pmc-dcp registers hold them, such as an apple,t8140-display-power node. Signed-off-by: Diljit Singh <hey@djdev.me>
T8140's SIO is stopped for a sleep and started again after, and the
sleep controller finds its coprocessor registers by name. Name T8140's
three blocks: the ASC registers before the mailbox window ("asc"), the
auxiliary ASC window, and the ASC registers after the mailbox window.
Signed-off-by: Diljit Singh <hey@djdev.me>
The PMP has to know when the GPU is in use. Around a sleep the GPU's driver withdraws that request before it lowers the GPU's power state, and makes it again after raising it, as macOS does. Let the tree name the PMP report entry that carries it. Signed-off-by: Diljit Singh <hey@djdev.me>
DjDeveloperr
force-pushed
the
feat/t8140-s2r
branch
2 times, most recently
from
November 8, 2026 12:35
8ad2161 to
034fd3f
Compare
… wake Three things a port controller's driver does for a firmware-driven system sleep, where the board asks for them: - apple,sleep-ssps: tell the controller the system's power state, S3 before the sleep and S0 after, as macOS does. - apple,debug-lock-key: the board's key that unlocks the controller's debug mode, to rearm the debug link after a sleep ended it. - apple,usb-remote-wake: this controller serves the USB controller's root port, and watches its USB2 lines for a remote wakeup during the sleep. Signed-off-by: Diljit Singh <hey@djdev.me>
The block next to the PIPE handler takes the USB controller's core in and out of hibernation: a power state request, a status with SLEEP, and event, enable and clear words for VVDD_USB_OFF, D0_ON and two wake events. macOS's controller driver uses it at every system sleep. In Linux the PHY drives that controller's reset and suspends after it, so give the block to the PHY, as an optional seventh window. Signed-off-by: Diljit Singh <hey@djdev.me>
The microphone array has a power rail of its own, which macOS switches on for each capture and off after it. Let the tree name it. Signed-off-by: Diljit Singh <hey@djdev.me>
A platform can find that what woke the system needs nothing from user space. On Apple laptops the SMC wakes the SoC for the lid closing as well as for it opening. macOS takes the closing as a dark wake: every driver wakes as usual, the display stays off, and the power management root domain puts the system back to sleep once it sees the lid closed. In macOS's 25G83 kernel, read statically, AppleSMC sends kIOPMClamshellClosed, and the dark wake policy sleeps with reason Maintenance when the clamshell is closed and the machine isn't in desktop mode on AC. suspend_again() can't do the same. It goes back to sleep after the early resume only, so drivers that do their sleep work in the normal suspend and resume phases are left half way. Add a dark wake flag for the platform to set during the noirq resume. Devices still resume fully but may leave out what the user would see. Once dpm_resume_end() is done, enter_state() runs suspend_devices_and_enter() again with user space still frozen, so user space sees one sleep. Only a wake from the sleep state can be dark, and each pass starts with the flags cleared. Signed-off-by: Diljit Singh <hey@djdev.me>
The enable callbacks of these features run through hotplug, and a context switch only writes ACTLR when the thread's value changes. A firmware-driven system suspend really resets the boot CPU, which comes back through cpu_resume and not through hotplug, so it keeps reset values with nothing to correct them. With AMX, that can leave EL0 access in the wrong state for the current thread. Restore them in __cpu_suspend_exit when pm_resume_via_firmware() says the CPU was reset. That's only set during such a system suspend, and the restore does nothing on a CPU without these features, so other machines don't change. Signed-off-by: Diljit Singh <hey@djdev.me>
Plain spin-table has no way out of the kernel, so disabling the non-boot CPUs for system sleep fails with -EOPNOTSUPP. That rules out suspend-to-RAM on a kernel that a boot monitor enters directly at EL2 with spin-table, with no PSCI below it. Where iBoot locks a core's reset vector to the boot object, a core that powers off and is started again goes back through the boot object. If that keeps the core's release address, the core returns to secondary_holding_pen on its own, and hotplug needs no more from it. Such a boot object says so once, with /chosen/aurora,s2r-mailbox, the mailbox the boot core resumes through. With that property present, a CPU whose enable method is "spin-table" gets the parking ops added here, so the device tree keeps the method every kernel knows, and a kernel without this change ignores the property and stays on plain spin-table. A boot object can also name the "aurora,rvbar-spin-table" method for a CPU. A secondary that takes its deep WFI at hotplug stops the whole machine within seconds on T8140, and macOS takes that WFI only after the sleep commit. So a CPU leaving the kernel parks here, still coherent, with interrupts masked. For a suspend it writes its stop bit at the start of the last mile and takes the deep WFI after the commit; otherwise it goes back through secondary_holding_pen, staying at EL2. Every core loses its power in a real sleep, so after the wake a core still parked, one that never saw its stop, is taken as off and started through the platform's on step, which only writes its start bits. Measured on a J700, though, a sleep committed with a core that missed its stop doesn't come back at all, so the sleep has to refuse for such a core while it still can. The sleep controller's hold does that. The SoC-specific stop, off and on steps come from a platform driver through spin_table_set_power_ops(). Until one registers, hotplug fails as on plain spin-table, and so does a kexec load: the parking ops have a cpu_die, so cpus_are_stuck_in_kernel() no longer catches them, and the kexec would otherwise fail at machine_kexec() with the CPUs still online. Without KEXEC_CORE (for cpu_soft_restart) hotplug is refused too. Other machines keep the ops they have, since only that property or the explicit method selects these. Signed-off-by: Diljit Singh <hey@djdev.me>
…pend T8140's firmware-driven suspend powers the controller down, and the AIC driver has no suspend code at all. Mask every line before the commit so that nothing can end the final WFI, and put the controller's state back after a real power-off. Unmask from the IRQ core's books, since reading the mask registers has not been shown to work. The boot CPU doesn't go through hotplug on the way back, so run its per-CPU setup from resume too, keeping the FIQ state of its existing users. The sleep engine also waits for every CPU to stop, and a raised line ends WFI even with interrupts off at the CPU. macOS masks every line right after the commit. Export that step, its undo and an event read to the sleep controller, which runs them with one CPU up and interrupts off. The syscore ops are only registered on T8140 and only act on a firmware-driven suspend, so nothing changes on other machines or in s2idle. Signed-off-by: Diljit Singh <hey@djdev.me>
Every command asks for an interrupt on its response, and the driver leaves that vector enabled. On T8140 the wake logic watches this queue's line for the PMU's RTC alarm, so our own traffic could end up read as a wake at the commit. macOS clears the same enable bit shortly before it, measured under aura. Mask and clear the vector in the noirq phase of a firmware-driven suspend and poll for responses until resume. After a real power-off also write the vector enables back, since whether they survive hasn't been measured. Only that kind of suspend is affected, so s2idle and other machines see no change. Signed-off-by: Diljit Singh <hey@djdev.me>
The driver refuses every sleep state but s2idle, because nothing routed the alarm as a wake source in any other. A firmware-driven suspend does that itself: on T8140 the alarm is wake source NUB.SPMI0.SW3, and macOS's RTC wakes come back through it with the same alarm registers (measured under aura). The suspend path clears the driver's refusal only when the platform has said it is driving the suspend, so other machines keep refusing. Signed-off-by: Diljit Singh <hey@djdev.me>
A firmware-driven suspend changes domain states behind genpd's back. macOS's wake record on T8140 shows AUTO_ENABLE gone from 22 domains and many others left clock gated: it raises the first group and lowers 30 of the second within 5 ms of a wake. genpd only powers a domain on if it turned it off, so a domain it left on stays wrong. On a resume from such a suspend, put each domain where genpd's books say it is. Follow the books rather than macOS's two lists, since the rule behind those lists hasn't been worked out. Firmware-owned domains stay untouched. Some domains need their driver's order on the way back. For the GPU's that is the target, the bridge, and only then automatic power management, and a resync would raise it with AUTO_ENABLE well before the GPU driver runs. Add apple,sleep-driver-owned for a domain whose driver lowers it before a sleep and brings it back afterwards; the resync leaves it alone. The PMC driver keeps which agents it enabled on each rail and the floors its domains hold, and writes neither again on its own. Restore them from the books in resume_early: a register that kept its value isn't written, and CLPC's sampling starts again only in resume, after this. Only pm_resume_via_firmware() runs any of this, so other paths are unchanged. Signed-off-by: Diljit Singh <hey@djdev.me>
A firmware-driven suspend powers the pin block off, and nothing sets a pin up again: the pin core thinks its state hasn't changed, so reselecting it does nothing, and a pin in a consumer's hands isn't reconfigured by anything. The PCIe host's CLKREQ mux and PERST# are two that need this. Mark each pin the driver writes, save those in suspend_noirq and write back the ones that differ in resume_noirq. Only those: the pins it hasn't configured belong to the firmware or another processor, and on T8140 a write to one raises an SError. Only on controllers whose regmap reaches the hardware, T8140 and T8132, since a cached one would hide the loss. A plain suspend-to-idle does nothing here, and other SoCs are unchanged. Signed-off-by: Diljit Singh <hey@djdev.me>
…d one A firmware-driven sleep powers the cluster down, and it came back in pstate 2 with the driver expecting 1, the suspend state. The first request after the wake then stopped all requests as if another owner held the cluster, and the next suspend failed. Read the state again on resume, before the governor starts. Signed-off-by: Diljit Singh <hey@djdev.me>
A coprocessor can have a second mailbox, the ADT's iop,secure-rtbuddy-proxy, for its secure side. It has the registers of the AOP setup port. Its interrupts aren't described yet, so this variant has none and its consumer polls. Match the generic compatible, since the node isn't specific to one SoC. Firmware that powers itself down for a system sleep needs more from its mailbox: - A way to clear and set bit 0 of the receive control word around the stop, as macOS does. - A check for a free send slot that works with interrupts off, where apple_mbox_send()'s own deadline can't pass. - For an ASCWrap v6 CPU whose stop moves to the sleep's last step, with interrupts off and one CPU up, where neither the lifecycle lock nor runtime PM can be taken: its control word for polling its status, the stop's register writes alone, and a call that settles the books after the sleep without touching the registers, so that the next start goes through. The wait for room to send now ends only on a fatal signal. The freezer signals every user task as a system sleep begins, and a message half sent can break a coprocessor's handshake. Rust bindings cover the receive control and holding and releasing receive. Signed-off-by: Diljit Singh <hey@djdev.me>
A firmware-driven sleep powers the co-processors off, and RTKit had no way to stop one for that or to bring it back without a full reboot of its firmware. This adds what the drivers need, each step measured against macOS: - AP-only power notices. macOS tells the AOP, MTP and SMC about a sleep with the AP power state alone, 0x201 before it and 0x20 after, and leaves them running. A noirq variant polls. - A sleep stop. After the IOP's idle ack macOS waits for the CPU status to read stopped and idle, clears bit 0 of the mailbox's I2A control word, and clears the run bit twice, each time from what it reads back. apple_rtkit_sleep_stop() does that, with a 5 ms poll for the status; on an error the run bit stays set and the firmware still runs. - A polled idle for a stop that can't be taken back. ANS doesn't come back from idle with its power kept, so its idle follows every refusal, with interrupts off and one CPU up. The messages are sent and acked by polling, the syslog and IOReport notifications that want an ack are answered as they come, and OSLOG counters are echoed, below. A counter the receive work still owed, which can't run then, is echoed before the first state request, and whichever side takes it first echoes it, so none is echoed twice. - A warm restart. After a real sleep the firmware restarts with its buffers and rings in place and sends HELLO unasked. Nothing is freed and no endpoint is started again; the endpoint map is echoed as macOS does, with bit 1 added to the first part, and a driver can set what the last part gets back (ANS gets endpoint 0x20 only). apple_rtkit_sleep_return() does that after a sleep that reached the hardware. After one that didn't, a firmware that idled gets -EAGAIN: an idled firmware doesn't wake with its power kept, so it stays down until the next real sleep restarts it. One whose idle failed still runs, and gets 0. apple_rtkit_sleep_return_start() is the first half, for a driver that starts other firmware before waiting, and apple_rtkit_power_cycled_return() starts one its driver power-cycled without a sleep. - Hooks around the AP state change in boot and idle, for a driver that has to answer another port in between. - A polled port that only answers the co-processor: the DCP's secure RTBuddy port, whose HELLO and endpoint map macOS's secure side echoes back unchanged. - OSLOG counter echo. A firmware counts its log writes, holds the next counter until the last is echoed, and with one outstanding takes no AP power notice: a DCP restarted warm refuses its next AP idle, and the MTP and the SMC stopped answering after about eight sleeps. macOS echoes every one, so on T8140 every RTKit device's counters are echoed, and a driver can turn that off. - A crash on a crash log endpoint the firmware never announced. Such a firmware asks for that buffer only when it crashes, at an address it chose. The request was taken for a boot-time one and dropped, so the driver never learned of the crash and kept sending to it: the AOP after a wake, refusing every later sleep. It's reported at once, and the buffer is mapped through the device's IOMMU when that's one contiguous range and acknowledged, so the log is dumped when it comes. Management message 0xc, which follows the IOP's idle ack, is no longer logged as unknown. An adopted session completes its endpoint map at adoption, as its previous owner did that. A handshake given up halfway can leave a firmware started that nothing stops, so RTKit's waits end only on a fatal signal, and during a system sleep not even that. The freezer signals every user task as a sleep begins: on a J700 that cut a display power-on short as a sleep began, left the DCP running while its driver took it as stopped, and every later sleep was refused. Rust bindings cover idle, mailbox off, warm restart and the OSLOG echo. Nothing changes for a driver that doesn't call the new functions. Signed-off-by: Diljit Singh <hey@djdev.me>
A firmware-driven sleep resets every DART. After the first real wake on T8140 every RTKit coprocessor but the DCP timed out: the PMP and SIO restart their firmware in their early resume, and their first DMA went through a DART that had not been put back yet. - For a firmware-driven sleep, suspend and resume in the noirq phase, which every device finishes before any early resume, and after a real sleep write the saved state back even for a DART runtime PM had suspended, which pm_runtime_force_resume() leaves alone. Every other sleep keeps the normal phase, on every SoC: the PCIe-C tunnel DARTs need it, as their host stops the tunnel ports in its suspend_noirq and sets the port clock up again in its resume_noirq. On Apple SoCs only the T8140 sleep driver, later in this series, sets pm_suspend_via_firmware(), from .begin, and it holds until the next suspend, so both halves take the same phase. - Replay the loader's address filter. The PMP's first write after the wake lands in a range the loader programs into its DART's DAPF (the ADT's dapf-instance-0), which a real sleep clears. Take the ranges from apple,dapf-ranges and an optional apple,dapf-r20, and write them back wherever the translations are restored, unless the filter still holds them or is locked. - Write the loader's tunables back where a power-off lost them, from apple,resume-tunable, only the words that differ. The ISP's DARTs need both, as its firmware starts again after the wake. - A consumer-driven save and restore for a DART whose firmware gates itself before the DARTs suspend (the SEP's): apple,sleep-prepare, apple,sleep-restore and apple,sleep-restore-late list the writes around it, and the consumer calls apple_dart_sleep_prepare() and apple_dart_sleep_restore() in its own order. The DART's own callbacks then leave it alone. Its config words go back as the DART had them before the sleep, since Linux's setup of those words differs from macOS's. - GAPF. On T8140 the fabric rejects a write to a display coprocessor's CPU power state while bit 4 of its DART's GAPF slice is set. A display stop that flushes the DART waits for the flush, then clears the bit; it's set again when the streams come back on. apple_dart_display_streams() does macOS's stream writes for a display power cycle. A deep sleep resets the GAPF bank too, so the slice's value is saved at probe and protected with that, and apple_dart_gapf_ready() releases a slice the wake left protected before the coprocessor's CPU domain comes up. - While a display DART's streams are off for a power-down its registers may be unpowered: take each stream's table level from a snapshot made as they went off, and defer TLB invalidations until the streams are back on. apple_dart_power_dark() does the same for a display DART whose domain goes off at runtime while others still map into it: the display subsystem maps every framebuffer into DCPEXT's DART, and a map during DCPEXT's power cycle read a TCR in the unpowered DART. - A DART that runs on its PCIe port's clock, as the T8140 radio port's does, gates itself in its noirq resume after a real sleep, until its host has the port back and calls apple_dart_host_resume(). The first real sleep with the radio host bound took an SError there. - apple_dart_state_save() and apple_dart_state_restore() keep a DART's state across its consumer's own power cycle, outside a system sleep. They return -ENODEV for a device apple-dart isn't bound to. DARTs with none of these properties, and other sleeps, behave as before. Signed-off-by: Diljit Singh <hey@djdev.me>
The SART sits in its coprocessor's power domain, which a firmware-driven suspend powers off, and the driver has no sleep code. A coprocessor that restarts with an empty SART can't reach its firmware's buffers. Save the entries in suspend_late, after the consumers have gone quiet, and write back every one that was in use in resume_noirq, the same or not, as macOS rewrites entries 0 to 7 at every wake. This includes the bootloader's protected entries: Linux doesn't change those, it only puts back what power loss took. macOS leaves unused entries at their power-on flags, and so does this. macOS writes the entries back once more right before ANS's run bit, config first, then address, then size. apple_sart_replay() does that on SARTv3, for the coprocessor's driver to call there, and warns about an entry that doesn't read back. Only a firmware-driven resume restores anything, so suspend-to-idle and every SoC that doesn't advertise deep sleep are unchanged. The SART also has throttle registers, which this doesn't touch since nothing in Linux knows their layout. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in integration/aurora 15f88e1 through aurora-silicon#1208. The conflicts are the glue's kernel-doc and the neo-stability host test's mock: this branch's sleep_ddr is kept next to integration's drd_regs and usb2_phy. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…omains Brings in integration/aurora 15f88e1 through aurora-silicon#1208. The one conflict is the PMGR domain's fields: the sleep records and integration's consumer-switched state are both kept. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…-edac-slpddr-wake Brings in integration/aurora 15f88e1 through aurora-silicon#1208. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…pcie-slpddr Brings in integration/aurora 15f88e1 through aurora-silicon#1208. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in integration/aurora 15f88e1 through aurora-silicon#1208. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…elect Brings in integration/aurora 15f88e1 through aurora-silicon#1208. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…omains Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…-edac-slpddr-wake Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…pcie-slpddr Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…elect Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
niteria
pushed a commit
to niteria/linux
that referenced
this pull request
Nov 10, 2026
…neric Brings in aurora-silicon#1208's aop-bootargs host test fix. No conflicts. Signed-off-by: Diljit Singh <hey@djdev.me>
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.
Deep sleep (suspend-to-RAM) for the MacBook Neo (J700, T8140) with Linux at EL2, as 72 commits on
integration/aurora. The order is dt-bindings first, then the shared infrastructure, the sleep controller, each subsystem's suspend and wake, and the device tree last. SoC facts live in the DT and in match data wherever shared code would otherwise hard-code the J700, so M4 and later machines need their own tables, not code changes.Needs from aura
/chosen/aurora,s2r-mailbox. The CPUs keepspin-tableas their enable method. The mailbox is the only signal. With it, Linux givesspin-tableCPUs the parking ops for power-off and return. Without it nothing in this series binds, the CPUs stay on plain spin-table, and s2idle stays the default.apple,tunable-axi2af,-cio3pll,-clkgen,-phy,-phy-ip-plland-phy-ip-auspmaon the PCIe node, andpcie-applereadsapple,tunable-port,-rc,-rc-gen3and-rc-gen4onpci@0,0. Without the PCIe node's tables the sleep controller refuses every real sleep with ANS stopped ("no PCIe tunables for ANS"). aura#112 has to publish all ten.What a system needs
apple_seploaded before the first sleep. A SEP that iBoot started and no driver has bound refuses every sleep ("sep runs with no driver bound"). Theaurora-seppackage in the Fedora, ALARM and NixOS trees does this. It blacklists the module's alias, andaurora-sep.timer(1 s after boot) startsaurora-sep.service, which runsmodprobe apple_sep. Fedora and ALARM enable the timer through a systemd preset that their image builds apply, and NixOS throughmodules/sep.nix, part of the default module set. A system without that package, or with the service masked, refuses every deep sleep.suspend_set_ops()path, sosystemctl suspendtakes it. The stockmem_sleep_default=s2idleparameter still keeps s2idle. Without aura#117's mailbox the controller logs that once and doesn't bind, so s2idle is the only sleep. Without ANS's APCIe tables every real sleep would refuse, so the controller says so once and leaves s2idle as the default. Distributions need nothing beyond theaurora-septimer they already ship.How it works
The boot object opts in with
/chosen/aurora,s2r-mailboxand nothing else. CPUs whose enable method isspin-tablethen get a parking spin table, so the device tree keeps the method every kernel knows, and a kernel without this series ignores the property. The olderaurora,rvbar-spin-tablemethod selects the same ops. Until a platform driver registers its power ops, the parked CPUs refuse hotplug as plain spin-table does, and kexec refuses the load up front instead of failing later with the CPUs still online..enterthe display goes off first, before the GPU, as on macOS. Then come each coprocessor's last check, such as NVMe's shutdown notification, and then the enclave's sleep request. That request is the very last refusal, because a gated enclave comes back only through a real sleep. After it nothing can refuse, and from the GPU's stop on, a failure goes on to the commit.apple,pcio-templates, switches on and off around each capture.Fitting into integration/aurora
t8140-peripherals.dtsialready describes the SIO, its DART and its mailbox, so the series only names the SIO's register blocks (asc,auxiliary,asc-tail) for the sleep controller.apple,t8140-secure-mailbox) becomes the secure RTBuddy port that the DCP driver polls (apple,t8140-secure-rtbuddy-proxy).apple,pcio-templates, so the GPIO driver needs no change for it.Measured on a J700
These numbers come from test kernels carrying this code. Those kernels also carried test-only switches and debug code, and the later ones two MTP commits that aren't in this series.
Not in this series
wake_reasonin sysfs, the SMC boot-record log,PM_DEBUGinasahi.config, timing lines, and the test-only switcheskeep_debug_link,refuse_lateandignore_stop_cpu.Open
W=1 dtbswarns about it. Moving it means reordering its binding's ranges and the tree together, as a separate change.Review
Two reviews of an earlier version found 25 issues. 24 are fixed in code, each folded into the commit it belongs to or added where it's new. The 25th, the DockChannel host-state order, was right, and only its comment was wrong. The fixes:
iommusare parsed by#iommu-cells.Testing
Image modules dtbswith the asahi profile, Rust on and no compiler warnings, on a build guest that verified clean before and after. Wherever this rebase changed a commit's code, or a later commit changes that code again, I also compiled the changed objects at that commit withW=1and the tip's config. That's#16,#24,#30,#31,#33,#41,#48,#50,#54,#56,#57,#58,#62, and the J700 DTB at#70. All pass. The oneW=1note ist8140_sleep_lower_io()unused at#30and#31, until its caller arrives in#32. Making deep the default touched#30and#32again, andt8140-sleep.ocompiles at both withW=1, with that same note only.udelayin atomic sections, arm64-only includes in the spin table, and a false positive on function-pointer locals.tools/asahi/run-host-tests.pypasses 195 of 195 at the tip, anddt_binding_checkand the DTB schema checks pass. Where a commit changes code that a host test compiles from source (the DCP, ATC and dwc3 slices of the apple-dcp selftests, the PMP bridge indisplay26-tests,mt7932-association-events-testsandrtkit-oslog-tests), the same commit updates that test's stubs. Each of those suites also passes at the commit that changes it.mt7932_bt_pcieagain, so the series' Bluetooth changes build here too.apple_seploaded anddeepselected:keep_debug_linkset so the debug cable's events didn't wake it. Each lasted the full 21 s and woke on the PMU (NUB.SPMI0.SW3), with an empty SMC wake record. Entry took 0.41-1.10 s and resume 1.14-1.17 s. After every wake Wi-Fi pinged, Bluetooth was powered, the USB hub and keyboard were back, NVMe was live, both audio cards were there and eDP was connected, with no crash lines.deepby hand. The change that makes deep the default came after them. On a boot of that tree with nomem_sleep_defaulton the command line,/sys/power/mem_sleepreads2idle [deep]untouched, and the controller logged "deep sleep is the default". One plainsystemctl suspendon a 30 s RTC alarm entered "PM: suspend entry (deep)" and returned 30.4 s later, woken by the PMU with an empty SMC wake record.009c87801b, which brings in upstream's G17 GPU changes, the rebased tree with the same three switches booted on stage2-14.mem_sleepreads2idle [deep]with nomem_sleep_defaulton the command line, andapple_sepwas bound. 6 of 6 sleeps on a 20 s RTC alarm, withkeep_debug_linkset, returned after 20-21 s, woken by the PMU with an empty SMC wake record. Entry took 0.88-1.14 s and resume 0.86-0.88 s. The GPU firmware restarted at every wake with no fault lines, and every device check passed: Wi-Fi ping, Bluetooth, the USB hub, NVMe, both audio cards and eDP, with no crash lines.🤖 Generated with Claude Code