XHCI reset ports
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ struct xhci_init {
|
||||
|
||||
struct xhci_port {
|
||||
struct list_node_link ports_link;
|
||||
uint8_t port_value;
|
||||
int type;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user