[WORKAROUND] xHCI host controller not responding at resume after suspend

I am a new owner of a Framework Desktop and running Arch Linux (move my disk over from my previous machine - no new installation).

The issue that I am facing is that the Keyboard + Mouse + USB WebCam are dead after resume, although the resume process finished as I can see my standard SDDM login screen.

The issue might be related to the fact that all those 3 USB devices are actually connected to my Dell U4924DW monitor and then there is only a single USB cable running from the monitor to the Framework, same as I had with my old machine (an ASRock DeskMini X300 btw.).

The USB cable actually is a USB-A (Framework) to USB-C (monitor).

Related messages that I am getting at resume are:

kernel: xhci_hcd 0000:c2:00.4: xHCI host not responding to stop endpoint command
kernel: xhci_hcd 0000:c2:00.4: xHCI host controller not responding, assume dead
kernel: xhci_hcd 0000:c2:00.4: HC died; cleaning up
kernel: usb 2-1: PM: dpm_run_callback(): usb_dev_resume returns -22
kernel: usb 1-1: PM: dpm_run_callback(): usb_dev_resume returns -22
kernel: usb 2-1: PM: failed to resume async: error -22
kernel: usb 1-1: PM: failed to resume async: error -22

After some fiddling, the only workaround for now seems to be to re-bind the XHCI controller:

echo -n "0000:c2:00.4" | tee /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:c2:00.4" | tee /sys/bus/pci/drivers/xhci_hcd/bind

Is this a known issue?

2 Likes

Somehow didn’t find this post which contains the same workaround (rebind controller):

1 Like

OK, now I just upgraded my Arch and Linux kernel 6.18.2 came in:

[2025-12-23T20:26:17+0100] [ALPM] upgraded linux (6.18.1.arch1-2 -> 6.18.2.arch2-1)

Immediately I ran some tests and what should I say: so far everything runs totally smooth. Even some annoying issues with my keyboard LEDs sometimes not working at boot or resume from suspend seem to be fixed.

It’s a little bit early to say anything final but so far no more workaround required.

Tested also different length of sleeps (a few seconds to multiple hours) and with the monitor that brings the USB hub kept on (going into standby) or turned off.

An additional thing I immediately noticed after the upgrade was this:

I have set my fan to “always on” due to the PSU fan issue to keep that one silent. However, before the kernel upgrade I also noticed that the fan wouldn’t stop spinning even when the system was in “sleep”.

Now, also the fan stops, so in the end it could all have been an issue with the suspend itself, not the resume process.

1 Like

Apparently, this morning - after a long sleep, the issue just came back with the exact same errors.

So IDK what made it work yesterday, maybe it’s a timing issue of things inside.

Since the issue only occurs in around 1 out of 3 times, I’ve added a poor mans detection of the issue to my script during resume, so that when the host controller did not die, it is also not forced to re-initialize everything:

#!/usr/bin/env bash

time_check="$(date --iso-8601=seconds -d '-30 seconds')"

quirks_enabled="$(sed -n -E 's/^QUIRKS_ENABLED\s*=\s*(true|false).*/\1/p' /etc/default/framework-quirks 2>/dev/null)"
[ -n "${quirks_enabled}" ] || quirks_enabled="true"

log() {
  printf "[FrameworkDesktop] %s: %s\n" "$1" "$2"
}

if [ "${quirks_enabled}" != "true" ]; then
  log "config" "quirks disabled"
  exit 0
fi

device_path="$(sed -n -E 's/^DEVICE_PATH\s*=\s*(.+)/\1/p' /etc/default/framework-quirks 2>/dev/null)"
[ -n "${device_path}" ] || device_path="c2:00.4"


suspend() {
  # log "suspend" "unbind xHCI start"
  log "suspend" "no action"
}

