diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sys/include/io/usb/xhciregs.h | 14 | ||||
-rw-r--r-- | src/sys/include/io/usb/xhcivar.h | 4 | ||||
-rw-r--r-- | src/sys/io/usb/hcd/xhci.c | 99 |
3 files changed, 114 insertions, 3 deletions
diff --git a/src/sys/include/io/usb/xhciregs.h b/src/sys/include/io/usb/xhciregs.h index f2e572b..f849c54 100644 --- a/src/sys/include/io/usb/xhciregs.h +++ b/src/sys/include/io/usb/xhciregs.h @@ -32,6 +32,7 @@ #include <sys/types.h> #include <sys/cdefs.h> +#include <sys/param.h> /* * XHCI capability registers @@ -69,4 +70,17 @@ struct __packed xhci_opregs { volatile uint32_t reserved2; }; +/* + * USB command register bits + * + * See section 5.4.1 of the xHCI spec + */ +#define USBCMD_HCRST BIT(1) + +/* + * Macros to get various register spaces + */ +#define XHCI_OPBASE(CAPBASE) \ + PTR_OFFSET(CAPBASE, (CAPBASE)->caplength) + #endif /* !_XHCIREGS_H_ */ diff --git a/src/sys/include/io/usb/xhcivar.h b/src/sys/include/io/usb/xhcivar.h index 5067063..c0b3a39 100644 --- a/src/sys/include/io/usb/xhcivar.h +++ b/src/sys/include/io/usb/xhcivar.h @@ -39,7 +39,9 @@ * @io: I/O space */ struct xhci_hcd { - volatile void *io; + struct xhci_capregs *capspace; }; +#define XHCI_TIMEOUT_MSEC 500 + #endif /* !_USB_XHCIVAR_H_ */ diff --git a/src/sys/io/usb/hcd/xhci.c b/src/sys/io/usb/hcd/xhci.c index 4cfbdc5..8d3b0a0 100644 --- a/src/sys/io/usb/hcd/xhci.c +++ b/src/sys/io/usb/hcd/xhci.c @@ -32,6 +32,7 @@ #include <sys/errno.h> #include <os/bus.h> #include <os/mmio.h> +#include <os/clkdev.h> #include <io/usb/xhcivar.h> #include <io/usb/xhciregs.h> #include <io/pci/bar.h> @@ -48,12 +49,106 @@ static struct pci_adv adv; static struct pci_device dev; static struct xhci_hcd hcd; +static struct clkdev *clk; + +/* + * Poll a register until it is either set or unset + * + * @reg: Register pointer to poll + * @mask: Mask to poll + * @pollset: If true, poll until set + */ +static int +xhci_poll32(volatile uint32_t *reg, uint32_t mask, bool pollset) +{ + uint16_t msec = 0; + uint32_t v, tmp; + + if (reg == NULL) { + return -EINVAL; + } + + for (;;) { + if (msec >= XHCI_TIMEOUT_MSEC) { + break; + } + + v = mmio_read32(reg); + v &= mask; + + if (pollset && v == mask) + return 0; + if (!pollset && v == 0) + return 0; + + clk->msleep(1); + ++msec; + } + + return -ETIME; +} + +/* + * Perform a hard host controller reset + */ +static int +xhci_reset_hc(struct xhci_hcd *hcd) +{ + struct xhci_opregs *opregs; + uint32_t usbcmd; + int error; + + if (hcd == NULL) { + return -EINVAL; + } + + opregs = XHCI_OPBASE(hcd->capspace); + + /* Reset the controller */ + usbcmd = mmio_read32(&opregs->usbcmd); + usbcmd |= USBCMD_HCRST; + mmio_write32(&opregs->usbcmd, usbcmd); + + /* Wait for it to be done */ + error = xhci_poll32(&opregs->usbcmd, USBCMD_HCRST, false); + if (error < 0) { + pr_trace("failed to initialize controller\n"); + return error; + } + + return 0; +} + +/* + * Initialize the host controller + */ +static int +xhci_init_hc(struct xhci_hcd *hcd) +{ + struct xhci_opregs *opregs; + uint32_t usbcmd; + int error; + + if (hcd == NULL) { + return -EINVAL; + } + + if ((error = xhci_reset_hc(hcd)) < 0) { + return error; + } + return 0; +} static int xhci_init(struct module *modp) { int error; + error = clkdev_get(CLKDEV_MSLEEP | CLKDEV_GET_USEC, &clk); + if (error < 0) { + return error; + } + if ((error = pci_advoc(&adv)) < 0) { return error; } @@ -84,8 +179,8 @@ xhci_attach(struct pci_adv *ap) return error; } - hcd.io = bs.va_base; - return 0; + hcd.capspace = bs.va_base; + return xhci_init_hc(&hcd); } static struct pci_adv adv = { |