summaryrefslogtreecommitdiff
path: root/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys')
-rw-r--r--src/sys/include/io/usb/xhciregs.h14
-rw-r--r--src/sys/include/io/usb/xhcivar.h4
-rw-r--r--src/sys/io/usb/hcd/xhci.c99
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 = {