Posted 5 mins read
tl;dr: on this 2019 T2 MacBook Pro, suspend/resume became usable by preserving the working brcmfmac Wi-Fi recovery path, reloading the T2 Touch Bar stack, and adding a Bluetooth-only hci_bcm4377 PCI reset plus bluetoothd restart after resume.

I have been running Omarchy on a 2019 Intel MacBook Pro with Apple's T2 chip. It is close enough to being a great little Linux laptop that the remaining problems become extra annoying.

The last rough edge was suspend/resume. The machine could sleep and wake, but some hardware would come back in a weird half-alive state. Touch Bar needed help. Wi-Fi needed help. Bluetooth looked alive, but devices would not reliably reconnect.

This post is not a universal T2 guide. It is the exact set of local fixes that made this machine usable.

The Machine

The system at the time of writing:

Host:   mbp2019
Kernel: 7.0.10-arch1-Watanare-T2-2-t2
Distro: Arch Linux with Omarchy
Model:  2019 Intel MacBook Pro with Apple T2

The important hardware mapping from lspci -nnk:

01:00.0 Broadcom BCM4377b Wireless Network Adapter [14e4:4488]
        Kernel driver in use: brcmfmac

01:00.1 Broadcom BRCM4377 Bluetooth Controller [14e4:5fa0]
        Kernel driver in use: hci_bcm4377

02:00.1 Apple T2 Bridge Controller [106b:1801]
        Kernel driver in use: apple-bce

06:00.0 Intel JHL7540 Thunderbolt 3 USB Controller [8086:15ec]
        Kernel driver in use: xhci_hcd

The first two lines are the trap. Wi-Fi and Bluetooth sit next to each other, but they are different PCI functions with different drivers:

0000:01:00.0 -> Wi-Fi      -> brcmfmac
0000:01:00.1 -> Bluetooth -> hci_bcm4377

Treating them as one thing is a good way to fix one problem while breaking another.

The Base T2 Pieces

This machine uses the arch-mact2 repository in /etc/pacman.conf:

[arch-mact2]
Server = https://github.com/NoaHimesaka1873/arch-mact2-mirror/releases/download/release
SigLevel = Never

Wi-Fi uses iwd, with this Broadcom quirk in /etc/iwd/main.conf:

[DriverQuirks]
SaeDisable=brcmfmac

The Touch Bar is handled by tiny-dfr. In my case, tiny-dfr.service, bluetooth.service, iwd.service, and the two custom suspend/resume units are all enabled.

The Problem After Resume

The useful failure mode was this:

  • The machine resumed.
  • The Touch Bar needed the usual T2 module dance and USB re-enumeration.
  • Wi-Fi needed the existing brcmfmac recovery path.
  • Bluetooth was powered on and visible in bluetoothctl show, but paired devices would not reliably reconnect.

The Bluetooth logs had errors like:

Reconnecting services failed: Device or resource busy
Connection timed out
Connection refused

Restarting random networking services is tempting here. Do not do that first. On this machine, Wi-Fi was already working. The fix needed to be Bluetooth-only.

Testing Bluetooth Without Suspending

Before putting anything into a resume hook, I tested the Bluetooth recovery manually:

sudo bash -c 'echo 0000:01:00.1 > /sys/bus/pci/drivers/hci_bcm4377/unbind; sleep 2; echo 0000:01:00.1 > /sys/bus/pci/drivers/hci_bcm4377/bind'
sudo systemctl restart bluetooth.service

That brought Bluetooth back immediately.

That was the important clue. The right fix was not to disturb Wi-Fi. It was to preserve the working Wi-Fi recovery and add the same narrow Bluetooth reset to the resume service.

Suspend Unit

This is /etc/systemd/system/suspend-fix-t2.service on my machine:

[Unit]
Description=Fix T2 suspend
Before=sleep.target

[Service]
Type=oneshot
ExecStart=-/usr/bin/bash -c 'if [ -L /sys/bus/pci/devices/0000:06:00.0/driver ]; then echo 0000:06:00.0 > /sys/bus/pci/drivers/xhci_hcd/unbind; fi'
ExecStart=-/usr/bin/bash -c 'if [ -L /sys/bus/pci/devices/0000:01:00.0/driver ]; then echo 0000:01:00.0 > /sys/bus/pci/drivers/brcmfmac/unbind; fi'
ExecStart=-/usr/bin/bash -c 'if [ -L /sys/bus/pci/devices/0000:01:00.1/driver ]; then echo 0000:01:00.1 > /sys/bus/pci/drivers/hci_bcm4377/unbind; fi'
ExecStart=/usr/bin/systemctl stop tiny-dfr
ExecStart=-/usr/bin/rmmod -f hid_appletb_kbd
ExecStart=-/usr/bin/rmmod -f hid_appletb_bl
ExecStart=-/usr/bin/rmmod -f appletbdrm
ExecStart=-/usr/bin/rmmod -f apple_bce

