diff options
Diffstat (limited to 'sys/dev/usb/xhci.c')
-rw-r--r-- | sys/dev/usb/xhci.c | 93 |
1 files changed, 75 insertions, 18 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index d68f98e..e14cb44 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -37,6 +37,7 @@ #include <dev/usb/xhciregs.h> #include <dev/usb/xhcivar.h> #include <dev/pci/pci.h> +#include <dev/pci/pciregs.h> #include <dev/acpi/acpi.h> #include <vm/physmem.h> #include <vm/dynalloc.h> @@ -56,10 +57,11 @@ static struct pci_device *hci_dev; static struct timer tmr; -__attribute__((__interrupt__)) static void -xhci_common_isr(void *sf) +static int +xhci_intr(void *sf) { pr_trace("received xHCI interrupt (via PCI MSI-X)\n"); + return 1; /* handled */ } /* @@ -69,11 +71,16 @@ xhci_common_isr(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; + } + + /* Zero based */ + if (portno > 0) { + --portno; } - return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1))); + return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno)); } static int @@ -174,6 +181,7 @@ xhci_init_scratchpads(struct xhci_hc *hc) struct xhci_caps *caps = XHCI_CAPS(hc->base); uint16_t max_bufs_lo, max_bufs_hi; uint16_t max_bufs; + size_t len; uintptr_t *bufarr, tmp; max_bufs_lo = XHCI_MAX_SP_LO(caps->hcsparams1); @@ -187,8 +195,9 @@ xhci_init_scratchpads(struct xhci_hc *hc) return 0; } - pr_trace("using %d pages for xHC scratchpads\n"); - bufarr = dynalloc_memalign(sizeof(uintptr_t)*max_bufs, 0x1000); + len = sizeof(uint64_t) * max_bufs; + pr_trace("using %d bytes for xHC scratchpads\n", len); + bufarr = dynalloc_memalign(len, 0x1000); if (bufarr == NULL) { pr_error("failed to allocate scratchpad buffer array\n"); return -1; @@ -196,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; } @@ -217,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); @@ -232,7 +241,7 @@ xhci_init_msix(struct xhci_hc *hc) struct msi_intr intr; intr.name = "xHCI MSI-X"; - intr.handler = xhci_common_isr; + intr.handler = xhci_intr; return pci_enable_msix(hci_dev, &intr); } @@ -254,7 +263,7 @@ xhci_init_evring(struct xhci_hc *hc) memset(segtab, 0, DEFAULT_PAGESIZE); /* Set the size of the event ring segment table */ - erst_size = PTR_OFFSET(runtime, 0x28); + erst_size = PTR_OFFSET(runtime, XHCI_RT_ERSTSZ); mmio_write32(erst_size, 1); /* Allocate the event ring segment */ @@ -264,24 +273,24 @@ 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 */ - erdp = PTR_OFFSET(runtime, 0x38); + erdp = PTR_OFFSET(runtime, XHCI_RT_ERDP); mmio_write64(erdp, segtab->base); /* Point ERSTBA to our event ring segment */ - erstba = PTR_OFFSET(runtime, 0x30); + erstba = PTR_OFFSET(runtime, XHCI_RT_ERSTBA); mmio_write64(erstba, VIRT_TO_PHYS(segtab)); hc->evring = PHYS_TO_VIRT(segtab->base); /* Setup interrupt moderation */ - imod = PTR_OFFSET(runtime, 0x24); + imod = PTR_OFFSET(runtime, XHCI_RT_IMOD); mmio_write32(imod, XHCI_IMOD_DEFAULT); /* Enable interrupts */ - iman = PTR_OFFSET(runtime, 0x20); + iman = PTR_OFFSET(runtime, XHCI_RT_IMAN); tmp = mmio_read32(iman); mmio_write32(iman, tmp | XHCI_IMAN_IE); } @@ -331,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; } @@ -368,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) { @@ -384,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); /* @@ -457,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; } @@ -495,6 +541,16 @@ xhci_init_hc(struct xhci_hc *hc) return 0; } +static void +xhci_init_pci(void) +{ + uint32_t tmp; + + tmp = pci_readl(hci_dev, PCIREG_CMDSTATUS); + tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE); + pci_writel(hci_dev, PCIREG_CMDSTATUS, tmp); +} + static int xhci_init(void) { @@ -527,7 +583,8 @@ xhci_init(void) return -ENODEV; } + xhci_init_pci(); return xhci_init_hc(&xhc); } -DRIVER_EXPORT(xhci_init); +DRIVER_EXPORT(xhci_init, "xhci"); |