Omarchy 4 on 12" MacBook8,1

Episode three of my ongoing love affair w/ the MacBook Retina, 12-inch, Early 2015: after elementary OS 6 “Odin” (2021) and Clear Linux (2022), the fanless wonder now runs Omarchy 4—Arch + Hyprland, opinionated, fast, tiling. 🤓
The installation itself: smooth and uneventful. The first boot into the installed system: also smooth—right up to the LUKS passphrase prompt, where the built-in keyboard and touchpad were… dead. 💀 Not “load a kernel module” dead like back in the Clear Linux days, but properly dead: external USB keyboard required for everything, and the kernel log drowning in
applespi spi-APP000D:00: SPI transfer timed out
applespi spi-APP000D:00: Error reading from device: -110
spi_master spi1: failed to transfer one message from queue
Turns out I’m not alone: omarchy#1954 and omarchy#2099 describe exactly this, with an impressive list of unsuccessful workarounds (irqpoll, DKMS drivers, older kernels, udev incantations…). But I knew this hardware could do better—it did in 2021 and 2022. So, down the rabbit hole. 🐇
The plot twist: it’s not applespi
After an evening of pair-debugging w/ Claude Code (poking IRQ counters, unbinding PCI devices at runtime, reading way too many kernel logs), the actual root cause turned out to be upstream of the keyboard driver:
The keyboard/touchpad sit on an Intel GSPI controller (00:15.4) that does its transfers through a companion DesignWare DMA engine (00:15.0, dw_dmac_pci, built into the Arch kernel). And on this machine those DMA transfers simply never complete. No DMA completion → no interrupt → timeout, forever.
The fix is gloriously blunt—don’t let that DMA driver register at all:
initcall_blacklist=dw_pci_driver_init
With that single kernel parameter the SPI driver shrugs, logs no DMA channels available, using PIO—and the keyboard and touchpad just work. Add the applespi modules to the initramfs and they work at the LUKS prompt, too. 💪
Sleep, the boss level
Of course it wouldn’t be a 12" MacBook post without a boss level:
- Suspend: deep/S3 sleep wedges the SPI device beyond recovery (only a reboot helps—a known mystery since 2017). Fix: default to
s2idle+ a systemd sleep hook that detaches the applespi stack before sleeping and reattaches it on wake. - Hibernate: a three-headed monster. 🐉 ACPI “platform” mode hangs (→
HibernateMode=shutdown), the image write intermittently freezes with zram swap active and the Broadcom wifi driver loaded (→ both get dropped around hibernation), and the image restore hangs when the boot kernel freezes an active applespi (→ a tiny initramfs hook detaches it right after the passphrase). - Suspend-then-hibernate (added 2026-09-06): after the closed MacBook sat unused for a week and greeted me with a flat battery (s2idle burns ~10% per day here 🪫), the lid switch now triggers
suspend-then-hibernate—two hours of instantly-wakeable s2idle, then an RTC alarm fires and the machine hibernates itself. One gotcha for fellow hook writers: systemd runs the system-sleep hooks around each phase, so naively restoring the wifi driver at the transition wake races its own re-unload against the async firmware probe—kernel oops, wifi dead until reboot. 🙈 The sleep hook is phase-aware now and simply stays detached between the phases. (How it tells the phases apart got revised on 2026-10-03—see the update below.)
All of it now works. Suspend, wake, type. Hibernate, power off, power on, passphrase—and the session is back, wifi and all. Close the lid on battery and the machine naps for two hours, then tucks itself into hibernation. On a machine two GitHub issues had written off. 😎
The script
Everything is bundled into one idempotent script—kernel parameter, initramfs modules + hook, sleep hook, hibernate config, rebind service:
Tested on: MacBook8,1, Omarchy 4.0.0, kernel 7.1.8/7.1.9-arch1. If you own one of these (or the 9,1 sibling), please give it a spin and report back in omarchy#1954, where I posted a summary of these findings—once a few more machines confirm, I’ll offer a proper PR to Omarchy (there’s precedent: Omarchy already ships an NVMe suspend fix for MacBooks).
Update 2026-09-06: that took less than two weeks. 🚀 Three more MacBook8,1s confirmed the fix in the issue thread, one reader contributed register-level measurements proving the DMA engine never services the SPI requests, an upstream kernel quirk is in the works—and there’s now a first-party PR, omarchy#9735, shipping the PIO switch on MacBook8,1 out of the box. The script meanwhile grew the suspend-then-hibernate setup described above.
Update 2026-10-03: and then the hook bit me. 🙈 Battery nearly empty, UPower pulled the emergency brake w/ a suspend-then-hibernate (mid-omarchy-update, inhibitors be damned), I plugged in the charger twelve seconds later—and woke up to a MacBook w/o keyboard, touchpad and wifi. Forced reboot. The culprit: my “did the RTC alarm fire?” check. systemd’s hibernate timer never shows up in the RTC wakealarm, so that check was true on every wake, and any early wake from the suspend phase was mistaken for the transition to hibernate → drivers stay detached, and no hibernate ever follows. The revised script
- decides via
$SYSTEMD_SLEEP_ACTION(systemd tells the hook which phase it is in) plus the time actually slept, - adds a safety-net unit that runs after every completed sleep operation and reattaches whatever is still missing—so a misjudging hook can no longer cost me the keyboard,
- and makes UPower hibernate straight away on critical battery, instead of two hours of s2idle on fumes.
If you installed the script before today: please re-run the current version from the gist.
…and a caveat, same evening: the early-wake fix held up, but the first real two-hour nap on the freshly updated kernel (7.2.5-omarchy) ended in a hang right at the hibernate step—no image written, hard reset. 😬 I then spent the evening trying to make it happen again: five more hibernates (plain, right after a short suspend, lid open, lid closed) all went through cleanly. So for now it’s filed under “intermittent, or something about the long suspend”, and auto-hibernate stays on. The script’s hook now keeps the kernel console alive and chatty during the hibernate step only, so the next hang at least says where it died. If your MacBook8,1 does the same, I’d love to hear about it in omarchy#1954.
Known quirks
- Audio is only available via headphone jack—speakers and microphone are not working. Same as on elementary OS 6 and Clear Linux back then; some things never change. 🎧
- Webcam: the built-in FacetimeHD is not working (I got it running on elementary OS 6 once, after A LOT of tinkering—haven’t bothered this time). Instead I clip on an Opal Tadpole, which works out of the box. 📹
- With a USB/Thunderbolt hub plugged in (a CalDigit USB-C SOHO Dock in my case), the Mac firmware auto-powers-on a few seconds after hibernate’s poweroff. Unplug before hibernating. 🤷
s2idledrains the battery faster than deep sleep (~10% per day—ask me how I know 🪫)—the price of a working keyboard. That’s exactly what the suspend-then-hibernate setup above is for.- Hibernating at the end of a long suspend-then-hibernate hung once on kernel 7.2.5-omarchy and hasn’t been reproduced since (see the 2026-10-03 update). Open. 🔍
- Hibernation needs real disk-backed swap; zram alone won’t do. Subtle trap (thanks @jpumfrey for flagging it): a swapfile with negative priority (
pri=-2) is silently skipped by systemd when picking a hibernation target—it just resumes as if nothing happened. Give it a non-negative priority below zram’s. - The other LPSS peripherals (I2C/UART) also lose DMA and run in PIO mode. No observable downside.
The 2015 MacBook: still not done. ❤️