XHCI works on real hardware!

This commit is contained in:
2026-03-24 22:12:36 +01:00
parent b2e4a3802d
commit 0478570b2b
11 changed files with 215 additions and 102 deletions

View File

@@ -13,6 +13,7 @@
#include <proc/suspension_q.h>
#include <sys/debug.h>
#include <sys/spin_lock.h>
#include <sys/stall.h>
/* REF:
* https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
@@ -119,11 +120,10 @@ static uint32_t xhci_read32 (uintptr_t base, uint32_t reg) {
static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint8_t type) {
switch (type) {
case XHCI_TRB_CMD_CMPL_EVENT: {
uintptr_t cmd_trb_phys = event->param;
uint8_t cmpl_code = (event->status >> 24) & 0xFF;
uint8_t slot_id = (event->ctrl >> 24) & 0xFF;
DEBUG ("cmd completion: phys=%p,code=%u,slot=%u\n", cmd_trb_phys, cmpl_code, slot_id);
DEBUG ("cmd completion: code=%u,slot=%u\n", cmpl_code, slot_id);
} break;
default:
DEBUG ("Unhandled event type %u at %u\n", type, xhci->event_ring_idx);
@@ -179,17 +179,7 @@ static void xhci_irq (void* arg, void* regs, bool user, struct reschedule_ctx* r
uintptr_t ir_base = xhci->xhci_runtime_base + 0x20;
/* ack */
uint32_t iman = xhci_read32 (ir_base, XHCI_IMAN);
xhci_write32 (ir_base, XHCI_IMAN, iman | 0x01);
uint32_t usbsts = xhci_read32 (xhci->xhci_oper_base, XHCI_USBSTS);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBSTS, usbsts | (1 << 3));
/* clear event busy */
uint64_t erdp = (uint64_t)xhci_read32 (ir_base, XHCI_ERDP) |
((uint64_t)xhci_read32 (ir_base, XHCI_ERDP + 4) << 32);
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)erdp | (1 << 3));
xhci_write32 (ir_base, XHCI_IMAN, xhci_read32 (ir_base, XHCI_IMAN) | (1 << 0));
xhci_poll_events (xhci);
@@ -206,7 +196,7 @@ void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t
struct xhci_trb* link = &xhci->cmd_ring[xhci->cmd_ring_idx];
link->param = xhci->cmd_ring_phys;
link->status = 0;
link->ctrl = (6 << 10) | (1 << 1) | xhci->cmd_cycle_bit;
link->ctrl = (XHCI_TRB_LINK << 10) | (1 << 1) | xhci->cmd_cycle_bit;
xhci->cmd_ring_idx = 0;
xhci->cmd_cycle_bit ^= 1;
@@ -237,6 +227,48 @@ void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t
spin_unlock (&xhci->device->lock, fd);
}
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;
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;
if (cap_id == 1) {
DEBUG ("Found USB Legacy Support at offset 0x%x\n", ext_offset);
if (cap & (1 << 16)) {
DEBUG ("BIOS owns XHCI, requesting handover!\n");
xhci_write8 (cap_ptr, 3, 1);
int timeout = 1000;
while (timeout--) {
uint32_t val = xhci_read32 (cap_ptr, 0);
if (!(val & (1 << 16)) && (val & (1 << 24)))
break;
stall_ms (1);
}
if (timeout <= 0)
DEBUG ("Warning: XHCI BIOS handover timed out!\n");
else
DEBUG ("XHCI Handover successful.\n");
}
break;
}
uint8_t next = (cap >> 8) & 0xFF;
if (!next)
break;
ext_offset += (next << 2);
}
}
DEFINE_DEVICE_INIT (xhci_init) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
@@ -255,7 +287,9 @@ DEFINE_DEVICE_INIT (xhci_init) {
device->udata = xhci;
uint8_t cap_length = xhci_read8 (xhci->xhci_mmio_base, XHCI_CAPLENGTH);
uint32_t usbcmd, config, cap_length;
cap_length = xhci_read8 (xhci->xhci_mmio_base, XHCI_CAPLENGTH);
xhci->xhci_oper_base = xhci->xhci_mmio_base + cap_length;
@@ -265,90 +299,80 @@ DEFINE_DEVICE_INIT (xhci_init) {
uint32_t dboff = xhci_read32 (xhci->xhci_mmio_base, XHCI_DBOFF);
xhci->xhci_doorbell_base = xhci->xhci_mmio_base + dboff;
uint32_t hcsparams2 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCSPARAMS2);
xhci->max_scratchpad = (((hcsparams2 >> 21) & 0x1F) << 5) | ((hcsparams2 >> 27) & 0x1F);
DEBUG ("starting init sequence\n");
/* assert CNR is 0 */
while (xhci_read32 (xhci->xhci_oper_base, XHCI_USBSTS) & (1 << 11))
spin_lock_relax ();
/* stop running / clear Run/Stop bit */
usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
usbcmd &= ~(1 << 0);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd);
/* STOP */
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, 0);
stall_ms (1000);
/* wait for HCH bit */
int timeout = 100000;
while (!(xhci_read32 (xhci->xhci_oper_base, XHCI_USBSTS) & (1 << 12))) {
if (--timeout == 0)
break;
xhci_bios_handover (xhci);
spin_lock_relax ();
}
/* reset controller */
usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
usbcmd |= (1 << 1);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd);
/* RESET */
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, (1 << 1));
stall_ms (1000);
while (xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD) & (1 << 1))
spin_lock_relax ();
DEBUG ("controller reset\n");
/* Stall while controller not ready */
while (xhci_read32 (xhci->xhci_oper_base, XHCI_USBSTS) & (1 << 11))
spin_lock_relax ();
xhci->max_slots = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCSPARAMS1) & 0xFF;
DEBUG ("max_slots=%u\n", xhci->max_slots);
DEBUG ("XHCI init done\n");
uint32_t hcsparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCSPARAMS1);
uint8_t max_slots = (uint8_t)(hcsparams1 & 0xFF);
/* enable device notifications */
xhci_write32 (xhci->xhci_oper_base, XHCI_DNCTRL, 0xFFFF);
/* enable slots */
uint32_t config = xhci_read32 (xhci->xhci_oper_base, XHCI_CONFIG);
xhci_write32 (xhci->xhci_oper_base, XHCI_CONFIG, (config & ~0xFF) | max_slots);
config = xhci_read32 (xhci->xhci_oper_base, XHCI_CONFIG);
xhci_write32 (xhci->xhci_oper_base, XHCI_CONFIG, (config & ~0xFF) | (xhci->max_slots & 0xFF));
DEBUG ("enabled %u slots\n", max_slots);
uint32_t hcsparams2 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCSPARAMS2);
xhci->max_scratchpad = (hcsparams2 >> 27) & 0x1F;
uintptr_t dcbaa_phys = pmm_alloc (1);
xhci->xhci_dcbaa_phys = dcbaa_phys;
xhci->xhci_dcbaa = (uintptr_t*)(dcbaa_phys + (uintptr_t)hhdm->offset);
/* Prepare DCBAA */
xhci->xhci_dcbaa_phys = pmm_alloc (1);
xhci->xhci_dcbaa = (uintptr_t*)(xhci->xhci_dcbaa_phys + (uintptr_t)hhdm->offset);
memset (xhci->xhci_dcbaa, 0, PAGE_SIZE);
if (xhci->max_scratchpad > 0) {
uintptr_t dev_array_phys = pmm_alloc (1);
xhci->scratchpads_phys = pmm_alloc (1);
xhci->scratchpads = (uintptr_t*)(xhci->scratchpads_phys + (uintptr_t)hhdm->offset);
memset (xhci->scratchpads, 0, PAGE_SIZE);
uintptr_t* dev_array = (uintptr_t*)(dev_array_phys + (uintptr_t)hhdm->offset);
memset (dev_array, 0, PAGE_SIZE);
for (size_t sp = 0; sp < xhci->max_scratchpad; sp++) {
xhci->scratchpads[sp] = pmm_alloc (1);
}
for (size_t i = 0; i < xhci->max_scratchpad; i++)
dev_array[i] = pmm_alloc (1);
xhci->xhci_dcbaa[0] = dev_array_phys;
xhci->xhci_dcbaa[0] = xhci->scratchpads_phys;
}
xhci_write32 (xhci->xhci_oper_base, XHCI_DCBAAP, (uint32_t)xhci->xhci_dcbaa_phys);
xhci_write32 (xhci->xhci_oper_base, XHCI_DCBAAP + 4, (uint32_t)(xhci->xhci_dcbaa_phys >> 32));
xhci_write32 (xhci->xhci_oper_base, XHCI_DCBAAP, (uint32_t)xhci->xhci_dcbaa_phys);
xhci->cmd_ring_phys = pmm_alloc (1);
xhci->cmd_ring_phys = pmm_alloc_aligned (1, 64);
xhci->cmd_ring = (struct xhci_trb*)(xhci->cmd_ring_phys + (uintptr_t)hhdm->offset);
memset (xhci->cmd_ring, 0, PAGE_SIZE);
xhci->cmd_ring_size = PAGE_SIZE / sizeof (struct xhci_trb);
xhci->cmd_ring_idx = 0;
xhci->cmd_cycle_bit = 1;
memset (xhci->cmd_ring, 0, PAGE_SIZE);
uint64_t crcr = xhci->cmd_ring_phys | xhci->cmd_cycle_bit;
xhci_write32 (xhci->xhci_oper_base, XHCI_CRCR, (uint32_t)crcr);
xhci_write32 (xhci->xhci_oper_base, XHCI_CRCR + 4, (uint32_t)(crcr >> 32));
xhci_write32 (xhci->xhci_oper_base, XHCI_CRCR, (uint32_t)crcr);
xhci->event_ring_phys = pmm_alloc (1);
xhci->event_ring_phys = pmm_alloc_aligned (1, 64);
xhci->event_ring = (struct xhci_trb*)(xhci->event_ring_phys + (uintptr_t)hhdm->offset);
memset (xhci->event_ring, 0, PAGE_SIZE);
xhci->event_ring_size = PAGE_SIZE / sizeof (struct xhci_trb);
xhci->event_ring_idx = 0;
xhci->event_cycle_bit = 1;
memset (xhci->event_ring, 0, PAGE_SIZE);
xhci->erst_phys = pmm_alloc (1);
xhci->erst_phys = pmm_alloc_aligned (1, 64);
xhci->erst = (struct xhci_erst_entry*)(xhci->erst_phys + (uintptr_t)hhdm->offset);
memset (xhci->erst, 0, PAGE_SIZE);
xhci->erst[0].ptr = xhci->event_ring_phys;
xhci->erst[0].size = xhci->event_ring_size;
xhci->erst[0]._rsvd = 0;
@@ -358,19 +382,22 @@ DEFINE_DEVICE_INIT (xhci_init) {
xhci_write32 (ir_base, XHCI_ERSTBA, (uint32_t)xhci->erst_phys);
xhci_write32 (ir_base, XHCI_ERSTBA + 4, (uint32_t)(xhci->erst_phys >> 32));
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)xhci->event_ring_phys | (1 << 3));
xhci_write32 (ir_base, XHCI_ERDP + 4, (uint32_t)(xhci->event_ring_phys >> 32));
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)xhci->event_ring_phys | (1 << 3));
if (xhci->irqs_support) {
/* enable interrupter */
irq_attach (&xhci_irq, xhci, xhci->irq);
xhci_write32 (ir_base, XHCI_IMAN, xhci_read32 (ir_base, XHCI_IMAN) | 0x02);
xhci_write32 (ir_base, XHCI_IMAN, xhci_read32 (ir_base, XHCI_IMAN) | (1 << 1));
}
uint32_t usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd | 0x01 | (1 << 2));
usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd | (1 << 0) | (1 << 2));
xhci_send_cmd (xhci, 0, 0, (23 << 10));
while (xhci_read32 (xhci->xhci_oper_base, XHCI_USBSTS) & (1 << 11))
spin_lock_relax ();
xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10);
return true;
}
@@ -381,16 +408,16 @@ DEFINE_DEVICE_FINI (xhci_fini) {
struct xhci* xhci = device->udata;
if (xhci->max_scratchpad > 0) {
uintptr_t dev_array_phys = xhci->xhci_dcbaa[0];
uintptr_t scratchpads_phys = xhci->xhci_dcbaa[0];
uintptr_t* dev_array = (uintptr_t*)(dev_array_phys + (uintptr_t)hhdm->offset);
uintptr_t* scratchpads = (uintptr_t*)(scratchpads_phys + (uintptr_t)hhdm->offset);
for (size_t i = 0; i < xhci->max_scratchpad; i++) {
if (dev_array[i] != 0)
pmm_free (dev_array[i], 1);
if (scratchpads[i] != 0)
pmm_free (scratchpads[i], 1);
}
pmm_free (dev_array_phys, 1);
pmm_free (scratchpads_phys, 1);
}
pmm_free (xhci->xhci_dcbaa_phys, 1);

View File

@@ -42,7 +42,12 @@ struct xhci {
uintptr_t* xhci_dcbaa;
uintptr_t xhci_dcbaa_phys;
uint32_t max_scratchpad;
uintptr_t* scratchpads;
uintptr_t scratchpads_phys;
uint32_t max_slots;
struct xhci_trb* cmd_ring;
uintptr_t cmd_ring_phys;