diff options
author | Ian Moffett <ian@osmora.org> | 2025-05-05 02:34:37 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-05-05 02:38:15 -0400 |
commit | 8799918c7d935703717f0b235d1dba09e400cf00 (patch) | |
tree | de65cde36079f182dbab459e1140117754c998bf | |
parent | 9adc9f74d66366e72d83a2ca9142c394f5ad5e2c (diff) |
kernel: xhci: Workaround Dell USBLEGSUP quirk
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r-- | sys/dev/usb/xhci.c | 43 | ||||
-rw-r--r-- | sys/include/dev/usb/xhcivar.h | 5 |
2 files changed, 39 insertions, 9 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index 67a1e4e..d68f98e 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -37,6 +37,7 @@ #include <dev/usb/xhciregs.h> #include <dev/usb/xhcivar.h> #include <dev/pci/pci.h> +#include <dev/acpi/acpi.h> #include <vm/physmem.h> #include <vm/dynalloc.h> #include <assert.h> @@ -145,15 +146,17 @@ xhci_parse_ecp(struct xhci_hc *hc) break; case XHCI_ECAP_USBLEGSUP: /* Begin xHC BIOS handoff to us */ - pr_trace("establishing xHC ownership...\n"); - val |= XHCI_OS_SEM; - mmio_write32(p, val); - - /* Ensure the xHC responded correctly */ - if (xhci_poll32(p, XHCI_OS_SEM, 1) < 0) - return -EIO; - if (xhci_poll32(p, XHCI_BIOS_SEM, 0) < 0) - return -EIO; + if (!ISSET(hc->quirks, XHCI_QUIRK_HANDOFF)) { + pr_trace("establishing xHC ownership...\n"); + val |= XHCI_OS_SEM; + mmio_write32(p, val); + + /* Ensure the xHC responded correctly */ + if (xhci_poll32(p, XHCI_OS_SEM, 1) < 0) + return -EIO; + if (xhci_poll32(p, XHCI_BIOS_SEM, 0) < 0) + return -EIO; + } break; } @@ -414,6 +417,28 @@ xhci_init_hc(struct xhci_hc *hc) uintptr_t dcbaap, cmdring; struct xhci_caps *caps; struct xhci_opregs *opregs; + const char *vendor; + + /* + * The firmware on some Dell machines handle the + * xHCI BIOS/OS handoff very poorly. Updating the + * the OS semaphore in the USBLEGSUP register will + * result in the chipset firing off an SMI which is + * supposed to perform the actual handoff. + * + * However, Dell is stupid as always and the machine + * can get stuck in SMM which results in the machine + * locking up in a *very* bad way. In other words, the + * OS execution is literally halted and further SMIs like + * thermal, power, and fan events are deferred forever + * (no bueno!!). The best thing to do is to not perform + * a handoff if the host board is by Dell (bad Dell!!). + */ + vendor = acpi_oemid(); + if (memcmp(vendor, "DELL", 4) == 0) { + pr_trace("detected xhc handoff quirk\n"); + hc->quirks |= XHCI_QUIRK_HANDOFF; + } caps = (struct xhci_caps *)hc->base; caplength = mmio_read8(&caps->caplength); diff --git a/sys/include/dev/usb/xhcivar.h b/sys/include/dev/usb/xhcivar.h index 0488ad8..a9a8fc1 100644 --- a/sys/include/dev/usb/xhcivar.h +++ b/sys/include/dev/usb/xhcivar.h @@ -32,6 +32,7 @@ #include <sys/types.h> #include <sys/types.h> +#include <sys/param.h> #include <dev/usb/xhciregs.h> #define XHCI_TIMEOUT 500 /* In ms */ @@ -41,6 +42,9 @@ #define XHCI_MAX_PROTOS 4 #define XHCI_IMOD_DEFAULT 0 +/* Quirks */ +#define XHCI_QUIRK_HANDOFF BIT(0) + /* * USB proto (USB 2.0 or 3.0) */ @@ -108,6 +112,7 @@ struct xhci_hc { uint32_t *evring; uint8_t maxslots; uint8_t cr_cycle : 1; + uint16_t quirks; size_t maxports; size_t protocnt; struct xhci_caps *caps; |