XHCI reset ports

This commit is contained in:
2026-03-25 23:05:03 +01:00
parent 167c0ad5fd
commit afe32feac7
3 changed files with 118 additions and 34 deletions

View File

@@ -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;
}

View File

@@ -34,6 +34,7 @@ struct xhci_init {
struct xhci_port {
struct list_node_link ports_link;
uint8_t port_value;
int type;
};

View File

@@ -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; \