Skip to content

t600x: PMU RTC wake alarm (SPMI notify at 192, Maverick event clear, DT) - #351

Open
CoreyH wants to merge 5 commits into
omacom:integration/aurorafrom
CoreyH:pmu-alarm-t600x
Open

CoreyH wants to merge 5 commits into
omacom:integration/aurorafrom
CoreyH:pmu-alarm-t600x

Conversation

@CoreyH

@CoreyH CoreyH commented Nov 4, 2026

Copy link
Copy Markdown

This makes the PMU's RTC wake alarm work on T600x (M1 Pro/Max/Ultra): rtcwake, and timed wakes from s2idle. It builds on the rtc-macsmc alarm from #274 (T6030) and on 2037f35, the commit that lets SPMI peripheral interrupts wake the system.

Patches

  1. spmi: apple: T6000's controller has 192 peripheral interrupts, so its own events start at 192, not 256. Notify, "reply ready", is the first of them. Without this, once a T600x device tree gives nub-spmi0 its interrupt, every SPMI transfer times out and the PMU is unreachable.
  2. dt-bindings: rtc: apple,smc-rtc: an optional fifth cell, rtc_alarm_event.
  3. rtc: macsmc: clear a Maverick PMU's fired alarm in both places. Maverick PMUs (M1 Pro, M2 Pro) latch a fired alarm in 0x14c0, which clears when 1 is written to it, as well as in the control byte, whose bit 1 clears when written as 0. Until both are cleared, every later alarm is lost.
  4. rtc: macsmc: arm the alarm one second later when the tick offset's fraction is under 1/32 s.
    • The time is CLKM plus the offset, and CLKM lags the PMU's counter by about 6 ms.
    • When the offset's fraction is under that lag (as after the PMU loses power and the offset is 0), the alarm fires while the time still reads the second before.
    • The RTC core then re-arms the same second, and the alarm fires five or six times.
  5. arm64: dts: apple: t600x: describe the alarm:
    • nub-spmi0 as an interrupt controller on AIC 636;
    • the alarm's nvmem cells, at the offsets in macOS's device tree: info-rtc_alarm_ctrl 0x1401, info-rtc_alarm_offset 0x140c, info-rtc_alarm_event 0x14c0, info-rtc_irq_mask_offset 0x14c2;
    • the rtc node's interrupt and wakeup-source.

Testing on J314s (M1 Pro), 7.2.11.aurora14:

  • On device: the changes ran as out-of-tree modules plus a live device-tree changeset, the same description patch 5 adds (compared property by property).
    • Awake alarms fire on their second.
    • Every timed s2idle sleep that nothing else woke ended on the alarm: 6 of 6 on aurora9, 3 of 3 on aurora14, plus a 900 s timed drain test.
  • Patch 4: with the offset's fraction cleared, three awake alarms raised 6, 5 and 5 interrupts before the patch and 1, 1 and 1 after. Kprobe traces show the RTC core re-arming the same second.
  • Builds and checks:
    • Patches 1, 3 and 4 build warning-free with W=1 (LLVM).
    • All six T600x DTBs compile with no new dtc warnings.
    • dt-doc-validate and dt-validate pass, and checkpatch.pl --strict is clean.
  • Not yet: a boot of a kernel built from this branch. I'm not on the vouch list, so it needs ci:build from a maintainer. With the package, I'll boot it on J314s and report back here.
  • Untested on M1 Max and M1 Ultra, which share t600x-die0.dtsi.

Credit: JJRPF/Aurora-DeepIdle first found the Maverick PMU's RTC register map, and that it needs both clears, on an M2 Pro. This J314s's own macOS device tree confirms the same layout.

The patches were written with Claude's help (each carries Assisted-by:); I've reviewed and tested them, and they carry my Signed-off-by.

…rupts

The controller's interrupt words hold the peripheral interrupts first and
the controller's own events after them, "notify" (the RX FIFO is no
longer empty) first. The driver puts notify at 256, after T6030's 256
peripheral interrupts. T6000's nub-spmi0 has 192: macOS's device tree
numbers its events from 0xc0, and with a reply left in the FIFO the
pending bit is 192 (ack word 6, bit 0).

So once a T6000 device tree gives nub-spmi0 its interrupt, the driver
unmasks bit 256 and waits for a notify that never comes: every SPMI
read times out and the PMU becomes unreachable. Take the peripheral
interrupt count from the match data, 192 for apple,t6000-spmi and 256
for the rest, and derive the notify position, the irq domain's bounds,
the handler's scan and the mask/ack setup from it.

Tested on J314s (M1 Pro): with nub-spmi0's interrupt described, SPMI
transfers complete in 40-60 us and the PMU's RTC alarm wakes the system
from s2idle.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Corey Henderson <nuwanda@gmail.com>
The Maverick PMUs (the M1 Pro's and the M2 Pro's) latch a fired alarm in
an event byte as well as in the alarm control byte, and keep the alarm
interrupt silent until the event byte is cleared by writing 1 to it.
Allow a fifth cell, rtc_alarm_event, after the four alarm cells.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Corey Henderson <nuwanda@gmail.com>
…gister