resume() {
  sleep 1
  xhci_dead="$(journalctl --output cat --no-pager -b -k --since "${time_check}" --grep "${device_path}" -n 5 | grep -F 'xHCI host controller not responding')"
  if [ -n "${xhci_dead}" ]; then
    log "resume" "rebind xHCI"
    echo -n "0000:${device_path}" > /sys/bus/pci/drivers/xhci_hcd/unbind
    sleep 2
    echo -n "0000:${device_path}" > /sys/bus/pci/drivers/xhci_hcd/bind
  else
    log "resume" "xHCI rebind not needed"
  fi
}

case "$1" in
  pre)
    suspend "$2"
    ;;
  post)
    resume "$2"
    ;;
esac

These are my recent sleep cycles where you can see that also the duration of sleep does not matter:

2025-12-25T16:02:31+00:00 purgatori systemd-sleep[170008]: [FrameworkDesktop] suspend: no action
2025-12-25T22:56:30+00:00 purgatori systemd-sleep[170091]: [FrameworkDesktop] resume: rebind xHCI
2025-12-25T23:06:08+00:00 purgatori systemd-sleep[171096]: [FrameworkDesktop] suspend: no action
2025-12-25T23:06:38+00:00 purgatori systemd-sleep[171193]: [FrameworkDesktop] resume: rebind xHCI
2025-12-25T23:17:03+00:00 purgatori systemd-sleep[172069]: [FrameworkDesktop] suspend: no action
2025-12-25T23:17:13+00:00 purgatori systemd-sleep[172145]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-25T23:19:09+00:00 purgatori systemd-sleep[172500]: [FrameworkDesktop] suspend: no action
2025-12-25T23:19:31+00:00 purgatori systemd-sleep[172553]: [FrameworkDesktop] resume: rebind xHCI
2025-12-25T23:25:27+00:00 purgatori systemd-sleep[173257]: [FrameworkDesktop] suspend: no action
2025-12-25T23:25:42+00:00 purgatori systemd-sleep[173311]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-25T23:26:36+00:00 purgatori systemd-sleep[173598]: [FrameworkDesktop] suspend: no action
2025-12-25T23:26:50+00:00 purgatori systemd-sleep[173658]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-25T23:27:08+00:00 purgatori systemd-sleep[173867]: [FrameworkDesktop] suspend: no action
2025-12-25T23:27:31+00:00 purgatori systemd-sleep[173921]: [FrameworkDesktop] resume: rebind xHCI
2025-12-25T23:46:03+00:00 purgatori systemd-sleep[175212]: [FrameworkDesktop] suspend: no action
2025-12-26T08:25:25+00:00 purgatori systemd-sleep[175290]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-26T09:44:36+00:00 purgatori systemd-sleep[182267]: [FrameworkDesktop] suspend: no action
2025-12-26T20:05:10+00:00 purgatori systemd-sleep[182364]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-26T20:23:32+00:00 purgatori systemd-sleep[183781]: [FrameworkDesktop] suspend: no action
2025-12-26T20:40:44+00:00 purgatori systemd-sleep[183892]: [FrameworkDesktop] resume: rebind xHCI
2025-12-27T00:24:15+00:00 purgatori systemd-sleep[206115]: [FrameworkDesktop] suspend: no action
2025-12-27T09:43:07+00:00 purgatori systemd-sleep[206177]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-27T11:13:57+00:00 purgatori systemd-sleep[216089]: [FrameworkDesktop] suspend: no action
2025-12-27T11:14:40+00:00 purgatori systemd-sleep[216100]: [FrameworkDesktop] resume: xHCI rebind not needed
2025-12-27T11:17:17+00:00 purgatori systemd-sleep[216584]: [FrameworkDesktop] suspend: no action
2025-12-27T11:17:28+00:00 purgatori systemd-sleep[216640]: [FrameworkDesktop] resume: xHCI rebind not needed

I wish there was a way to actually listen on that kernel event and trigger the quirk on-demand.

1 Like

Since the PCI device path is not stable when enabling/disabling WiFi/BT or adding/removing an NVMe, I needed to modify the detection and now I get the actual device from the kernel message output in my updated script:

#!/usr/bin/env bash

time_check="$(date --iso-8601=seconds -d '-30 seconds')"

