summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-05-05 02:34:37 -0400
committerIan Moffett <ian@osmora.org>2025-05-05 02:38:15 -0400
commit8799918c7d935703717f0b235d1dba09e400cf00 (patch)
treede65cde36079f182dbab459e1140117754c998bf
parent9adc9f74d66366e72d83a2ca9142c394f5ad5e2c (diff)
kernel: xhci: Workaround Dell USBLEGSUP quirk
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--sys/dev/usb/xhci.c43
-rw-r--r--sys/include/dev/usb/xhcivar.h5
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;