The Maverick PMUs (the M1 Pro's and the M2 Pro's) latch a fired alarm in
two places: bit 1 of the alarm control byte and an interrupt status
byte. Writing the control byte back with bit 1 set, which is how T6030's
PMU acknowledges the event, leaves the bit set here. It clears when it
is written as 0. The status byte clears when 1 is written to it, and
until then the PMU keeps the alarm interrupt silent, so every later
alarm would be lost.

With the optional rtc_alarm_event cell, clear the status byte before
arming and after firing. The control byte's bit 1 then clears where it
is written as 0. Without the cell, nothing changes.

Tested on J314s (M1 Pro): awake alarms fire on their second and leave
both registers clear, and every timed s2idle sleep that nothing else
woke ended on the alarm.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Corey Henderson <nuwanda@gmail.com>
The time is the SMC's CLKM plus a tick offset, but the PMU's alarm
compares against the PMU's own counter, which CLKM lags by a few ms (5.5
to 6.3 ms on J314s, once 14 to 24 ms right after boot). When the tick
offset's fraction is under that lag, the alarm fires while the time
still reads the second before. The RTC core then finds its timer not
yet expired and sets the same second again, the PMU fires again at
once, and so on until the time catches up: five or six interrupts and a
burst of SPMI traffic for one alarm. That is the state after the PMU
loses power, which leaves the offset at 0.

When the offset's fraction is under 1/32 s, arm the alarm one counter
second later. It then fires within the set second, and read_alarm still
reports the time that was set. For fractions between the lag and 1/32 s
the alarm comes up to a second late, which the tick-offset path already
allowed. The seconds-offset path is unchanged.

Tested on J314s (M1 Pro) with the offset's fraction cleared: three awake
alarms raised one interrupt each, where they raised 6, 5 and 5 before.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Corey Henderson <nuwanda@gmail.com>
The Maverick PMU on nub-spmi0 has an RTC alarm (macOS's device tree:
info-rtc_alarm_ctrl 0x1401, info-rtc_alarm_offset 0x140c,
info-rtc_alarm_event 0x14c0, info-rtc_irq_mask_offset 0x14c2). It raises
peripheral interrupt 2 of nub-spmi0, whose own interrupt is AIC 636.
Describe nub-spmi0 as an interrupt controller and the alarm's four
cells, and give the SMC's rtc node the cells, the alarm interrupt and
wakeup-source.

This needs the SPMI controller to put T6000's notify event at 192
(previous patches); without it, describing the interrupt breaks every
SPMI transfer.

Tested on J314s (M1 Pro) with the same description applied to the live
device tree: the alarm fires on its second, and it wakes the system
from s2idle.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Corey Henderson <nuwanda@gmail.com>
@p4ulcristian

Copy link
Copy Markdown

Tested this series' rtc-macsmc on a third PMU: Stowe, on a J413 (MacBook Air 13" M2), 7.2.11.aurora16. It works unchanged with a T8112 description added. That commit is in #454, on top of this branch; fold it in here if that is easier for you.

What Stowe does, from reads and writes by hand through the nvmem device:

  • Cells at macOS's info-rtc_alarm_ctrl 0xf800, info-rtc_alarm_offset 0xf808, info-rtc_alarm_event 0xf80c, info-rtc_irq_mask_offset 0xf80e; alarm on peripheral interrupt 2 of nub-spmi0 (AIC 384). The controller has 256 peripheral interrupts, so patch 1 isn't involved.
  • A fired alarm latches in the event byte and in the control byte's event bit, and the enable bit clears (0x41 → 0x03).
  • Writing 1 to the event byte clears both latches. Writing the control byte back clears neither, with the event bit as 1 or as 0. So on Stowe the control byte's event bit goes with the event-byte write, not with writing it as 0 as the Maverick comment says; your sequence covers both.

Results: 3 of 3 awake alarms raised one interrupt each, and 19 of 19 timed s2idle sleeps ended on the alarm (18 at 20–30 s, 1 at 3600 s). I did not look for the repeated-interrupt case patch 4 fixes; I saw one interrupt per alarm in all 3 awake runs.

🤖 Generated with Claude Code

@CoreyH

CoreyH commented Nov 12, 2026 •

Copy link
Copy Markdown
Author

Thank you for testing this on a third PMU, and for writing down how Stowe behaves.

  • t8112: PMU RTC wake alarm (M2), on top of #351 #454: I'd keep it as your own PR on top of this one. It's your hardware and your testing, and I can't check a T8112 description here. Rebasing once this merges, as you planned, works for me.
  • The event bit on Stowe: noted that it clears with the event-byte write and not by writing it as 0. The comment in patch 3 describes what I saw on the Maverick PMU of a J314s; since the sequence does both, I'll leave the code as it is and widen the comment's wording if a maintainer asks for a respin.
  • Repeated interrupts (patch 4): on the J314s they show only when the tick offset's sub-second fraction is smaller than the few ms by which CLKM lags the PMU's counter, which is the state after the PMU loses power (an offset of 0). With any other offset one interrupt per alarm is what I'd expect too, so three clean awake alarms don't rule it out on Stowe. The patch changes nothing unless the fraction is under 1/32 s.

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