summaryrefslogtreecommitdiff
path: root/sys/dev/usb/xhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/xhci.c')
-rw-r--r--sys/dev/usb/xhci.c56
1 files changed, 49 insertions, 7 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 0ccb7a0..e14cb44 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -71,11 +71,16 @@ xhci_intr(void *sf)
static inline uint32_t *
xhci_get_portsc(struct xhci_hc *hc, uint8_t portno)
{
- if (portno > hc->maxports) {
- portno = hc->maxports;
+ if (portno >= hc->maxports) {
+ return NULL;
}
- return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1)));
+ /* Zero based */
+ if (portno > 0) {
+ --portno;
+ }
+
+ return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno));
}
static int
@@ -200,12 +205,12 @@ xhci_init_scratchpads(struct xhci_hc *hc)
for (size_t i = 0; i < max_bufs; ++i) {
tmp = vm_alloc_frame(1);
- memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
if (tmp == 0) {
/* TODO: Shutdown, free memory */
pr_error("failed to fill scratchpad buffer array\n");
return -1;
}
+ memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
bufarr[i] = tmp;
}
@@ -221,7 +226,7 @@ xhci_alloc_dcbaa(struct xhci_hc *hc)
{
size_t size;
- size = sizeof(uintptr_t) * hc->maxslots;
+ size = sizeof(uintptr_t) * (hc->maxslots + 1);
hc->dcbaap = dynalloc_memalign(size, 0x1000);
__assert(hc->dcbaap != NULL);
return VIRT_TO_PHYS(hc->dcbaap);
@@ -268,7 +273,7 @@ xhci_init_evring(struct xhci_hc *hc)
/* setup the event ring segment */
segtab->base = VIRT_TO_PHYS(tmp_p);
- segtab->base = ((uintptr_t)segtab->base) + (2 * 4096) & ~0xF;
+ segtab->base = ((uintptr_t)segtab->base);
segtab->size = XHCI_EVRING_LEN;
/* Setup the event ring dequeue pointer */
@@ -335,6 +340,13 @@ xhci_reset(struct xhci_hc *hc)
return error;
}
+ /* Wait longer if the xHC is not ready */
+ error = xhci_poll32(&opregs->usbsts, USBSTS_CNR, false);
+ if (error < 0) {
+ pr_error("xhci_reset: xHC ready wait timeout\n");
+ return error;
+ }
+
return 0;
}
@@ -372,13 +384,33 @@ xhci_start_hc(struct xhci_hc *hc)
/* Don't start up if we are already running */
usbcmd = mmio_read32(&opregs->usbcmd);
if (ISSET(usbcmd, USBCMD_RUN))
- return -EBUSY;
+ return 0;
usbcmd |= USBCMD_RUN;
mmio_write32(&opregs->usbcmd, usbcmd);
return 0;
}
+/*
+ * Stop and bring down the host controller.
+ * Returns 0 on success.
+ */
+static int
+xhci_stop_hc(struct xhci_hc *hc)
+{
+ struct xhci_opregs *opregs = hc->opregs;
+ uint32_t usbcmd;
+
+ /* Don't continue if we aren't running */
+ usbcmd = mmio_read32(&opregs->usbcmd);
+ if (!ISSET(usbcmd, USBCMD_RUN))
+ return 0;
+
+ usbcmd &= ~USBCMD_RUN;
+ mmio_write32(&opregs->usbcmd, usbcmd);
+ return 0;
+}
+
static int
xhci_init_ports(struct xhci_hc *hc)
{
@@ -388,6 +420,9 @@ xhci_init_ports(struct xhci_hc *hc)
for (size_t i = 1; i < hc->maxports; ++i) {
portsc_p = xhci_get_portsc(hc, i);
+ if (portsc_p == NULL) {
+ continue;
+ }
portsc = mmio_read32(portsc_p);
/*
@@ -461,8 +496,15 @@ xhci_init_hc(struct xhci_hc *hc)
return -1;
}
+ pr_trace("stopping xHC chip...\n");
+ if ((error = xhci_stop_hc(hc)) != 0) {
+ pr_error("run/stop timeout\n");
+ return error;
+ }
+
pr_trace("resetting xHC chip...\n");
if ((error = xhci_reset(hc)) != 0) {
+ pr_error("reset timeout\n");
return error;
}