quirks_enabled="$(sed -n -E 's/^QUIRKS_ENABLED\s*=\s*(true|false).*/\1/p' /etc/default/framework-quirks 2>/dev/null)"
[ -n "${quirks_enabled}" ] || quirks_enabled="true"

log() {
  printf "[FrameworkDesktop] %s: %s\n" "$1" "$2"
}

if [ "${quirks_enabled}" != "true" ]; then
  log "config" "quirks disabled"
  exit 0
fi

device_path="$(sed -n -E 's/^DEVICE_PATH\s*=\s*(.+)/\1/p' /etc/default/framework-quirks 2>/dev/null)"
[ -n "${device_path}" ] || device_path="auto"


suspend() {
  log "suspend" "no action"
}

resume() {
  sleep 2
  local xhci_dead=""
  if [ "${device_path}" = "auto" ]; then
    xhci_dead="$(journalctl --output cat --no-pager -b -k --since "${time_check}" --grep 'xHCI host controller not responding' -n 1 | sed -n -E 's/^.*xhci_hcd\s+([0-9]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9]):\s+xHCI host controller not responding.*$/\1/p')"
    [ -z "${xhci_dead}" ] || device_path="${xhci_dead}"
  else
    xhci_dead="$(journalctl --output cat --no-pager -b -k --since "${time_check}" --grep "${device_path}" -n 5 | grep -F 'xHCI host controller not responding')"
  fi

  if [ -n "${xhci_dead}" ]; then
    log "resume" "rebind xHCI (device: ${device_path})"
    echo -n "${device_path}" > /sys/bus/pci/drivers/xhci_hcd/unbind
    sleep 2
    echo -n "${device_path}" > /sys/bus/pci/drivers/xhci_hcd/bind
  else
    log "resume" "xHCI rebind not needed (device: ${device_path})"
  fi
}

case "$1" in
  pre)
    suspend "$2"
    ;;
  post)
    resume "$2"
    ;;
esac

I also needed to increase the initial delay on resume, since sometimes the systemd-sleep hook gets invoked before the actual xHCI timeout occurs, missing the event and then skipping the re-bind.

1 Like

Thanks for debugging this!

I also run Arch but I use a TESmart KVM and have noticed a similar issue where the USB connections fail after resuming.

I took your script and so far seems to work well for my use case as well.

For others, @ancoron’s script can be added in /usr/lib/systemd/system-sleep/ and you’ll be good to go.

1 Like

I seem to be facing the same issue (on Fedora Sway Atomic 43), but it doesn’t happen right after resume. I’m using my computer normally and suddenly my USB periferals become unresponsive. It’s the two USB-A ports on the back that “die”, other seem to work. This happens about once or twice a week.

I have a USB hub with keyboard and mouse connected to one of the USB-A, and an external sound card connected to the other.

My journal messages are the same except the device identifier which is 0000:c3:00.4.

xhci_hcd 0000:c3:00.4: xHCI host not responding to stop endpoint command
xhci_hcd 0000:c3:00.4: xHCI host controller not responding, assume dead
xhci_hcd 0000:c3:00.4: HC died; cleaning up
usb 1-1: USB disconnect, device number 2
usb 1-1.1: USB disconnect, device number 3
usb 1-1.1.3: USB disconnect, device number 5
usb 1-1.2: 3:0: usb_set_interface failed (-110)
usb 2-1: USB disconnect, device number 2
usb 2-1.1: USB disconnect, device number 3
usb 1-1.1.4: USB disconnect, device number 6
usb 1-1.2: USB disconnect, device number 4

I have had this problem with my fingerprint reader ever since I got my Laptop 13. Thought it was a hardware issue after trying a bunch of fixes posted online but your script finally seems to have done the trick! Thank you!

@Jesse_Darnley you asked the community to help out with getting these into the issue tracker. Thanks, I didn’t know where to report issues.

Issue added to the tracker.

But I can’t reproduce it on my machine (yet)

@sounds Thank you for reporting this, I also didn’t know such issue tracker exists.

I guess this is the issue?

Just posting the direct link here so people may vote for it.