From afe32feac7a4cd4db8af166297c810a8d7fb43da Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Wed, 25 Mar 2026 23:05:03 +0100 Subject: [PATCH] XHCI reset ports --- kernel/device/xhci.c | 144 +++++++++++++++++++++++++++++++++---------- kernel/device/xhci.h | 1 + kernel/libk/list.h | 7 ++- 3 files changed, 118 insertions(+), 34 deletions(-) diff --git a/kernel/device/xhci.c b/kernel/device/xhci.c index 57dba44..116f8cd 100644 --- a/kernel/device/xhci.c +++ b/kernel/device/xhci.c @@ -128,6 +128,58 @@ static uint32_t xhci_read32 (uintptr_t base, uint32_t reg) { return *(volatile uint32_t*)(base + reg); } +static uint32_t xhci_portsc_read (struct xhci* xhci, uint8_t port) { + return xhci_read32 (xhci->xhci_oper_base, 1024 + (16 * port)); +} + +static void xhci_portsc_write (struct xhci* xhci, uint8_t port, uint32_t value) { + xhci_write32 (xhci->xhci_oper_base, 1024 + (16 * port), value); +} + +static void xhci_port_reset (struct xhci* xhci, uint8_t port) { + struct xhci_port* xhci_port; + + list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value, port, ports_link); + + uint32_t portsc = xhci_portsc_read (xhci, port); + + /* check Port Power */ + if (!(portsc & (1 << 9))) { + portsc |= (1 << 9); + xhci_portsc_write (xhci, port, portsc); + + stall_ms (50); + + portsc = xhci_portsc_read (xhci, port); + } + + /* connect status change, enable port, port reset change */ + portsc |= (1 << 17) | (1 << 18) | (1 << 21); + + xhci_portsc_write (xhci, port, portsc); + + if (xhci_port->type == XHCI_PORT_USB3) { + /* warm port reset */ + portsc |= (1 << 31); + } else if (xhci_port->type == XHCI_PORT_USB2) { + /* port reset */ + portsc |= (1 << 4); + } + + xhci_portsc_write (xhci, port, portsc); + + stall_ms (500); + + /* warm port reset change, port reset change , connect status change, port enabled change */ + portsc |= (1 << 21) | (1 << 19) | (1 << 17) | (1 << 18); + /* port enabled */ + portsc &= ~(1 << 1); + + xhci_portsc_write (xhci, port, portsc); + + stall_ms (100); +} + static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint8_t type) { switch (type) { case XHCI_TRB_CMD_CMPL_EVENT: { @@ -246,7 +298,7 @@ void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t spin_unlock (&xhci->device->lock, fd); } -static void xhci_parse_ext_cap (struct xhci* xhci) { +static void xhci_bios_handover (struct xhci* xhci) { uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1); uint32_t ext_offset = (hccparams1 >> 16) << 2; @@ -257,33 +309,8 @@ static void xhci_parse_ext_cap (struct xhci* xhci) { uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset; uint32_t cap = xhci_read32 (cap_ptr, 0); uint8_t cap_id = cap & 0xFF; - uint8_t minor = (cap >> 16) & 0xFF; - uint8_t major = (cap >> 24) & 0xFF; - switch (cap_id) { - case XHCI_EXTCAP_SUPPORTED_PROTOCOL: { - uint32_t dword2 = xhci_read32 (cap_ptr, 8); - uint8_t port_off = dword2 & 0xFF; - uint8_t port_count = (dword2 >> 8) & 0xFF; - - uint8_t first_port = port_off - 1; - uint8_t last_port = first_port + port_count - 1; - - for (uint8_t port = first_port; port <= last_port; port++) { - struct xhci_port* xhci_port = malloc (sizeof (*xhci_port)); - memset (xhci_port, 0, sizeof (*xhci_port)); - - if (major == 3) - xhci_port->type = XHCI_PORT_USB3; - else - xhci_port->type = XHCI_PORT_USB2; - - list_append (xhci->xhci_ports, &xhci_port->ports_link); - - DEBUG ("PORT %u: USB %u.%u\n", port, major, minor); - } - } break; - case XHCI_EXTCAP_USB_LEGACY_SUPPORT: { + if (cap_id == XHCI_EXTCAP_USB_LEGACY_SUPPORT) { /* Make or break on real hardware. We need to take over ownership from the BIOS. */ if (cap & (1 << 16)) { DEBUG ("BIOS owns XHCI, requesting handover!\n"); @@ -302,8 +329,63 @@ static void xhci_parse_ext_cap (struct xhci* xhci) { DEBUG ("XHCI Handover OK\n"); } - } break; - default: + + break; + } + + uint8_t next = (cap >> 8) & 0xFF; + if (!next) + break; + + ext_offset += (next << 2); + } +} + +static void xhci_reset_ports (struct xhci* xhci) { + uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1); + uint32_t ext_offset = (hccparams1 >> 16) << 2; + + if (ext_offset == 0) + return; + + while (ext_offset) { + uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset; + uint32_t cap = xhci_read32 (cap_ptr, 0); + uint8_t cap_id = cap & 0xFF; + uint8_t minor = (cap >> 16) & 0xFF; + uint8_t major = (cap >> 24) & 0xFF; + + if (cap_id == XHCI_EXTCAP_SUPPORTED_PROTOCOL) { + uint32_t dword2 = xhci_read32 (cap_ptr, 8); + uint8_t port_off = dword2 & 0xFF; + uint8_t port_count = (dword2 >> 8) & 0xFF; + + uint8_t first_port = port_off - 1; + uint8_t last_port = first_port + port_count - 1; + + for (uint8_t port = first_port; port <= last_port; port++) { + struct xhci_port* xhci_port = malloc (sizeof (*xhci_port)); + memset (xhci_port, 0, sizeof (*xhci_port)); + + if (major == 3) + xhci_port->type = XHCI_PORT_USB3; + else + xhci_port->type = XHCI_PORT_USB2; + + xhci_port->port_value = port; + + list_append (xhci->xhci_ports, &xhci_port->ports_link); + + uint32_t portsc = xhci_portsc_read (xhci, port); + + if ((portsc & (1 << 17)) && (portsc & (1 << 0))) { + DEBUG ("Device connected. resetting\n"); + xhci_port_reset (xhci, port); + } + + DEBUG ("PORT %u: USB %u.%u\n", port, major, minor); + } + break; } @@ -357,7 +439,7 @@ DEFINE_DEVICE_INIT (xhci_init) { stall_ms (1000); - xhci_parse_ext_cap (xhci); + xhci_bios_handover (xhci); /* reset controller */ usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD); @@ -443,7 +525,7 @@ DEFINE_DEVICE_INIT (xhci_init) { xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10); - xhci_send_cmd (xhci, 0, 0, XHCI_TRB_NOOP_CMD << 10); + xhci_reset_ports (xhci); return true; } diff --git a/kernel/device/xhci.h b/kernel/device/xhci.h index 885e43e..0d9b7f0 100644 --- a/kernel/device/xhci.h +++ b/kernel/device/xhci.h @@ -34,6 +34,7 @@ struct xhci_init { struct xhci_port { struct list_node_link ports_link; + uint8_t port_value; int type; }; diff --git a/kernel/libk/list.h b/kernel/libk/list.h index 3757260..e96213b 100644 --- a/kernel/libk/list.h +++ b/kernel/libk/list.h @@ -54,13 +54,14 @@ struct list_node_link { } \ } while (0) -#define list_find(head, out, propname, propvalue) \ +#define list_find(type, head, out, propname, propvalue, link_field) \ do { \ (out) = NULL; \ struct list_node_link* __tmp = (head); \ while (__tmp) { \ - if (__tmp->propname == (propvalue)) { \ - (out) = __tmp; \ + type* __tmp2 = list_entry (__tmp, type, link_field); \ + if (__tmp2->propname == (propvalue)) { \ + (out) = __tmp2; \ break; \ } \ __tmp = __tmp->next; \