[Install]
WantedBy=sleep.target

The leading - makes systemd ignore failures for that command. That matters because a device or module may already be absent depending on the current state.

Resume Unit

This is /etc/systemd/system/resume-fix-t2.service:

[Unit]
Description=Reload T2 modules after resume
After=suspend.target

[Service]
Type=oneshot
ExecStart=/usr/bin/sleep 4
ExecStart=/usr/bin/modprobe apple_bce
ExecStart=/usr/bin/sleep 4
ExecStart=/usr/bin/modprobe hid_appletb_bl
ExecStart=/usr/bin/sleep 2
ExecStart=/usr/bin/modprobe hid_appletb_kbd
ExecStart=/usr/bin/sleep 4
ExecStart=-/usr/bin/bash -c "test -e /sys/bus/usb/devices/5-6/bConfigurationValue && echo 0 > /sys/bus/usb/devices/5-6/bConfigurationValue"
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/bash -c "test -e /sys/bus/usb/devices/5-6/bConfigurationValue && echo 2 > /sys/bus/usb/devices/5-6/bConfigurationValue"
ExecStart=/usr/bin/sleep 3
ExecStart=-/usr/bin/systemctl restart tiny-dfr
ExecStart=-/usr/bin/bash -c 'if [ ! -L /sys/bus/pci/devices/0000:06:00.0/driver ]; then echo 0000:06:00.0 > /sys/bus/pci/drivers/xhci_hcd/bind; fi'
ExecStart=-/usr/bin/bash -c 'if [ ! -L /sys/bus/pci/devices/0000:01:00.0/driver ]; then echo 0000:01:00.0 > /sys/bus/pci/drivers/brcmfmac/bind; fi'
ExecStart=-/usr/bin/bash -c 'if [ ! -L /sys/bus/pci/devices/0000:01:00.1/driver ]; then echo 0000:01:00.1 > /sys/bus/pci/drivers/hci_bcm4377/bind; fi'
ExecStart=-/usr/bin/systemctl restart bluetooth
ExecStart=-/usr/bin/bash -c 'sleep 8; networkctl status wlan0 --no-pager | /usr/bin/grep -q "State: routable" || /usr/bin/systemctl restart iwd'

[Install]
WantedBy=suspend.target

The Touch Bar USB path is machine-specific. Mine is 5-6. Yours may not be.

To find it, look for product ID 8302:

for dev in /sys/bus/usb/devices/*/; do
  printf "%s: " "$dev"
  cat "$dev/idProduct" 2>/dev/null || true
done

If your Touch Bar path is different, replace 5-6 in the resume service.

Enable The Units

After writing the files:

sudo systemctl daemon-reload
sudo systemctl enable suspend-fix-t2.service resume-fix-t2.service
systemd-analyze verify /etc/systemd/system/suspend-fix-t2.service /etc/systemd/system/resume-fix-t2.service

Then test the whole flow:

systemctl suspend

After waking:

systemctl status bluetooth --no-pager
systemctl status iwd --no-pager
bluetoothctl show
networkctl status wlan0 --no-pager

Useful logs:

journalctl -u suspend-fix-t2.service -u resume-fix-t2.service -b --no-pager
journalctl -u bluetooth.service -b --no-pager
journalctl -u iwd.service -b --no-pager

What Finally Worked

The working recipe was boring, which is usually a good sign:

  • Keep the existing brcmfmac Wi-Fi recovery because Wi-Fi needed it and it worked.
  • Add the Bluetooth PCI function separately with hci_bcm4377.
  • Restart bluetooth.service after binding Bluetooth again.
  • Reload the T2 / Touch Bar modules in order.
  • Re-enumerate the Touch Bar USB device before restarting tiny-dfr.

The key lesson: do not fix Bluetooth by poking the Wi-Fi driver, and do not fix Wi-Fi by restarting the whole world. On this machine, the reliable path was narrow and boring: reset exactly the device that failed, then restart exactly the service that manages it.

Thanks to the folks in the Omarchy and t2linux discussions who documented the Touch Bar side of this. The Bluetooth bit here is just the extra piece I needed for this specific 13-inch 2019 machine.