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_pci 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/systemd/system. 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.

Tip

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/udev/rules.d (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 Wanted­By.

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_pci driver.