From b5c65e3022f285d389759bb699a9cefee79c5fd0 Mon Sep 17 00:00:00 2001
From: Ian Moffett <ian@osmora.org>
Date: Wed, 8 Jan 2025 22:00:42 -0500
Subject: kernel: xhci: Add xHC interrupt management

Signed-off-by: Ian Moffett <ian@osmora.org>
---
 sys/dev/usb/xhci.c             | 44 ++++++++++++++++++++++++++++++++++++++++++
 sys/include/dev/usb/xhciregs.h |  5 +++++
 sys/include/dev/usb/xhcivar.h  |  1 +
 3 files changed, 50 insertions(+)

diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index ad99f72..d2c671f 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -55,6 +55,12 @@
 static struct pci_device *hci_dev;
 static struct timer tmr;
 
+__attribute__((__interrupt__)) static void
+xhci_common_isr(void *sf)
+{
+    pr_trace("Received xHCI interrupt (via PCI MSI-X)\n");
+}
+
 /*
  * Get port status and control register for a specific
  * port.
@@ -214,6 +220,19 @@ xhci_alloc_dcbaa(struct xhci_hc *hc)
     return VIRT_TO_PHYS(hc->dcbaap);
 }
 
+/*
+ * Initialize MSI-X interrupts.
+ */
+static int
+xhci_init_msix(struct xhci_hc *hc)
+{
+    struct msi_intr intr;
+
+    intr.name = "xHCI MSI-X";
+    intr.handler = xhci_common_isr;
+    return pci_enable_msix(hci_dev, &intr);
+}
+
 /*
  * Sets up the event ring.
  */
@@ -312,6 +331,27 @@ xhci_reset(struct xhci_hc *hc)
     return 0;
 }
 
+/*
+ * Enable or disable xHC interrupts.
+ */
+static void
+xhci_set_intr(struct xhci_hc *hc, int enable)
+{
+    struct xhci_opregs *opregs = hc->opregs;
+    uint32_t usbcmd;
+
+    enable &= 1;
+    usbcmd = mmio_read32(&opregs->usbcmd);
+
+    if (enable == 1) {
+        usbcmd |= USBCMD_INTE;
+    } else {
+        usbcmd &= ~USBCMD_INTE;
+    }
+
+    mmio_write32(&opregs->usbcmd, usbcmd);
+}
+
 /*
  * Start up the host controller and put it into
  * a running state. Returns 0 on success.
@@ -419,9 +459,13 @@ xhci_init_hc(struct xhci_hc *hc)
     mmio_write64(&opregs->cmd_ring, cmdring);
     hc->cr_cycle = 1;
 
+    xhci_init_msix(hc);
     xhci_init_evring(hc);
     xhci_parse_ecp(hc);
     xhci_start_hc(hc);
+
+    /* Allow the xHC to generate interrupts */
+    xhci_set_intr(hc, 1);
     xhci_init_ports(hc);
     return 0;
 }
diff --git a/sys/include/dev/usb/xhciregs.h b/sys/include/dev/usb/xhciregs.h
index 8c47739..0416601 100644
--- a/sys/include/dev/usb/xhciregs.h
+++ b/sys/include/dev/usb/xhciregs.h
@@ -73,6 +73,7 @@ struct xhci_opregs {
 /* USBCMD bits */
 #define USBCMD_RUN      BIT(0)    /* Run/stop */
 #define USBCMD_HCRST    BIT(1)    /* xHC reset */
+#define USBCMD_INTE     BIT(2)    /* Interrupt Enable */
 
 /* USBSTS bits */
 #define USBSTS_HCH      BIT(0)    /* HC halted */
@@ -113,4 +114,8 @@ struct xhci_opregs {
 #define XHCI_BIOS_SEM   BIT(16)
 #define XHCI_OS_SEM     BIT(24)
 
+/* IMAN bits */
+#define XHCI_IMAN_IP    BIT(0)
+#define XHCI_IMAN_IE    BIT(1)
+
 #endif  /* !_USB_XHCIREGS_H_ */
diff --git a/sys/include/dev/usb/xhcivar.h b/sys/include/dev/usb/xhcivar.h
index cd445c8..4b0f1bf 100644
--- a/sys/include/dev/usb/xhcivar.h
+++ b/sys/include/dev/usb/xhcivar.h
@@ -39,6 +39,7 @@
 #define XHCI_EVRING_LEN 16
 #define XHCI_TRB_SIZE 16   /* In bytes */
 #define XHCI_MAX_PROTOS 4
+#define XHCI_IMOD_DEFAULT 0
 
 /*
  * USB proto (USB 2.0 or 3.0)
-- 
cgit v1.2.3