Fixing Hibernation Issues with ath11k_pci
Wi-Fi Driver
Certain laptop Wi-Fi cards made by Qualcomm are plagued by a rather unfortunate issue when using Linux: After the laptop resumes from sleep or hibernation, the Wi-Fi card is unusable. This occurs because the ath11k
driver cannot handle system suspend. Fortunately a workaround exists.
Hibernation
Since the problem is caused by the driver not being able to survive hibernation, the solution is to unload it before the system hibernates and re-load it after it resumes. To do so, you can create a systemd unit for each of these two events. Systemd units are configured in /etc
. First make sure the driver gets unloaded before hibernation:
[Unit]
Description=Suspend: rmmod ath11k_pci
Before=suspend-then-hibernate.target hibernate.target hybrid-sleep.target
[Service]
Type=simple
ExecStart=/usr/bin/sh -c 'if (lsmod | grep -wq ath11k_pci); then \
logger -p user.info "Unloading ath11k_pci kernel module before suspend."; \
rmmod ath11k_pci; fi'
[Install]
WantedBy=suspend-then-hibernate.target hibernate.target hybrid-sleep.target
Then create a second unit to re-load the driver after the system resumes from hibernation:
[Unit]
Description=Resume: modprobe ath11k_pci
After=suspend-then-hibernate.target hibernate.target hybrid-sleep.target
[Service]
Type=simple
ExecStart=/usr/bin/sh -c 'if ! (lsmod | grep -wq ath11k_pci); then \
logger -p user.info "Loading ath11k_pci kernel module after suspend."; \
modprobe ath11k_pci; fi'
[Install]
WantedBy=suspend-then-hibernate.target hibernate.target hybrid-sleep.target
Finally, run systemctl enable
for the two units. Now you should be able to hibernate and resume without the Wi-Fi card breaking.
If you’re using NetworkManager, you might need to restart it after the driver is re-loaded. For example:
ExecStart=/usr/bin/sh -c 'if ! (lsmod | grep -wq ath11k_pci); then \
logger -p user.info "Loading ath11k_pci kernel module after suspend."; \
modprobe ath11k_pci; \
sleep 3; \
systemctl restart NetworkManager.service; fi'
Stable Network Interface Name
Usually, the Wi-Fi network interface is named wlan0
. However, when the driver is re-loaded, the Wi-Fi card gets detected as a new device, and the network interface name will have a different number at the end. If you want the name to stay the same, you can create a udev rule that forces a specific interface name.
You’ll need to find the MAC address of your Wi-Fi card (using ip a
for example). Then, create a new udev rule in /etc
(make sure the file name ends with .rules
):
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="de:ad:be:ef:01:23", NAME="wlan0"
Sleep
To resolve sleep issues, you can extend the systemd units that hook into hibernation by adding sleep.target
to Before
/After
and WantedBy
.
Personally, I think there’s even better approach. Set the following kernel parameter:
acpi.ec_no_wakeup=1
This kernel parameter ignores wakeup events sent by the embedded controller of the computer, which reduces battery drain on some modern systems that no longer support the S3 sleep state. As a side-effect, it also resolves broken Wi-Fi after resuming from sleep without needing to unload and re-load the ath11k
driver.