summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorjakllsch <jakllsch@NetBSD.org>2013-09-14 00:40:31 +0000
committerjakllsch <jakllsch@NetBSD.org>2013-09-14 00:40:31 +0000
commit077c1e5a554ba66d4c6e4a09f337d2a8ded1bcee (patch)
treeebd3ae33933b6e78a5701b381723558f8930f016 /sys/dev
parent70f74c9d46f323f05ffd392f89065bf28f6409cc (diff)
Add work-in-progress xhci(4) driver code. Currently (mostly) supports
interrupt-driven control, interrupt and bulk transfers at the three USB 2.0 speeds on root hub ports.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/xhci_pci.c230
-rw-r--r--sys/dev/usb/usb_subr.c17
-rw-r--r--sys/dev/usb/usbdivar.h13
-rw-r--r--sys/dev/usb/xhci.c2841
-rw-r--r--sys/dev/usb/xhcireg.h426
-rw-r--r--sys/dev/usb/xhcivar.h127
6 files changed, 3645 insertions, 9 deletions
diff --git a/sys/dev/pci/xhci_pci.c b/sys/dev/pci/xhci_pci.c
new file mode 100644
index 00000000000..0c39530525b
--- /dev/null
+++ b/sys/dev/pci/xhci_pci.c
@@ -0,0 +1,230 @@
+/* $NetBSD: xhci_pci.c,v 1.1 2013/09/14 00:40:31 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: xhci_pci.c,v 1.1 2013/09/14 00:40:31 jakllsch Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+
+#include <sys/bus.h>
+
+#include <dev/pci/pcivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/xhcireg.h>
+#include <dev/usb/xhcivar.h>
+
+struct xhci_pci_softc {
+ struct xhci_softc sc_xhci;
+ pci_chipset_tag_t sc_pc;
+ pcitag_t sc_tag;
+};
+
+static int
+xhci_pci_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *) aux;
+
+ if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS &&
+ PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB &&
+ PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_XHCI)
+ return 1;
+
+ return 0;
+}
+
+static void
+xhci_pci_attach(device_t parent, device_t self, void *aux)
+{
+ struct xhci_pci_softc * const psc = device_private(self);
+ struct xhci_softc * const sc = &psc->sc_xhci;
+ struct pci_attach_args *const pa = (struct pci_attach_args *)aux;
+ const pci_chipset_tag_t pc = pa->pa_pc;
+ const pcitag_t tag = pa->pa_tag;
+ char const *intrstr;
+ pci_intr_handle_t ih;
+ pcireg_t csr, memtype;
+ usbd_status r;
+ //const char *vendor;
+ uint32_t hccparams;
+
+ sc->sc_dev = self;
+ sc->sc_bus.hci_private = sc;
+
+ pci_aprint_devinfo(pa, "USB Controller");
+
+ /* check if memory space access is enabled */
+ csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+#ifdef DEBUG
+ printf("csr: %08x\n", csr);
+#endif
+ if ((csr & PCI_COMMAND_MEM_ENABLE) == 0) {
+ aprint_error_dev(self, "memory access is disabled\n");
+ return;
+ }
+
+ /* map MMIO registers */
+ memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_CBMEM);
+ switch (memtype) {
+ case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
+ case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
+ if (pci_mapreg_map(pa, PCI_CBMEM, memtype, 0,
+ &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios)) {
+ sc->sc_ios = 0;
+ aprint_error_dev(self, "can't map mem space\n");
+ return;
+ }
+ break;
+ default:
+ aprint_error_dev(self, "BAR not 64 or 32-bit MMIO\n");
+ return;
+ break;
+ }
+
+ psc->sc_pc = pc;
+ psc->sc_tag = tag;
+
+ hccparams = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0x10);
+
+ if (pci_dma64_available(pa) && ((hccparams&1)==1))
+ sc->sc_bus.dmatag = pa->pa_dmat64;
+ else
+ sc->sc_bus.dmatag = pa->pa_dmat;
+
+ /* Enable the device. */
+ pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
+ csr | PCI_COMMAND_MASTER_ENABLE);
+
+ /* Map and establish the interrupt. */
+ if (pci_intr_map(pa, &ih)) {
+ aprint_error_dev(self, "couldn't map interrupt\n");
+ goto fail;
+ }
+
+ /*
+ * Allocate IRQ
+ */
+ intrstr = pci_intr_string(pc, ih);
+ sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, xhci_intr, sc);
+ if (sc->sc_ih == NULL) {
+ aprint_error_dev(self, "couldn't establish interrupt");
+ if (intrstr != NULL)
+ aprint_error(" at %s", intrstr);
+ aprint_error("\n");
+ goto fail;
+ }
+ aprint_normal_dev(self, "interrupting at %s\n", intrstr);
+
+#if 0
+ /* Figure out vendor for root hub descriptor. */
+ vendor = pci_findvendor(pa->pa_id);
+ sc->sc_id_vendor = PCI_VENDOR(pa->pa_id);
+ if (vendor)
+ strlcpy(sc->sc_vendor, vendor, sizeof(sc->sc_vendor));
+ else
+ snprintf(sc->sc_vendor, sizeof(sc->sc_vendor),
+ "vendor 0x%04x", PCI_VENDOR(pa->pa_id));
+#endif
+
+ r = xhci_init(sc);
+ if (r != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(self, "init failed, error=%d\n", r);
+ goto fail;
+ }
+
+ if (!pmf_device_register1(self, xhci_suspend, xhci_resume,
+ xhci_shutdown))
+ aprint_error_dev(self, "couldn't establish power handler\n");
+
+ /* Attach usb device. */
+ sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint);
+ return;
+
+fail:
+ if (sc->sc_ih) {
+ pci_intr_disestablish(psc->sc_pc, sc->sc_ih);
+ sc->sc_ih = NULL;
+ }
+ if (sc->sc_ios) {
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ sc->sc_ios = 0;
+ }
+ return;
+}
+
+static int
+xhci_pci_detach(device_t self, int flags)
+{
+ struct xhci_pci_softc * const psc = device_private(self);
+ struct xhci_softc * const sc = &psc->sc_xhci;
+ int rv;
+
+ rv = xhci_detach(sc, flags);
+ if (rv)
+ return rv;
+
+ pmf_device_deregister(self);
+
+ xhci_shutdown(self, flags);
+
+ if (sc->sc_ios) {
+#if 0
+ /* Disable interrupts, so we don't get any spurious ones. */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+ OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+#endif
+ }
+
+ if (sc->sc_ih != NULL) {
+ pci_intr_disestablish(psc->sc_pc, sc->sc_ih);
+ sc->sc_ih = NULL;
+ }
+ if (sc->sc_ios) {
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ sc->sc_ios = 0;
+ }
+
+ return 0;
+}
+
+CFATTACH_DECL3_NEW(xhci_pci, sizeof(struct xhci_pci_softc),
+ xhci_pci_match, xhci_pci_attach, xhci_pci_detach, xhci_activate, NULL,
+ xhci_childdet, DVF_DETACH_SHUTDOWN);
diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c
index 904605bbfe6..72212ec3339 100644
--- a/sys/dev/usb/usb_subr.c
+++ b/sys/dev/usb/usb_subr.c
@@ -1,4 +1,4 @@
-/* $NetBSD: usb_subr.c,v 1.192 2013/09/07 16:39:15 skrll Exp $ */
+/* $NetBSD: usb_subr.c,v 1.193 2013/09/14 00:40:31 jakllsch Exp $ */
/* $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.18 1999/11/17 22:33:47 n_hibma Exp $ */
/*
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: usb_subr.c,v 1.192 2013/09/07 16:39:15 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: usb_subr.c,v 1.193 2013/09/14 00:40:31 jakllsch Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -82,12 +82,8 @@ Static int usbd_getnewaddr(usbd_bus_handle);
Static int usbd_print(void *, const char *);
Static int usbd_ifprint(void *, const char *);
Static void usbd_free_iface_data(usbd_device_handle, int);
-Static void usbd_kill_pipe(usbd_pipe_handle);
-usbd_status usbd_attach_roothub(device_t, usbd_device_handle);
-Static usbd_status usbd_probe_and_attach(device_t, usbd_device_handle, int,
- int);
-Static u_int32_t usb_cookie_no = 0;
+uint32_t usb_cookie_no = 0;
Static const char * const usbd_error_strs[] = {
"NORMAL_COMPLETION",
@@ -1064,7 +1060,7 @@ usbd_reattach_device(device_t parent, usbd_device_handle dev,
* recognize the initial descriptor fetch (before the control endpoint's
* MaxPacketSize is known by the host) by exactly this length.
*/
-static usbd_status
+usbd_status
usbd_get_initial_ddesc(usbd_device_handle dev, usb_device_descriptor_t *desc)
{
usb_device_request_t req;
@@ -1107,6 +1103,11 @@ usbd_new_device(device_t parent, usbd_bus_handle bus, int depth,
DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
bus, port, depth, speed));
+
+ if (bus->methods->new_device != NULL)
+ return (bus->methods->new_device)(parent, bus, depth, speed,
+ port, up);
+
addr = usbd_getnewaddr(bus);
if (addr < 0) {
printf("%s: No free USB addresses, new device ignored.\n",
diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h
index 2a5b00ed88b..fa5c9c25ac4 100644
--- a/sys/dev/usb/usbdivar.h
+++ b/sys/dev/usb/usbdivar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: usbdivar.h,v 1.104 2013/09/07 16:47:23 skrll Exp $ */
+/* $NetBSD: usbdivar.h,v 1.105 2013/09/14 00:40:31 jakllsch Exp $ */
/*
* Copyright (c) 1998, 2012 The NetBSD Foundation, Inc.
@@ -48,6 +48,7 @@
* allocx -
* freex -
* get_lock - Called at attach time
+ * new_device
*
* PIPE METHOD LOCK NOTES
* ----------------------- ------- -------------------------
@@ -82,6 +83,7 @@ typedef struct {
struct usbd_xfer;
struct usbd_pipe;
+struct usbd_port;
struct usbd_endpoint {
usb_endpoint_descriptor_t *edesc;
@@ -99,6 +101,8 @@ struct usbd_bus_methods {
struct usbd_xfer * (*allocx)(struct usbd_bus *);
void (*freex)(struct usbd_bus *, struct usbd_xfer *);
void (*get_lock)(struct usbd_bus *, kmutex_t **);
+ usbd_status (*new_device)(device_t, usbd_bus_handle, int,
+ int, int, struct usbd_port *);
};
struct usbd_pipe_methods {
@@ -191,6 +195,7 @@ struct usbd_device {
int subdevlen; /* array length of following */
device_t *subdevs; /* sub-devices */
int nifaces_claimed; /* number of ifaces in use */
+ void *hci_private;
};
struct usbd_interface {
@@ -308,6 +313,12 @@ usbd_status usb_insert_transfer(usbd_xfer_handle);
void usb_transfer_complete(usbd_xfer_handle);
int usb_disconnect_port(struct usbd_port *, device_t, int);
+void usbd_kill_pipe(usbd_pipe_handle);
+usbd_status usbd_attach_roothub(device_t, usbd_device_handle);
+usbd_status usbd_probe_and_attach(device_t, usbd_device_handle, int, int);
+usbd_status usbd_get_initial_ddesc(usbd_device_handle,
+ usb_device_descriptor_t *);
+
/* Routines from usb.c */
void usb_needs_explore(usbd_device_handle);
void usb_needs_reattach(usbd_device_handle);
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
new file mode 100644
index 00000000000..85b526b6293
--- /dev/null
+++ b/sys/dev/usb/xhci.c
@@ -0,0 +1,2841 @@
+/* $NetBSD: xhci.c,v 1.1 2013/09/14 00:40:31 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2013 Jonathan A. Kollasch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.1 2013/09/14 00:40:31 jakllsch Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+
+#include <machine/endian.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/xhcireg.h>
+#include <dev/usb/xhcivar.h>
+#include <dev/usb/usbroothub_subr.h>
+
+#ifdef XHCI_DEBUG
+int xhcidebug = 0;
+#define DPRINTF(x) do { if (xhcidebug) printf x; } while(0)
+#define DPRINTFN(n,x) do { if (xhcidebug>(n)) printf x; } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define XHCI_DCI_SLOT 0
+#define XHCI_DCI_EP_CONTROL 1
+
+#define XHCI_ICI_INPUT_CONTROL 0
+
+struct xhci_pipe {
+ struct usbd_pipe xp_pipe;
+};
+
+#define XHCI_INTR_ENDPT 1
+#define XHCI_COMMAND_RING_TRBS 256
+#define XHCI_EVENT_RING_TRBS 256
+#define XHCI_EVENT_RING_SEGMENTS 1
+#define XHCI_TRB_3_ED_BIT XHCI_TRB_3_ISP_BIT
+
+static usbd_status xhci_open(usbd_pipe_handle);
+static int xhci_intr1(struct xhci_softc * const);
+static void xhci_softintr(void *);
+static void xhci_poll(struct usbd_bus *);
+static usbd_status xhci_allocm(struct usbd_bus *, usb_dma_t *, uint32_t);
+static void xhci_freem(struct usbd_bus *, usb_dma_t *);
+static usbd_xfer_handle xhci_allocx(struct usbd_bus *);
+static void xhci_freex(struct usbd_bus *, usbd_xfer_handle);
+static void xhci_get_lock(struct usbd_bus *, kmutex_t **);
+static usbd_status xhci_new_device(device_t, usbd_bus_handle, int, int, int,
+ struct usbd_port *);
+
+static usbd_status xhci_configure_endpoint(usbd_pipe_handle);
+static usbd_status xhci_unconfigure_endpoint(usbd_pipe_handle);
+static usbd_status xhci_reset_endpoint(usbd_pipe_handle);
+//static usbd_status xhci_stop_endpoint(usbd_pipe_handle);
+
+static usbd_status xhci_set_dequeue(usbd_pipe_handle);
+
+static usbd_status xhci_do_command(struct xhci_softc * const,
+ struct xhci_trb * const, int);
+static usbd_status xhci_init_slot(struct xhci_softc * const, uint32_t,
+ int, int, int, int);
+static usbd_status xhci_enable_slot(struct xhci_softc * const,
+ uint8_t * const);
+static usbd_status xhci_address_device(struct xhci_softc * const,
+ uint64_t, uint8_t, bool);
+static usbd_status xhci_update_ep0_mps(struct xhci_softc * const,
+ struct xhci_slot * const, u_int);
+static usbd_status xhci_ring_init(struct xhci_softc * const,
+ struct xhci_ring * const, size_t, size_t);
+static void xhci_ring_free(struct xhci_softc * const, struct xhci_ring * const);
+
+static void xhci_noop(usbd_pipe_handle);
+
+static usbd_status xhci_root_ctrl_transfer(usbd_xfer_handle);
+static usbd_status xhci_root_ctrl_start(usbd_xfer_handle);
+static void xhci_root_ctrl_abort(usbd_xfer_handle);
+static void xhci_root_ctrl_close(usbd_pipe_handle);
+static void xhci_root_ctrl_done(usbd_xfer_handle);
+
+static usbd_status xhci_root_intr_transfer(usbd_xfer_handle);
+static usbd_status xhci_root_intr_start(usbd_xfer_handle);
+static void xhci_root_intr_abort(usbd_xfer_handle);
+static void xhci_root_intr_close(usbd_pipe_handle);
+static void xhci_root_intr_done(usbd_xfer_handle);
+
+static usbd_status xhci_device_ctrl_transfer(usbd_xfer_handle);
+static usbd_status xhci_device_ctrl_start(usbd_xfer_handle);
+static void xhci_device_ctrl_abort(usbd_xfer_handle);
+static void xhci_device_ctrl_close(usbd_pipe_handle);
+static void xhci_device_ctrl_done(usbd_xfer_handle);
+
+static usbd_status xhci_device_intr_transfer(usbd_xfer_handle);
+static usbd_status xhci_device_intr_start(usbd_xfer_handle);
+static void xhci_device_intr_abort(usbd_xfer_handle);
+static void xhci_device_intr_close(usbd_pipe_handle);
+static void xhci_device_intr_done(usbd_xfer_handle);
+
+static usbd_status xhci_device_bulk_transfer(usbd_xfer_handle);
+static usbd_status xhci_device_bulk_start(usbd_xfer_handle);
+static void xhci_device_bulk_abort(usbd_xfer_handle);
+static void xhci_device_bulk_close(usbd_pipe_handle);
+static void xhci_device_bulk_done(usbd_xfer_handle);
+
+static void xhci_timeout(void *);
+static void xhci_timeout_task(void *);
+
+static const struct usbd_bus_methods xhci_bus_methods = {
+ .open_pipe = xhci_open,
+ .soft_intr = xhci_softintr,
+ .do_poll = xhci_poll,
+ .allocm = xhci_allocm,
+ .freem = xhci_freem,
+ .allocx = xhci_allocx,
+ .freex = xhci_freex,
+ .get_lock = xhci_get_lock,
+ .new_device = xhci_new_device,
+};
+
+static const struct usbd_pipe_methods xhci_root_ctrl_methods = {
+ .transfer = xhci_root_ctrl_transfer,
+ .start = xhci_root_ctrl_start,
+ .abort = xhci_root_ctrl_abort,
+ .close = xhci_root_ctrl_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_root_ctrl_done,
+};
+
+static const struct usbd_pipe_methods xhci_root_intr_methods = {
+ .transfer = xhci_root_intr_transfer,
+ .start = xhci_root_intr_start,
+ .abort = xhci_root_intr_abort,
+ .close = xhci_root_intr_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_root_intr_done,
+};
+
+
+static const struct usbd_pipe_methods xhci_device_ctrl_methods = {
+ .transfer = xhci_device_ctrl_transfer,
+ .start = xhci_device_ctrl_start,
+ .abort = xhci_device_ctrl_abort,
+ .close = xhci_device_ctrl_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_ctrl_done,
+};
+
+static const struct usbd_pipe_methods xhci_device_isoc_methods = {
+ .cleartoggle = xhci_noop,
+};
+
+static const struct usbd_pipe_methods xhci_device_bulk_methods = {
+ .transfer = xhci_device_bulk_transfer,
+ .start = xhci_device_bulk_start,
+ .abort = xhci_device_bulk_abort,
+ .close = xhci_device_bulk_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_bulk_done,
+};
+
+static const struct usbd_pipe_methods xhci_device_intr_methods = {
+ .transfer = xhci_device_intr_transfer,
+ .start = xhci_device_intr_start,
+ .abort = xhci_device_intr_abort,
+ .close = xhci_device_intr_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_intr_done,
+};
+
+static inline uint32_t
+xhci_read_4(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
+}
+
+static inline void
+xhci_write_4(const struct xhci_softc * const sc, bus_size_t offset,
+ uint32_t value)
+{
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value);
+}
+
+static inline uint32_t
+xhci_cap_read_4(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_cbh, offset);
+}
+
+static inline uint32_t
+xhci_op_read_4(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_obh, offset);
+}
+
+static inline void
+xhci_op_write_4(const struct xhci_softc * const sc, bus_size_t offset,
+ uint32_t value)
+{
+ bus_space_write_4(sc->sc_iot, sc->sc_obh, offset, value);
+}
+
+static inline uint64_t
+xhci_op_read_8(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ uint64_t value;
+
+ if (sc->sc_ac64) {
+#ifdef XHCI_USE_BUS_SPACE_8
+ value = bus_space_read_8(sc->sc_iot, sc->sc_obh, offset);
+#else
+ value = bus_space_read_4(sc->sc_iot, sc->sc_obh, offset);
+ value |= (uint64_t)bus_space_read_4(sc->sc_iot, sc->sc_obh,
+ offset + 4) << 32;
+#endif
+ } else {
+ value = bus_space_read_4(sc->sc_iot, sc->sc_obh, offset);
+ }
+
+ return value;
+}
+
+static inline void
+xhci_op_write_8(const struct xhci_softc * const sc, bus_size_t offset,
+ uint64_t value)
+{
+ if (sc->sc_ac64) {
+#ifdef XHCI_USE_BUS_SPACE_8
+ bus_space_write_8(sc->sc_iot, sc->sc_obh, offset, value);
+#else
+ bus_space_write_4(sc->sc_iot, sc->sc_obh, offset + 0,
+ (value >> 0) & 0xffffffff);
+ bus_space_write_4(sc->sc_iot, sc->sc_obh, offset + 4,
+ (value >> 32) & 0xffffffff);
+#endif
+ } else {
+ bus_space_write_4(sc->sc_iot, sc->sc_obh, offset, value);
+ }
+}
+
+static inline uint32_t
+xhci_rt_read_4(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_rbh, offset);
+}
+
+static inline void
+xhci_rt_write_4(const struct xhci_softc * const sc, bus_size_t offset,
+ uint32_t value)
+{
+ bus_space_write_4(sc->sc_iot, sc->sc_rbh, offset, value);
+}
+
+static inline uint64_t
+xhci_rt_read_8(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ uint64_t value;
+
+ if (sc->sc_ac64) {
+#ifdef XHCI_USE_BUS_SPACE_8
+ value = bus_space_read_8(sc->sc_iot, sc->sc_rbh, offset);
+#else
+ value = bus_space_read_4(sc->sc_iot, sc->sc_rbh, offset);
+ value |= (uint64_t)bus_space_read_4(sc->sc_iot, sc->sc_rbh,
+ offset + 4) << 32;
+#endif
+ } else {
+ value = bus_space_read_4(sc->sc_iot, sc->sc_rbh, offset);
+ }
+
+ return value;
+}
+
+static inline void
+xhci_rt_write_8(const struct xhci_softc * const sc, bus_size_t offset,
+ uint64_t value)
+{
+ if (sc->sc_ac64) {
+#ifdef XHCI_USE_BUS_SPACE_8
+ bus_space_write_8(sc->sc_iot, sc->sc_rbh, offset, value);
+#else
+ bus_space_write_4(sc->sc_iot, sc->sc_rbh, offset + 0,
+ (value >> 0) & 0xffffffff);
+ bus_space_write_4(sc->sc_iot, sc->sc_rbh, offset + 4,
+ (value >> 32) & 0xffffffff);
+#endif
+ } else {
+ bus_space_write_4(sc->sc_iot, sc->sc_rbh, offset, value);
+ }
+}
+
+static inline uint32_t
+xhci_db_read_4(const struct xhci_softc * const sc, bus_size_t offset)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_dbh, offset);
+}
+
+static inline void
+xhci_db_write_4(const struct xhci_softc * const sc, bus_size_t offset,
+ uint32_t value)
+{
+ bus_space_write_4(sc->sc_iot, sc->sc_dbh, offset, value);
+}
+
+/* --- */
+
+static inline uint8_t
+xhci_ep_get_type(usb_endpoint_descriptor_t * const ed)
+{
+ u_int eptype;
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_CONTROL:
+ eptype = 0x0;
+ break;
+ case UE_ISOCHRONOUS:
+ eptype = 0x1;
+ break;
+ case UE_BULK:
+ eptype = 0x2;
+ break;
+ case UE_INTERRUPT:
+ eptype = 0x3;
+ break;
+ }
+
+ if ((UE_GET_XFERTYPE(ed->bmAttributes) == UE_CONTROL) ||
+ (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN))
+ return eptype | 0x4;
+ else
+ return eptype;
+}
+
+static u_int
+xhci_ep_get_dci(usb_endpoint_descriptor_t * const ed)
+{
+ /* xHCI 1.0 section 4.5.1 */
+ u_int epaddr = UE_GET_ADDR(ed->bEndpointAddress);
+ u_int in = 0;
+
+ if ((UE_GET_XFERTYPE(ed->bmAttributes) == UE_CONTROL) ||
+ (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN))
+ in = 1;
+
+ return epaddr * 2 + in;
+}
+
+static inline u_int
+xhci_dci_to_ici(const u_int i)
+{
+ return i + 1;
+}
+
+static inline void *
+xhci_slot_get_dcv(struct xhci_softc * const sc, struct xhci_slot * const xs,
+ const u_int dci)
+{
+ return KERNADDR(&xs->xs_dc_dma, sc->sc_ctxsz * dci);
+}
+
+static inline bus_addr_t
+xhci_slot_get_dcp(struct xhci_softc * const sc, struct xhci_slot * const xs,
+ const u_int dci)
+{
+ return DMAADDR(&xs->xs_dc_dma, sc->sc_ctxsz * dci);
+}
+
+static inline void *
+xhci_slot_get_icv(struct xhci_softc * const sc, struct xhci_slot * const xs,
+ const u_int ici)
+{
+ return KERNADDR(&xs->xs_ic_dma, sc->sc_ctxsz * ici);
+}
+
+static inline bus_addr_t
+xhci_slot_get_icp(struct xhci_softc * const sc, struct xhci_slot * const xs,
+ const u_int ici)
+{
+ return DMAADDR(&xs->xs_ic_dma, sc->sc_ctxsz * ici);
+}
+
+static inline struct xhci_trb *
+xhci_ring_trbv(struct xhci_ring * const xr, u_int idx)
+{
+ return KERNADDR(&xr->xr_dma, XHCI_TRB_SIZE * idx);
+}
+
+static inline bus_addr_t
+xhci_ring_trbp(struct xhci_ring * const xr, u_int idx)
+{
+ return DMAADDR(&xr->xr_dma, XHCI_TRB_SIZE * idx);
+}
+
+static inline void
+xhci_trb_put(struct xhci_trb * const trb, uint64_t parameter, uint32_t status,
+ uint32_t control)
+{
+ trb->trb_0 = parameter;
+ trb->trb_2 = status;
+ trb->trb_3 = control;
+}
+
+/* --- */
+
+void
+xhci_childdet(device_t self, device_t child)
+{
+ struct xhci_softc * const sc = device_private(self);
+
+ KASSERT(sc->sc_child == child);
+ if (child == sc->sc_child)
+ sc->sc_child = NULL;
+}
+
+int
+xhci_detach(struct xhci_softc *sc, int flags)
+{
+ int rv = 0;
+
+ if (sc->sc_child != NULL)
+ rv = config_detach(sc->sc_child, flags);
+
+ if (rv != 0)
+ return (rv);
+
+ /* XXX unconfigure/free slots */
+
+ /* verify: */
+ xhci_rt_write_4(sc, XHCI_IMAN(0), 0);
+ xhci_op_write_4(sc, XHCI_USBCMD, 0);
+ /* do we need to wait for stop? */
+
+ xhci_op_write_8(sc, XHCI_CRCR, 0);
+ xhci_ring_free(sc, &sc->sc_cr);
+ cv_destroy(&sc->sc_command_cv);
+
+ xhci_rt_write_4(sc, XHCI_ERSTSZ(0), 0);
+ xhci_rt_write_8(sc, XHCI_ERSTBA(0), 0);
+ xhci_rt_write_8(sc, XHCI_ERDP(0), 0|XHCI_ERDP_LO_BUSY);
+ xhci_ring_free(sc, &sc->sc_er);
+
+ usb_freemem(&sc->sc_bus, &sc->sc_eventst_dma);
+
+ xhci_op_write_8(sc, XHCI_DCBAAP, 0);
+ usb_freemem(&sc->sc_bus, &sc->sc_dcbaa_dma);
+
+ kmem_free(sc->sc_slots, sizeof(*sc->sc_slots) * sc->sc_maxslots);
+
+ mutex_destroy(&sc->sc_lock);
+ mutex_destroy(&sc->sc_intr_lock);
+
+ pool_cache_destroy(sc->sc_xferpool);
+
+ return rv;
+}
+
+int
+xhci_activate(device_t self, enum devact act)
+{
+ struct xhci_softc * const sc = device_private(self);
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = true;
+ return 0;
+ default:
+ return EOPNOTSUPP;
+ }
+}
+
+bool
+xhci_suspend(device_t dv, const pmf_qual_t *qual)
+{
+ return false;
+}
+
+bool
+xhci_resume(device_t dv, const pmf_qual_t *qual)
+{
+ return false;
+}
+
+bool
+xhci_shutdown(device_t self, int flags)
+{
+ return false;
+}
+
+
+static void
+hexdump(const char *msg, const void *base, size_t len)
+{
+#if 0
+ size_t cnt;
+ const uint32_t *p;
+ extern paddr_t vtophys(vaddr_t);
+
+ p = base;
+ cnt = 0;
+
+ printf("*** %s (%zu bytes @ %p %p)\n", msg, len, base,
+ (void *)vtophys((vaddr_t)base));
+
+ while (cnt < len) {
+ if (cnt % 16 == 0)
+ printf("%p: ", p);
+ else if (cnt % 8 == 0)
+ printf(" |");
+ printf(" %08x", *p++);
+ cnt += 4;
+ if (cnt % 16 == 0)
+ printf("\n");
+ }
+#endif
+}
+
+
+usbd_status
+xhci_init(struct xhci_softc *sc)
+{
+ bus_size_t bsz;
+ uint32_t cap, hcs1, hcs2, hcs3, hcc, dboff, rtsoff;
+ uint32_t ecp, ecr;
+ uint32_t usbcmd, usbsts, pagesize, config;
+ int i;
+ uint16_t hciversion;
+ uint8_t caplength;
+
+ DPRINTF(("%s\n", __func__));
+
+ sc->sc_bus.usbrev = USBREV_2_0; /* XXX Low/Full/High speeds for now */
+
+ cap = xhci_read_4(sc, XHCI_CAPLENGTH);
+ caplength = XHCI_CAP_CAPLENGTH(cap);
+ hciversion = XHCI_CAP_HCIVERSION(cap);
+
+ if ((hciversion < 0x0096) || (hciversion > 0x0100)) {
+ aprint_normal_dev(sc->sc_dev,
+ "xHCI version %x.%x not known to be supported\n",
+ (hciversion >> 8) & 0xff, (hciversion >> 0) & 0xff);
+ } else {
+ aprint_verbose_dev(sc->sc_dev, "xHCI version %x.%x\n",
+ (hciversion >> 8) & 0xff, (hciversion >> 0) & 0xff);
+ }
+
+ if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0, caplength,
+ &sc->sc_cbh) != 0) {
+ aprint_error_dev(sc->sc_dev, "capability subregion failure\n");
+ return USBD_NOMEM;
+ }
+
+ hcs1 = xhci_cap_read_4(sc, XHCI_HCSPARAMS1);
+ sc->sc_maxslots = XHCI_HCS1_MAXSLOTS(hcs1);
+ sc->sc_maxintrs = XHCI_HCS1_MAXINTRS(hcs1);
+ sc->sc_maxports = XHCI_HCS1_MAXPORTS(hcs1);
+ hcs2 = xhci_cap_read_4(sc, XHCI_HCSPARAMS2);
+ hcs3 = xhci_cap_read_4(sc, XHCI_HCSPARAMS3);
+ hcc = xhci_cap_read_4(sc, XHCI_HCCPARAMS);
+
+ sc->sc_ac64 = XHCI_HCC_AC64(hcc);
+ sc->sc_ctxsz = XHCI_HCC_CSZ(hcc) ? 64 : 32;
+ device_printf(sc->sc_dev, "ac64 %d ctxsz %d\n", sc->sc_ac64,
+ sc->sc_ctxsz);
+
+ device_printf(sc->sc_dev, "xECP %x\n", XHCI_HCC_XECP(hcc) * 4);
+ ecp = XHCI_HCC_XECP(hcc) * 4;
+ while (ecp != 0) {
+ ecr = xhci_read_4(sc, ecp);
+ device_printf(sc->sc_dev, "ECR %x: %08x\n", ecp, ecr);
+ switch (XHCI_XECP_ID(ecr)) {
+ case XHCI_ID_PROTOCOLS: {
+ uint32_t w0, w4, w8;
+ uint16_t w2;
+ w0 = xhci_read_4(sc, ecp + 0);
+ w2 = (w0 >> 16) & 0xffff;
+ w4 = xhci_read_4(sc, ecp + 4);
+ w8 = xhci_read_4(sc, ecp + 8);
+ device_printf(sc->sc_dev, "SP: %08x %08x %08x\n",
+ w0, w4, w8);
+ if (w4 == 0x20425355 && w2 == 0x0300) {
+ sc->sc_ss_port_start = (w8 >> 0) & 0xff;;
+ sc->sc_ss_port_count = (w8 >> 8) & 0xff;;
+ }
+ if (w4 == 0x20425355 && w2 == 0x0200) {
+ sc->sc_hs_port_start = (w8 >> 0) & 0xff;
+ sc->sc_hs_port_count = (w8 >> 8) & 0xff;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ ecr = xhci_read_4(sc, ecp);
+ if (XHCI_XECP_NEXT(ecr) == 0) {
+ ecp = 0;
+ } else {
+ ecp += XHCI_XECP_NEXT(ecr) * 4;
+ }
+ }
+
+ bsz = XHCI_PORTSC(sc->sc_maxports + 1);
+ if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, caplength, bsz,
+ &sc->sc_obh) != 0) {
+ aprint_error_dev(sc->sc_dev, "operational subregion failure\n");
+ return USBD_NOMEM;
+ }
+
+ dboff = xhci_cap_read_4(sc, XHCI_DBOFF);
+ if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, dboff,
+ sc->sc_maxslots * 4, &sc->sc_dbh) != 0) {
+ aprint_error_dev(sc->sc_dev, "doorbell subregion failure\n");
+ return USBD_NOMEM;
+ }
+
+ rtsoff = xhci_cap_read_4(sc, XHCI_RTSOFF);
+ if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, rtsoff,
+ sc->sc_maxintrs * 0x20, &sc->sc_rbh) != 0) {
+ aprint_error_dev(sc->sc_dev, "runtime subregion failure\n");
+ return USBD_NOMEM;
+ }
+
+ for (i = 0; i < 100; i++) {
+ usbsts = xhci_op_read_4(sc, XHCI_USBSTS);
+ if ((usbsts & XHCI_STS_CNR) == 0)
+ break;
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (i >= 100)
+ return USBD_IOERROR;
+
+ usbcmd = 0;
+ xhci_op_write_4(sc, XHCI_USBCMD, usbcmd);
+ usb_delay_ms(&sc->sc_bus, 1);
+
+ usbcmd = XHCI_CMD_HCRST;
+ xhci_op_write_4(sc, XHCI_USBCMD, usbcmd);
+ for (i = 0; i < 100; i++) {
+ usbcmd = xhci_op_read_4(sc, XHCI_USBCMD);
+ if ((usbcmd & XHCI_CMD_HCRST) == 0)
+ break;
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (i >= 100)
+ return USBD_IOERROR;
+
+ for (i = 0; i < 100; i++) {
+ usbsts = xhci_op_read_4(sc, XHCI_USBSTS);
+ if ((usbsts & XHCI_STS_CNR) == 0)
+ break;
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (i >= 100)
+ return USBD_IOERROR;
+
+ device_printf(sc->sc_dev, "maxspbuf %d\n", XHCI_HCS2_MAXSPBUF(hcs2));
+ if (XHCI_HCS2_MAXSPBUF(hcs2) != 0) {
+ /* XXX */
+ aprint_error_dev(sc->sc_dev,
+ "TODO implement scratchpad allocation\n");
+ return USBD_INVAL;
+ }
+
+ pagesize = xhci_op_read_4(sc, XHCI_PAGESIZE);
+ device_printf(sc->sc_dev, "PAGESIZE 0x%08x\n", pagesize);
+ pagesize = ffs(pagesize);
+ if (pagesize == 0)
+ return USBD_IOERROR;
+ sc->sc_pgsz = 1 << (12 + (pagesize - 1));
+ device_printf(sc->sc_dev, "sc_pgsz 0x%08x\n", (uint32_t)sc->sc_pgsz);
+ device_printf(sc->sc_dev, "sc_maxslots 0x%08x\n",
+ (uint32_t)sc->sc_maxslots);
+
+ config = xhci_op_read_4(sc, XHCI_CONFIG);
+ config &= ~0xFF;
+ config |= sc->sc_maxslots & 0xFF;
+ xhci_op_write_4(sc, XHCI_CONFIG, config);
+
+ usbd_status err;
+
+ err = xhci_ring_init(sc, &sc->sc_cr, XHCI_COMMAND_RING_TRBS,
+ XHCI_COMMAND_RING_SEGMENTS_ALIGN);
+ if (err) {
+ aprint_error_dev(sc->sc_dev, "command ring init fail\n");
+ return err;
+ }
+
+ err = xhci_ring_init(sc, &sc->sc_er, XHCI_EVENT_RING_TRBS,
+ XHCI_EVENT_RING_SEGMENTS_ALIGN);
+ if (err) {
+ aprint_error_dev(sc->sc_dev, "event ring init fail\n");
+ return err;
+ }
+
+ {
+ usb_dma_t *dma;
+ size_t size;
+ size_t align;
+
+ dma = &sc->sc_eventst_dma;
+ size = roundup2(XHCI_EVENT_RING_SEGMENTS * XHCI_ERSTE_SIZE,
+ XHCI_EVENT_RING_SEGMENT_TABLE_ALIGN);
+ KASSERT(size <= (512 * 1024));
+ align = XHCI_EVENT_RING_SEGMENT_TABLE_ALIGN;
+ err = usb_allocmem(&sc->sc_bus, size, align, dma);
+ memset(KERNADDR(dma, 0), 0, size);
+ usb_syncmem(dma, 0, size, BUS_DMASYNC_PREWRITE);
+ device_printf(sc->sc_dev, "eventst: %s %016jx %p %zx\n",
+ usbd_errstr(err),
+ (uintmax_t)DMAADDR(&sc->sc_eventst_dma, 0),
+ KERNADDR(&sc->sc_eventst_dma, 0),
+ sc->sc_eventst_dma.block->size);
+
+ dma = &sc->sc_dcbaa_dma;
+ size = (1 + sc->sc_maxslots) * sizeof(uint64_t);
+ KASSERT(size <= 2048);
+ align = XHCI_DEVICE_CONTEXT_BASE_ADDRESS_ARRAY_ALIGN;
+ err = usb_allocmem(&sc->sc_bus, size, align, dma);
+ memset(KERNADDR(dma, 0), 0, size);
+ usb_syncmem(dma, 0, size, BUS_DMASYNC_PREWRITE);
+ device_printf(sc->sc_dev, "dcbaa: %s %016jx %p %zx\n",
+ usbd_errstr(err),
+ (uintmax_t)DMAADDR(&sc->sc_dcbaa_dma, 0),
+ KERNADDR(&sc->sc_dcbaa_dma, 0),
+ sc->sc_dcbaa_dma.block->size);
+ }
+
+ sc->sc_slots = kmem_zalloc(sizeof(*sc->sc_slots) * sc->sc_maxslots,
+ KM_SLEEP);
+
+ cv_init(&sc->sc_command_cv, "xhcicmd");
+
+ struct xhci_erste *erst;
+ erst = KERNADDR(&sc->sc_eventst_dma, 0);
+ erst[0].erste_0 = htole64(xhci_ring_trbp(&sc->sc_er, 0));
+ erst[0].erste_2 = htole32(XHCI_EVENT_RING_TRBS);
+ erst[0].erste_3 = htole32(0);
+ usb_syncmem(&sc->sc_eventst_dma, 0,
+ XHCI_ERSTE_SIZE * XHCI_EVENT_RING_SEGMENTS, BUS_DMASYNC_PREWRITE);
+
+ xhci_rt_write_4(sc, XHCI_ERSTSZ(0), XHCI_EVENT_RING_SEGMENTS);
+ xhci_rt_write_8(sc, XHCI_ERSTBA(0), DMAADDR(&sc->sc_eventst_dma, 0));
+ xhci_rt_write_8(sc, XHCI_ERDP(0), xhci_ring_trbp(&sc->sc_er, 0) |
+ XHCI_ERDP_LO_BUSY);
+ xhci_op_write_8(sc, XHCI_DCBAAP, DMAADDR(&sc->sc_dcbaa_dma, 0));
+ xhci_op_write_8(sc, XHCI_CRCR, xhci_ring_trbp(&sc->sc_cr, 0) |
+ sc->sc_cr.xr_cs);
+
+#if 0
+ hexdump("eventst", KERNADDR(&sc->sc_eventst_dma, 0),
+ XHCI_ERSTE_SIZE * XHCI_EVENT_RING_SEGMENTS);
+#endif
+
+ xhci_rt_write_4(sc, XHCI_IMAN(0), XHCI_IMAN_INTR_ENA);
+ xhci_rt_write_4(sc, XHCI_IMOD(0), 0);
+
+ xhci_op_write_4(sc, XHCI_USBCMD, XHCI_CMD_INTE|XHCI_CMD_RS); /* Go! */
+ device_printf(sc->sc_dev, "USBCMD %08"PRIx32"\n",
+ xhci_op_read_4(sc, XHCI_USBCMD));
+
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
+ mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
+ cv_init(&sc->sc_softwake_cv, "xhciab");
+
+ sc->sc_xferpool = pool_cache_init(sizeof(struct xhci_xfer), 0, 0, 0,
+ "xhcixfer", NULL, IPL_USB, NULL, NULL, NULL);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &xhci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct xhci_pipe);
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+int
+xhci_intr(void *v)
+{
+ struct xhci_softc * const sc = v;
+
+ if (sc == NULL || sc->sc_dying || !device_has_power(sc->sc_dev))
+ return 0;
+
+ DPRINTF(("%s: %s\n", __func__, device_xname(sc->sc_dev)));
+
+ /* If we get an interrupt while polling, then just ignore it. */
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ DPRINTFN(16, ("xhci_intr: ignored interrupt while polling\n"));
+#endif
+ return 0;
+ }
+
+ return xhci_intr1(sc);
+}
+
+int
+xhci_intr1(struct xhci_softc * const sc)
+{
+ uint32_t usbsts;
+ uint32_t iman;
+
+ usbsts = xhci_op_read_4(sc, XHCI_USBSTS);
+ //device_printf(sc->sc_dev, "%s USBSTS %08x\n", __func__, usbsts);
+#if 0
+ if ((usbsts & (XHCI_STS_EINT|XHCI_STS_PCD)) == 0) {
+ return 0;
+ }
+#endif
+ xhci_op_write_4(sc, XHCI_USBSTS,
+ usbsts & (2|XHCI_STS_EINT|XHCI_STS_PCD)); /* XXX */
+ usbsts = xhci_op_read_4(sc, XHCI_USBSTS);
+ //device_printf(sc->sc_dev, "%s USBSTS %08x\n", __func__, usbsts);
+
+ iman = xhci_rt_read_4(sc, XHCI_IMAN(0));
+ //device_printf(sc->sc_dev, "%s IMAN0 %08x\n", __func__, iman);
+ if ((iman & XHCI_IMAN_INTR_PEND) == 0) {
+ return 0;
+ }
+ xhci_rt_write_4(sc, XHCI_IMAN(0), iman);
+ iman = xhci_rt_read_4(sc, XHCI_IMAN(0));
+ //device_printf(sc->sc_dev, "%s IMAN0 %08x\n", __func__, iman);
+ usbsts = xhci_op_read_4(sc, XHCI_USBSTS);
+ //device_printf(sc->sc_dev, "%s USBSTS %08x\n", __func__, usbsts);
+
+ sc->sc_bus.no_intrs++;
+ usb_schedsoftintr(&sc->sc_bus);
+
+ return 1;
+}
+
+static usbd_status
+xhci_configure_endpoint(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(pipe->endpoint->edesc);
+ usb_endpoint_descriptor_t * const ed = pipe->endpoint->edesc;
+ const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+ struct xhci_trb trb;
+ usbd_status err;
+ uint32_t *cp;
+
+ device_printf(sc->sc_dev, "%s dci %u (0x%x)\n", __func__, dci,
+ pipe->endpoint->edesc->bEndpointAddress);
+
+ /* XXX ensure input context is available? */
+
+ memset(xhci_slot_get_icv(sc, xs, 0), 0, sc->sc_pgsz);
+
+ cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
+ cp[0] = htole32(0);
+ cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(dci));
+
+ /* set up input slot context */
+ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
+ cp[0] = htole32(XHCI_SCTX_0_CTX_NUM_SET(dci));
+ cp[1] = htole32(0);
+ cp[2] = htole32(0);
+ cp[3] = htole32(0);
+
+ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(dci));
+ if (xfertype == UE_INTERRUPT) {
+ cp[0] = htole32(
+ XHCI_EPCTX_0_IVAL_SET(3) /* XXX */
+ );
+ cp[1] = htole32(
+ XHCI_EPCTX_1_CERR_SET(3) |
+ XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(pipe->endpoint->edesc)) |
+ XHCI_EPCTX_1_MAXB_SET(0) |
+ XHCI_EPCTX_1_MAXP_SIZE_SET(8) /* XXX */
+ );
+ cp[4] = htole32(
+ XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)
+ );
+ } else {
+ cp[0] = htole32(0);
+ cp[1] = htole32(
+ XHCI_EPCTX_1_CERR_SET(3) |
+ XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(pipe->endpoint->edesc)) |
+ XHCI_EPCTX_1_MAXB_SET(0) |
+ XHCI_EPCTX_1_MAXP_SIZE_SET(512) /* XXX */
+ );
+ }
+ *(uint64_t *)(&cp[2]) = htole64(
+ xhci_ring_trbp(&xs->xs_ep[dci].xe_tr, 0) |
+ XHCI_EPCTX_2_DCS_SET(1));
+
+ /* sync input contexts before they are read from memory */
+ usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
+ hexdump("input control context", xhci_slot_get_icv(sc, xs, 0),
+ sc->sc_ctxsz * 1);
+ hexdump("input endpoint context", xhci_slot_get_icv(sc, xs,
+ xhci_dci_to_ici(dci)), sc->sc_ctxsz * 1);
+
+ trb.trb_0 = xhci_slot_get_icp(sc, xs, 0);
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+
+ usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
+ hexdump("output context", xhci_slot_get_dcv(sc, xs, dci),
+ sc->sc_ctxsz * 1);
+
+ return err;
+}
+
+static usbd_status
+xhci_unconfigure_endpoint(usbd_pipe_handle pipe)
+{
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+xhci_reset_endpoint(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(pipe->endpoint->edesc);
+ struct xhci_trb trb;
+ usbd_status err;
+
+ device_printf(sc->sc_dev, "%s\n", __func__);
+
+ trb.trb_0 = 0;
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+ XHCI_TRB_3_EP_SET(dci) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+
+ return err;
+}
+
+#if 0
+static usbd_status
+xhci_stop_endpoint(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = pipe->device->hci_private;
+ struct xhci_trb trb;
+ usbd_status err;
+ const u_int dci = xhci_ep_get_dci(pipe->endpoint->edesc);
+
+ device_printf(sc->sc_dev, "%s\n", __func__);
+
+ trb.trb_0 = 0;
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+ XHCI_TRB_3_EP_SET(dci) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+
+ return err;
+}
+#endif
+
+static usbd_status
+xhci_set_dequeue(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(pipe->endpoint->edesc);
+ struct xhci_ring * const xr = &xs->xs_ep[dci].xe_tr;
+ struct xhci_trb trb;
+ usbd_status err;
+
+ device_printf(sc->sc_dev, "%s\n", __func__);
+
+ memset(xr->xr_trb, 0, xr->xr_ntrb * XHCI_TRB_SIZE);
+ usb_syncmem(&xr->xr_dma, 0, xr->xr_ntrb * XHCI_TRB_SIZE,
+ BUS_DMASYNC_PREWRITE);
+
+ xr->xr_ep = 0;
+ xr->xr_cs = 1;
+
+ trb.trb_0 = xhci_ring_trbp(xr, 0) | 1; /* XXX */
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+ XHCI_TRB_3_EP_SET(dci) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+
+ return err;
+}
+
+static usbd_status
+xhci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle const dev = pipe->device;
+ struct xhci_softc * const sc = dev->bus->hci_private;
+ usb_endpoint_descriptor_t * const ed = pipe->endpoint->edesc;
+ const int8_t addr = dev->address;
+ const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+
+ DPRINTF(("%s\n", __func__));
+ DPRINTF(("addr %d\n", addr));
+ device_printf(sc->sc_dev, "%s addr %d depth %d port %d speed %d\n",
+ __func__, addr, dev->depth, dev->powersrc->portno, dev->speed);
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ /* Root Hub */
+ if (dev->depth == 0 && dev->powersrc->portno == 0 &&
+ dev->speed != USB_SPEED_SUPER) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &xhci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | XHCI_INTR_ENDPT:
+ pipe->methods = &xhci_root_intr_methods;
+ break;
+ default:
+ pipe->methods = NULL;
+ DPRINTF(("xhci_open: bad bEndpointAddress 0x%02x\n",
+ ed->bEndpointAddress));
+ return USBD_INVAL;
+ }
+ return USBD_NORMAL_COMPLETION;
+ }
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ pipe->methods = &xhci_device_ctrl_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ pipe->methods = &xhci_device_isoc_methods;
+ return USBD_INVAL;
+ break;
+ case UE_BULK:
+ pipe->methods = &xhci_device_bulk_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &xhci_device_intr_methods;
+ break;
+ default:
+ return USBD_IOERROR;
+ break;
+ }
+
+ if (ed->bEndpointAddress != USB_CONTROL_ENDPOINT)
+ xhci_configure_endpoint(pipe);
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+xhci_rhpsc(struct xhci_softc * const sc, u_int port)
+{
+ usbd_xfer_handle const xfer = sc->sc_intrxfer;
+ uint8_t *p;
+
+ device_printf(sc->sc_dev, "port %u status change\n", port);
+
+ if (xfer == NULL)
+ return;
+
+ if (!(port >= sc->sc_hs_port_start &&
+ port < sc->sc_hs_port_start + sc->sc_hs_port_count))
+ return;
+
+ port -= sc->sc_hs_port_start;
+ port += 1;
+ device_printf(sc->sc_dev, "hs port %u status change\n", port);
+
+ p = KERNADDR(&xfer->dmabuf, 0);
+ memset(p, 0, xfer->length);
+ p[port/NBBY] |= 1 << (port%NBBY);
+ xfer->actlen = xfer->length;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ usb_transfer_complete(xfer);
+}
+
+static void
+xhci_handle_event(struct xhci_softc * const sc, const struct xhci_trb * const trb)
+{
+ uint64_t trb_0;
+ uint32_t trb_2, trb_3;
+
+ DPRINTF(("%s: %s\n", __func__, device_xname(sc->sc_dev)));
+
+ trb_0 = le64toh(trb->trb_0);
+ trb_2 = le32toh(trb->trb_2);
+ trb_3 = le32toh(trb->trb_3);
+
+#if 0
+ device_printf(sc->sc_dev,
+ "event: %p 0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32"\n", trb,
+ trb_0, trb_2, trb_3);
+#endif
+
+ switch (XHCI_TRB_3_TYPE_GET(trb_3)){
+ case XHCI_TRB_EVENT_TRANSFER: {
+ u_int slot, dci;
+ struct xhci_slot *xs;
+ struct xhci_ring *xr;
+ struct xhci_xfer *xx;
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ slot = XHCI_TRB_3_SLOT_GET(trb_3);
+ dci = XHCI_TRB_3_EP_GET(trb_3);
+
+ xs = &sc->sc_slots[slot];
+ xr = &xs->xs_ep[dci].xe_tr;
+
+ if ((trb_3 & XHCI_TRB_3_ED_BIT) == 0) {
+ xx = xr->xr_cookies[(trb_0 - xhci_ring_trbp(xr, 0))/
+ sizeof(struct xhci_trb)];
+ } else {
+ xx = (void *)(uintptr_t)(trb_0 & ~0x3);
+ }
+ xfer = &xx->xx_xfer;
+#if 0
+ device_printf(sc->sc_dev, "%s xfer %p\n", __func__, xfer);
+#endif
+
+ if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) {
+#if 0
+ device_printf(sc->sc_dev, "transfer event data: "
+ "0x%016"PRIx64" 0x%08"PRIx32" %02x\n",
+ trb_0, XHCI_TRB_2_REM_GET(trb_2),
+ XHCI_TRB_2_ERROR_GET(trb_2));
+#endif
+ if ((trb_0 & 0x3) == 0x3) {
+ xfer->actlen = XHCI_TRB_2_REM_GET(trb_2);
+ }
+ }
+
+ if (XHCI_TRB_2_ERROR_GET(trb_2) ==
+ XHCI_TRB_ERROR_SUCCESS) {
+ xfer->actlen = xfer->length - XHCI_TRB_2_REM_GET(trb_2);
+ err = USBD_NORMAL_COMPLETION;
+ } else if (XHCI_TRB_2_ERROR_GET(trb_2) ==
+ XHCI_TRB_ERROR_SHORT_PKT) {
+ xfer->actlen = xfer->length - XHCI_TRB_2_REM_GET(trb_2);
+ err = USBD_NORMAL_COMPLETION;
+ } else if (XHCI_TRB_2_ERROR_GET(trb_2) ==
+ XHCI_TRB_ERROR_STALL) {
+ err = USBD_STALLED;
+ xr->is_halted = true;
+ } else {
+ err = USBD_IOERROR;
+ }
+ xfer->status = err;
+
+ //mutex_enter(&sc->sc_lock); /* XXX ??? */
+ if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) {
+ if ((trb_0 & 0x3) == 0x0) {
+ usb_transfer_complete(xfer);
+ }
+ } else {
+ usb_transfer_complete(xfer);
+ }
+ //mutex_exit(&sc->sc_lock); /* XXX ??? */
+
+ }
+ break;
+ case XHCI_TRB_EVENT_CMD_COMPLETE:
+ if (trb_0 == sc->sc_command_addr) {
+ sc->sc_result_trb.trb_0 = trb_0;
+ sc->sc_result_trb.trb_2 = trb_2;
+ sc->sc_result_trb.trb_3 = trb_3;
+ if (XHCI_TRB_2_ERROR_GET(trb_2) !=
+ XHCI_TRB_ERROR_SUCCESS) {
+ device_printf(sc->sc_dev, "command completion "
+ "failure: 0x%016"PRIx64" 0x%08"PRIx32" "
+ "0x%08"PRIx32"\n", trb_0, trb_2, trb_3);
+ }
+ cv_signal(&sc->sc_command_cv);
+ } else {
+ device_printf(sc->sc_dev, "event: %p 0x%016"PRIx64" "
+ "0x%08"PRIx32" 0x%08"PRIx32"\n", trb, trb_0,
+ trb_2, trb_3);
+ }
+ break;
+ case XHCI_TRB_EVENT_PORT_STS_CHANGE:
+ xhci_rhpsc(sc, (uint32_t)((trb_0 >> 24) & 0xff));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+xhci_softintr(void *v)
+{
+ struct usbd_bus * const bus = v;
+ struct xhci_softc * const sc = bus->hci_private;
+ struct xhci_ring * const er = &sc->sc_er;
+ struct xhci_trb *trb;
+ int i, j, k;
+
+ DPRINTF(("%s: %s\n", __func__, device_xname(sc->sc_dev)));
+
+ i = er->xr_ep;
+ j = er->xr_cs;
+
+ while (1) {
+ usb_syncmem(&er->xr_dma, XHCI_TRB_SIZE * i, XHCI_TRB_SIZE,
+ BUS_DMASYNC_POSTREAD);
+ trb = &er->xr_trb[i];
+ k = (le32toh(trb->trb_3) & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0;
+
+ if (j != k)
+ break;
+
+ xhci_handle_event(sc, trb);
+
+ i++;
+ if (i == XHCI_EVENT_RING_TRBS) {
+ i = 0;
+ j ^= 1;
+ }
+ }
+
+ er->xr_ep = i;
+ er->xr_cs = j;
+
+ xhci_rt_write_8(sc, XHCI_ERDP(0), xhci_ring_trbp(er, er->xr_ep) |
+ XHCI_ERDP_LO_BUSY);
+
+ DPRINTF(("%s: %s ends\n", __func__, device_xname(sc->sc_dev)));
+
+ return;
+}
+
+static void
+xhci_poll(struct usbd_bus *bus)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+
+ DPRINTF(("%s: %s\n", __func__, device_xname(sc->sc_dev)));
+
+ xhci_intr1(sc);
+
+ return;
+}
+
+static usbd_status
+xhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, uint32_t size)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+ usbd_status err;
+
+ DPRINTF(("%s\n", __func__));
+
+ err = usb_allocmem_flags(&sc->sc_bus, size, 0, dma, 0);
+#if 0
+ if (err == USBD_NOMEM)
+ err = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size);
+#endif
+#ifdef XHCI_DEBUG
+ if (err)
+ device_printf(sc->sc_dev, "xhci_allocm: usb_allocmem()=%d\n",
+ err);
+#endif
+
+ return err;
+}
+
+static void
+xhci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+
+// DPRINTF(("%s\n", __func__));
+
+#if 0
+ if (dma->block->flags & USB_DMA_RESERVE) {
+ usb_reserve_freem(&sc->sc_dma_reserve, dma);
+ return;
+ }
+#endif
+ usb_freemem(&sc->sc_bus, dma);
+}
+
+static usbd_xfer_handle
+xhci_allocx(struct usbd_bus *bus)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+ usbd_xfer_handle xfer;
+
+// DPRINTF(("%s\n", __func__));
+
+ xfer = pool_cache_get(sc->sc_xferpool, PR_NOWAIT);
+ if (xfer != NULL) {
+#ifdef DIAGNOSTIC
+ memset(xfer, 0, sizeof(struct xhci_xfer));
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+
+ return xfer;
+}
+
+static void
+xhci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+
+// DPRINTF(("%s\n", __func__));
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ device_printf(sc->sc_dev, "xhci_freex: xfer=%p "
+ "not busy, 0x%08x\n", xfer, xfer->busy_free);
+ }
+ xfer->busy_free = XFER_FREE;
+#endif
+ pool_cache_put(sc->sc_xferpool, xfer);
+}
+
+static void
+xhci_get_lock(struct usbd_bus *bus, kmutex_t **lock)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+
+ *lock = &sc->sc_lock;
+}
+
+extern u_int32_t usb_cookie_no;
+
+static usbd_status
+xhci_new_device(device_t parent, usbd_bus_handle bus, int depth,
+ int speed, int port, struct usbd_port *up)
+{
+ struct xhci_softc * const sc = bus->hci_private;
+ usbd_device_handle dev;
+ usbd_status err;
+ usb_device_descriptor_t *dd;
+ struct usbd_device *hub;
+ struct usbd_device *adev;
+ int rhport = 0;
+ struct xhci_slot *xs;
+ uint32_t *cp;
+ uint8_t slot;
+ uint8_t addr;
+
+ dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO);
+ if (dev == NULL)
+ return USBD_NOMEM;
+
+ dev->bus = bus;
+
+ /* Set up default endpoint handle. */
+ dev->def_ep.edesc = &dev->def_ep_desc;
+
+ /* Set up default endpoint descriptor. */
+ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
+ dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ dev->def_ep_desc.bmAttributes = UE_CONTROL;
+ /* XXX */
+ USETW(dev->def_ep_desc.wMaxPacketSize, 64);
+ dev->def_ep_desc.bInterval = 0;
+
+ /* doesn't matter, just don't let it uninitialized */
+ dev->def_ep.datatoggle = 0;
+
+ device_printf(sc->sc_dev, "%s up %p portno %d\n", __func__, up,
+ up->portno);
+
+ dev->quirks = &usbd_no_quirk;
+ dev->address = 0;
+ dev->ddesc.bMaxPacketSize = 0;
+ dev->depth = depth;
+ dev->powersrc = up;
+ dev->myhub = up->parent;
+
+ up->device = dev;
+
+ /* Locate root hub port */
+ for (adev = dev, hub = dev;
+ hub != NULL;
+ adev = hub, hub = hub->myhub) {
+ device_printf(sc->sc_dev, "%s hub %p\n", __func__, hub);
+ }
+ device_printf(sc->sc_dev, "%s hub %p\n", __func__, hub);
+
+ if (hub != NULL) {
+ for (int p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+ if (hub->hub->ports[p].device == adev) {
+ rhport = p;
+ }
+ }
+ } else {
+ rhport = port;
+ }
+ if (speed == USB_SPEED_SUPER) {
+ rhport += sc->sc_ss_port_start - 1;
+ } else {
+ rhport += sc->sc_hs_port_start - 1;
+ }
+ device_printf(sc->sc_dev, "%s rhport %d\n", __func__, rhport);
+
+ dev->speed = speed;
+ dev->langid = USBD_NOLANG;
+ dev->cookie.cookie = ++usb_cookie_no;
+
+ /* Establish the default pipe. */
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
+ &dev->default_pipe);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ dd = &dev->ddesc;
+
+ if ((depth == 0) && (port == 0)) {
+ KASSERT(bus->devices[dev->address] == NULL);
+ bus->devices[dev->address] = dev;
+ err = usbd_get_initial_ddesc(dev, dd);
+ if (err)
+ return err;
+ err = usbd_reload_device_desc(dev);
+ if (err)
+ return err;
+ } else {
+ err = xhci_enable_slot(sc, &slot);
+ if (err)
+ return err;
+ err = xhci_init_slot(sc, slot, depth, speed, port, rhport);
+ if (err)
+ return err;
+ xs = &sc->sc_slots[slot];
+ dev->hci_private = xs;
+ cp = xhci_slot_get_dcv(sc, xs, XHCI_DCI_SLOT);
+ //hexdump("slot context", cp, sc->sc_ctxsz);
+ addr = XHCI_SCTX_3_DEV_ADDR_GET(cp[3]);
+ device_printf(sc->sc_dev, "%s device address %u\n",
+ __func__, addr);
+ /* XXX ensure we know when the hardware does something
+ we can't yet cope with */
+ KASSERT(addr >= 1 && addr <= 127);
+ dev->address = addr;
+ /* XXX dev->address not necessarily unique on bus */
+ KASSERT(bus->devices[dev->address] == NULL);
+ bus->devices[dev->address] = dev;
+
+ err = usbd_get_initial_ddesc(dev, dd);
+ if (err)
+ return err;
+ USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
+ device_printf(sc->sc_dev, "%s bMaxPacketSize %u\n", __func__, dd->bMaxPacketSize);
+ xhci_update_ep0_mps(sc, xs, dd->bMaxPacketSize);
+ err = usbd_reload_device_desc(dev);
+ if (err)
+ return err;
+
+ usbd_kill_pipe(dev->default_pipe);
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep,
+ USBD_DEFAULT_INTERVAL, &dev->default_pipe);
+ }
+
+ DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, noconf=%d, "
+ "speed=%d\n", dev->address,UGETW(dd->bcdUSB),
+ dd->bDeviceClass, dd->bDeviceSubClass, dd->bDeviceProtocol,
+ dd->bMaxPacketSize, dd->bLength, dd->bNumConfigurations,
+ dev->speed));
+
+ usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev);
+
+ if ((depth == 0) && (port == 0)) {
+ usbd_attach_roothub(parent, dev);
+ device_printf(sc->sc_dev, "root_hub %p\n", bus->root_hub);
+ return USBD_NORMAL_COMPLETION;
+ }
+
+
+ err = usbd_probe_and_attach(parent, dev, port, dev->address);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+xhci_ring_init(struct xhci_softc * const sc, struct xhci_ring * const xr,
+ size_t ntrb, size_t align)
+{
+ usbd_status err;
+ size_t size = ntrb * XHCI_TRB_SIZE;
+
+ err = usb_allocmem(&sc->sc_bus, size, align, &xr->xr_dma);
+ if (err)
+ return err;
+ mutex_init(&xr->xr_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
+ xr->xr_cookies = kmem_zalloc(sizeof(*xr->xr_cookies) * ntrb, KM_SLEEP);
+ xr->xr_trb = xhci_ring_trbv(xr, 0);
+ xr->xr_ntrb = ntrb;
+ xr->xr_ep = 0;
+ xr->xr_cs = 1;
+ memset(xr->xr_trb, 0, size);
+ usb_syncmem(&xr->xr_dma, 0, size, BUS_DMASYNC_PREWRITE);
+ xr->is_halted = false;
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+xhci_ring_free(struct xhci_softc * const sc, struct xhci_ring * const xr)
+{
+ usb_freemem(&sc->sc_bus, &xr->xr_dma);
+ mutex_destroy(&xr->xr_lock);
+ kmem_free(xr->xr_cookies, sizeof(*xr->xr_cookies) * xr->xr_ntrb);
+}
+
+static void
+xhci_ring_put(struct xhci_softc * const sc, struct xhci_ring * const xr,
+ void *cookie, struct xhci_trb * const trbs, size_t ntrbs)
+{
+ size_t i;
+ u_int ri;
+ u_int cs;
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+
+ for (i = 0; i < ntrbs; i++) {
+#if 0
+ device_printf(sc->sc_dev, "%s %p %p %zu "
+ "%016"PRIx64" %08"PRIx32" %08"PRIx32"\n", __func__, xr,
+ trbs, i, trbs[i].trb_0, trbs[i].trb_2, trbs[i].trb_3);
+#endif
+ KASSERT(XHCI_TRB_3_TYPE_GET(trbs[i].trb_3) !=
+ XHCI_TRB_TYPE_LINK);
+ }
+
+#if 0
+ device_printf(sc->sc_dev, "%s %p xr_ep 0x%x xr_cs %u\n", __func__,
+ xr, xr->xr_ep, xr->xr_cs);
+#endif
+
+ ri = xr->xr_ep;
+ cs = xr->xr_cs;
+
+ if (ri + ntrbs >= (xr->xr_ntrb - 1)) {
+ parameter = xhci_ring_trbp(xr, 0);
+ status = 0;
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
+ XHCI_TRB_3_TC_BIT | (cs ? XHCI_TRB_3_CYCLE_BIT : 0);
+ xhci_trb_put(&xr->xr_trb[ri], htole64(parameter),
+ htole32(status), htole32(control));
+ usb_syncmem(&xr->xr_dma, XHCI_TRB_SIZE * ri, XHCI_TRB_SIZE * 1,
+ BUS_DMASYNC_PREWRITE);
+ xr->xr_cookies[ri] = NULL;
+ xr->xr_ep = 0;
+ xr->xr_cs ^= 1;
+ ri = xr->xr_ep;
+ cs = xr->xr_cs;
+ }
+
+ ri++;
+
+ for (i = 1; i < ntrbs; i++) {
+ parameter = trbs[i].trb_0;
+ status = trbs[i].trb_2;
+ control = trbs[i].trb_3;
+
+ if (cs) {
+ control |= XHCI_TRB_3_CYCLE_BIT;
+ } else {
+ control &= ~XHCI_TRB_3_CYCLE_BIT;
+ }
+
+ xhci_trb_put(&xr->xr_trb[ri], htole64(parameter),
+ htole32(status), htole32(control));
+ usb_syncmem(&xr->xr_dma, XHCI_TRB_SIZE * ri, XHCI_TRB_SIZE * 1,
+ BUS_DMASYNC_PREWRITE);
+ xr->xr_cookies[ri] = cookie;
+ ri++;
+ }
+
+ i = 0;
+ {
+ parameter = trbs[i].trb_0;
+ status = trbs[i].trb_2;
+ control = trbs[i].trb_3;
+
+ if (xr->xr_cs) {
+ control |= XHCI_TRB_3_CYCLE_BIT;
+ } else {
+ control &= ~XHCI_TRB_3_CYCLE_BIT;
+ }
+
+ xhci_trb_put(&xr->xr_trb[xr->xr_ep], htole64(parameter),
+ htole32(status), htole32(control));
+ usb_syncmem(&xr->xr_dma, XHCI_TRB_SIZE * ri, XHCI_TRB_SIZE * 1,
+ BUS_DMASYNC_PREWRITE);
+ xr->xr_cookies[xr->xr_ep] = cookie;
+ }
+
+ xr->xr_ep = ri;
+ xr->xr_cs = cs;
+
+#if 0
+ device_printf(sc->sc_dev, "%s %p xr_ep 0x%x xr_cs %u\n", __func__,
+ xr, xr->xr_ep, xr->xr_cs);
+#endif
+}
+
+static usbd_status
+xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb,
+ int timeout)
+{
+ struct xhci_ring * const cr = &sc->sc_cr;
+ usbd_status err;
+
+ device_printf(sc->sc_dev, "%s input: "
+ "0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32"\n", __func__,
+ trb->trb_0, trb->trb_2, trb->trb_3);
+
+ mutex_enter(&sc->sc_lock);
+
+ KASSERT(sc->sc_command_addr == 0);
+ sc->sc_command_addr = xhci_ring_trbp(cr, cr->xr_ep);
+
+ mutex_enter(&cr->xr_lock);
+ xhci_ring_put(sc, cr, NULL, trb, 1);
+ mutex_exit(&cr->xr_lock);
+
+ xhci_db_write_4(sc, XHCI_DOORBELL(0), 0);
+
+ if (cv_timedwait(&sc->sc_command_cv, &sc->sc_lock,
+ MAX(1, mstohz(timeout))) == EWOULDBLOCK) {
+ err = USBD_TIMEOUT;
+ goto timedout;
+ }
+
+ trb->trb_0 = sc->sc_result_trb.trb_0;
+ trb->trb_2 = sc->sc_result_trb.trb_2;
+ trb->trb_3 = sc->sc_result_trb.trb_3;
+
+ device_printf(sc->sc_dev, "%s output: "
+ "0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32"\n", __func__,
+ trb->trb_0, trb->trb_2, trb->trb_3);
+
+ switch (XHCI_TRB_2_ERROR_GET(trb->trb_2)) {
+ case XHCI_TRB_ERROR_SUCCESS:
+ err = USBD_NORMAL_COMPLETION;
+ break;
+ default:
+ case 192 ... 223:
+ err = USBD_IOERROR;
+ break;
+ case 224 ... 255:
+ err = USBD_NORMAL_COMPLETION;
+ break;
+ }
+
+timedout:
+ sc->sc_command_addr = 0;
+ mutex_exit(&sc->sc_lock);
+ return err;
+}
+
+static usbd_status
+xhci_enable_slot(struct xhci_softc * const sc, uint8_t * const slotp)
+{
+ struct xhci_trb trb;
+ usbd_status err;
+
+ trb.trb_0 = 0;
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ENABLE_SLOT);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+ if (err != USBD_NORMAL_COMPLETION) {
+ return err;
+ }
+
+ *slotp = XHCI_TRB_3_SLOT_GET(trb.trb_3);
+
+ return err;
+}
+
+static usbd_status
+xhci_address_device(struct xhci_softc * const sc,
+ uint64_t icp, uint8_t slot_id, bool bsr)
+{
+ struct xhci_trb trb;
+ usbd_status err;
+
+ trb.trb_0 = icp;
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(slot_id) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ADDRESS_DEVICE) |
+ (bsr ? XHCI_TRB_3_BSR_BIT : 0);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+ return err;
+}
+
+static usbd_status
+xhci_update_ep0_mps(struct xhci_softc * const sc,
+ struct xhci_slot * const xs, u_int mps)
+{
+ struct xhci_trb trb;
+ usbd_status err;
+ uint32_t * cp;
+
+ device_printf(sc->sc_dev, "%s\n", __func__);
+
+ cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
+ cp[0] = htole32(0);
+ cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(XHCI_DCI_EP_CONTROL));
+
+ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_EP_CONTROL));
+ cp[1] = htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
+
+ /* sync input contexts before they are read from memory */
+ usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
+ hexdump("input context", xhci_slot_get_icv(sc, xs, 0),
+ sc->sc_ctxsz * 4);
+
+ trb.trb_0 = xhci_slot_get_icp(sc, xs, 0);
+ trb.trb_2 = 0;
+ trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX);
+
+ err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+ KASSERT(err == USBD_NORMAL_COMPLETION); /* XXX */
+ return err;
+}
+
+static void
+xhci_set_dcba(struct xhci_softc * const sc, uint64_t dcba, int si)
+{
+ uint64_t * const dcbaa = KERNADDR(&sc->sc_dcbaa_dma, 0);
+
+ device_printf(sc->sc_dev, "dcbaa %p dc %016"PRIx64" slot %d\n",
+ &dcbaa[si], dcba, si);
+
+ dcbaa[si] = dcba;
+ usb_syncmem(&sc->sc_dcbaa_dma, si * sizeof(uint64_t), sizeof(uint64_t),
+ BUS_DMASYNC_PREWRITE);
+}
+
+static usbd_status
+xhci_init_slot(struct xhci_softc * const sc, uint32_t slot, int depth,
+ int speed, int port, int rhport)
+{
+ struct xhci_slot *xs;
+ usbd_status err;
+ u_int dci;
+ uint32_t *cp;
+ uint32_t mps;
+ uint32_t xspeed;
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ xspeed = 2;
+ mps = USB_MAX_IPACKET;
+ break;
+ case USB_SPEED_FULL:
+ xspeed = 1;
+ mps = 64;
+ break;
+ case USB_SPEED_HIGH:
+ xspeed = 3;
+ mps = USB_2_MAX_CTRL_PACKET;
+ break;
+ case USB_SPEED_SUPER:
+ xspeed = 4;
+ mps = USB_3_MAX_CTRL_PACKET;
+ break;
+ }
+
+ xs = &sc->sc_slots[slot];
+ xs->xs_idx = slot;
+
+ /* allocate contexts */
+ err = usb_allocmem(&sc->sc_bus, sc->sc_pgsz, sc->sc_pgsz,
+ &xs->xs_dc_dma);
+ if (err)
+ return err;
+ memset(KERNADDR(&xs->xs_dc_dma, 0), 0, sc->sc_pgsz);
+
+ err = usb_allocmem(&sc->sc_bus, sc->sc_pgsz, sc->sc_pgsz,
+ &xs->xs_ic_dma);
+ if (err)
+ return err;
+ memset(KERNADDR(&xs->xs_ic_dma, 0), 0, sc->sc_pgsz);
+
+ for (dci = 0; dci < 32; dci++) {
+ //CTASSERT(sizeof(xs->xs_ep[dci]) == sizeof(struct xhci_endpoint));
+ memset(&xs->xs_ep[dci], 0, sizeof(xs->xs_ep[dci]));
+ if (dci == XHCI_DCI_SLOT)
+ continue;
+ err = xhci_ring_init(sc, &xs->xs_ep[dci].xe_tr,
+ XHCI_TRANSFER_RING_TRBS, XHCI_TRB_ALIGN);
+ if (err) {
+ device_printf(sc->sc_dev, "ring init failure\n");
+ return err;
+ }
+ }
+
+ /* set up initial input control context */
+ cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
+ cp[0] = htole32(0);
+ cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(XHCI_DCI_EP_CONTROL)|
+ XHCI_INCTX_1_ADD_MASK(XHCI_DCI_SLOT));
+
+ /* set up input slot context */
+ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
+ cp[0] = htole32(
+ XHCI_SCTX_0_CTX_NUM_SET(1) |
+ XHCI_SCTX_0_SPEED_SET(xspeed)
+ );
+ cp[1] = htole32(
+ XHCI_SCTX_1_RH_PORT_SET(rhport)
+ );
+ cp[2] = htole32(
+ XHCI_SCTX_2_IRQ_TARGET_SET(0)
+ );
+ cp[3] = htole32(0);
+
+ /* set up input EP0 context */
+ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_EP_CONTROL));
+ cp[0] = htole32(0);
+ cp[1] = htole32(
+ XHCI_EPCTX_1_MAXP_SIZE_SET(mps) |
+ XHCI_EPCTX_1_EPTYPE_SET(4) |
+ XHCI_EPCTX_1_CERR_SET(3)
+ );
+ /* can't use xhci_ep_get_dci() yet? */
+ *(uint64_t *)(&cp[2]) = htole64(
+ xhci_ring_trbp(&xs->xs_ep[XHCI_DCI_EP_CONTROL].xe_tr, 0) |
+ XHCI_EPCTX_2_DCS_SET(1));
+ cp[4] = htole32(
+ XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)
+ );
+
+ /* sync input contexts before they are read from memory */
+ usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
+ hexdump("input context", xhci_slot_get_icv(sc, xs, 0),
+ sc->sc_ctxsz * 3);
+
+ xhci_set_dcba(sc, DMAADDR(&xs->xs_dc_dma, 0), slot);
+
+ err = xhci_address_device(sc, xhci_slot_get_icp(sc, xs, 0), slot,
+ false);
+
+ usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
+ hexdump("output context", xhci_slot_get_dcv(sc, xs, 0),
+ sc->sc_ctxsz * 2);
+
+ return err;
+}
+
+/* ----- */
+
+static void
+xhci_noop(usbd_pipe_handle pipe)
+{
+ DPRINTF(("%s\n", __func__));
+}
+
+/* root hub descriptors */
+
+static const usb_device_descriptor_t xhci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_HSHUBSTT, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indexes */
+ 1 /* # of configurations */
+};
+
+static const usb_device_qualifier_t xhci_odevd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE_QUALIFIER, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ 1, /* # of configurations */
+ 0
+};
+
+static const usb_config_descriptor_t xhci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_ATTR_MBO | UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+static const usb_interface_descriptor_t xhci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_HSHUBSTT,
+ 0
+};
+
+static const usb_endpoint_descriptor_t xhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | XHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 12
+};
+
+static const usb_hub_descriptor_t xhci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {""},
+ {""},
+};
+
+/* root hub control */
+
+static usbd_status
+xhci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usbd_status err;
+
+ DPRINTF(("%s\n", __func__));
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /* Pipe isn't running, start first */
+ return (xhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+xhci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usb_port_status_t ps;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ usb_hub_descriptor_t hubd;
+ usbd_status err;
+ int len, value, index;
+ int l, totlen = 0;
+ int port, i;
+ uint32_t v;
+
+ DPRINTF(("%s\n", __func__));
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ req = &xfer->request;
+
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+ len = UGETW(req->wLength);
+
+ if (len != 0)
+ buf = KERNADDR(&xfer->dmabuf, 0);
+
+ DPRINTF(("root req: %02x %02x %04x %04x %04x\n", req->bmRequestType,
+ req->bRequest, value, index, len));
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch(C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(uint8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(8,("xhci_root_ctrl_start: wValue=0x%04x\n", value));
+ if (len == 0)
+ break;
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_devd, l);
+ break;
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_odevd, l);
+ break;
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_confd, l);
+ ((usb_config_descriptor_t *)buf)->bDescriptorType =
+ value >> 8;
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &xhci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &xhci_endpd, l);
+ break;
+ case UDESC_STRING:
+#define sd ((usb_string_descriptor_t *)buf)
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ totlen = usb_makelangtbl(sd, len);
+ break;
+ case 1: /* Vendor */
+ totlen = usb_makestrdesc(sd, len, "NetBSD");
+ break;
+ case 2: /* Product */
+ totlen = usb_makestrdesc(sd, len,
+ "xHCI Root Hub");
+ break;
+ }
+#undef sd
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(uint8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ //sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(4, ("xhci_root_ctrl_start: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index < 1 || index > sc->sc_hs_port_count) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index);
+ v = xhci_op_read_4(sc, port);
+ DPRINTFN(4, ("xhci_root_ctrl_start: portsc=0x%08x\n", v));
+ v &= ~XHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ xhci_op_write_4(sc, port, v &~ XHCI_PS_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ err = USBD_IOERROR;
+ goto ret;
+ case UHF_PORT_POWER:
+ break;
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ err = USBD_IOERROR;
+ goto ret;
+ case UHF_C_PORT_CONNECTION:
+ xhci_op_write_4(sc, port, v | XHCI_PS_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ err = USBD_IOERROR;
+ goto ret;
+ case UHF_C_PORT_RESET:
+ xhci_op_write_4(sc, port, v | XHCI_PS_PRC);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if (len == 0)
+ break;
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ hubd = xhci_hubd;
+ hubd.bNbrPorts = sc->sc_hs_port_count;
+ USETW(hubd.wHubCharacteristics, UHD_PWR_NO_SWITCH);
+ hubd.bPwrOn2PwrGood = 200;
+ for (i = 0, l = sc->sc_maxports; l > 0; i++, l -= 8, v >>= 8)
+ hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8,("xhci_root_ctrl_start: get port status i=%d\n",
+ index));
+ if (index < 1 || index > sc->sc_maxports) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = xhci_op_read_4(sc, XHCI_PORTSC(sc->sc_hs_port_start - 1 +
+ index));
+ DPRINTF(("%s READ_CLASS_OTHER GET_STATUS PORTSC %d (%d) %08x\n",
+ __func__, index, sc->sc_hs_port_start - 1 + index, v));
+ switch (XHCI_PS_SPEED_GET(v)) {
+ case 1:
+ i = UPS_FULL_SPEED;
+ break;
+ case 2:
+ i = UPS_LOW_SPEED;
+ break;
+ case 3:
+ i = UPS_HIGH_SPEED;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+ if (v & XHCI_PS_CCS) i |= UPS_CURRENT_CONNECT_STATUS;
+ if (v & XHCI_PS_PED) i |= UPS_PORT_ENABLED;
+ if (v & XHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR;
+ //if (v & XHCI_PS_SUSP) i |= UPS_SUSPEND;
+ if (v & XHCI_PS_PR) i |= UPS_RESET;
+ if (v & XHCI_PS_PP) i |= UPS_PORT_POWER;
+ USETW(ps.wPortStatus, i);
+ i = 0;
+ if (v & XHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS;
+ if (v & XHCI_PS_PEC) i |= UPS_C_PORT_ENABLED;
+ if (v & XHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR;
+ if (v & XHCI_PS_PRC) i |= UPS_C_PORT_RESET;
+ USETW(ps.wPortChange, i);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index < 1 || index > sc->sc_hs_port_count) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index);
+ v = xhci_op_read_4(sc, port);
+ DPRINTFN(4, ("xhci_root_ctrl_start: portsc=0x%08x\n", v));
+ v &= ~XHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ xhci_op_write_4(sc, port, v | XHCI_PS_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ /* XXX suspend */
+ break;
+ case UHF_PORT_RESET:
+ v &= ~ (XHCI_PS_PED | XHCI_PS_PR);
+ xhci_op_write_4(sc, port, v | XHCI_PS_PR);
+ /* Wait for reset to complete. */
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = xhci_op_read_4(sc, port);
+ if (v & XHCI_PS_PR) {
+ xhci_op_write_4(sc, port, v & ~XHCI_PS_PR);
+ usb_delay_ms(&sc->sc_bus, 10);
+ /* XXX */
+ }
+ break;
+ case UHF_PORT_POWER:
+ /* XXX power control */
+ break;
+ /* XXX more */
+ case UHF_C_PORT_RESET:
+ xhci_op_write_4(sc, port, v | XHCI_PS_PRC);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
+ case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
+ case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
+ case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ret:
+ xfer->status = err;
+ mutex_enter(&sc->sc_lock);
+ usb_transfer_complete(xfer);
+ mutex_exit(&sc->sc_lock);
+ return USBD_IN_PROGRESS;
+}
+
+
+static void
+xhci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+
+static void
+xhci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("%s\n", __func__));
+ /* Nothing to do. */
+}
+
+static void
+xhci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+/* root hub intrerrupt */
+
+static usbd_status
+xhci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usbd_status err;
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /* Pipe isn't running, start first */
+ return (xhci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+xhci_root_intr_start(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ mutex_enter(&sc->sc_lock);
+ sc->sc_intrxfer = xfer;
+ mutex_exit(&sc->sc_lock);
+
+ return USBD_IN_PROGRESS;
+}
+
+static void
+xhci_root_intr_abort(usbd_xfer_handle xfer)
+{
+#ifdef DIAGNOSTIC
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+#endif
+
+ KASSERT(mutex_owned(&sc->sc_lock));
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("%s: remove\n", __func__));
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ usb_transfer_complete(xfer);
+}
+
+static void
+xhci_root_intr_close(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+
+ KASSERT(mutex_owned(&sc->sc_lock));
+
+ DPRINTF(("%s\n", __func__));
+
+ sc->sc_intrxfer = NULL;
+}
+
+static void
+xhci_root_intr_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+/* -------------- */
+/* device control */
+
+static usbd_status
+xhci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usbd_status err;
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (xhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+xhci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+ struct xhci_ring * const tr = &xs->xs_ep[dci].xe_tr;
+ struct xhci_xfer * const xx = (void *)xfer;
+ usb_device_request_t * const req = &xfer->request;
+ const bool isread = UT_GET_DIR(req->bmRequestType) == UT_READ;
+ const uint32_t len = UGETW(req->wLength);
+ usb_dma_t * const dma = &xfer->dmabuf;
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+ u_int i;
+
+ DPRINTF(("%s\n", __func__));
+ DPRINTF(("req: %02x %02x %04x %04x %04x\n", req->bmRequestType,
+ req->bRequest, UGETW(req->wValue), UGETW(req->wIndex),
+ UGETW(req->wLength)));
+
+ /* XXX */
+ if (tr->is_halted) {
+ xhci_reset_endpoint(xfer->pipe);
+ tr->is_halted = false;
+ xhci_set_dequeue(xfer->pipe);
+ }
+
+ /* we rely on the bottom bits for extra info */
+ KASSERT(((uintptr_t)xfer & 0x3) == 0x0);
+
+ KASSERT((xfer->rqflags & URQ_REQUEST) != 0);
+
+ i = 0;
+
+ /* setup phase */
+ memcpy(&parameter, req, sizeof(*req));
+ parameter = le64toh(parameter);
+ status = XHCI_TRB_2_IRQ_SET(0) | XHCI_TRB_2_BYTES_SET(sizeof(*req));
+ control = ((len == 0) ? XHCI_TRB_3_TRT_NONE :
+ (isread ? XHCI_TRB_3_TRT_IN : XHCI_TRB_3_TRT_OUT)) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) |
+ XHCI_TRB_3_IDT_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ if (len == 0)
+ goto no_data;
+
+ /* data phase */
+ parameter = DMAADDR(dma, 0);
+ KASSERT(len <= 0x10000);
+ status = XHCI_TRB_2_IRQ_SET(0) |
+ XHCI_TRB_2_TDSZ_SET(1) |
+ XHCI_TRB_2_BYTES_SET(len);
+ control = (isread ? XHCI_TRB_3_DIR_IN : 0) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE) |
+ XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_ENT_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ parameter = (uintptr_t)xfer | 0x3;
+ status = XHCI_TRB_2_IRQ_SET(0);
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVENT_DATA) |
+ XHCI_TRB_3_IOC_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+no_data:
+ parameter = 0;
+ status = XHCI_TRB_2_IRQ_SET(0) | XHCI_TRB_2_TDSZ_SET(1);
+ /* the status stage has inverted direction */
+ control = (isread ? 0 : XHCI_TRB_3_DIR_IN) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE) |
+ XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_ENT_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ parameter = (uintptr_t)xfer | 0x0;
+ status = XHCI_TRB_2_IRQ_SET(0);
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVENT_DATA) |
+ XHCI_TRB_3_IOC_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ mutex_enter(&tr->xr_lock);
+ xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
+ mutex_exit(&tr->xr_lock);
+
+ xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
+
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout),
+ xhci_timeout, xfer);
+ }
+
+ if (sc->sc_bus.use_polling) {
+ device_printf(sc->sc_dev, "%s polling\n", __func__);
+ //xhci_waitintr(sc, xfer);
+ }
+
+ return USBD_IN_PROGRESS;
+}
+
+static void
+xhci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(("%s\n", __func__));
+
+ callout_stop(&xfer->timeout_handle); /* XXX wrong place */
+
+}
+
+static void
+xhci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("%s\n", __func__));
+}
+
+static void
+xhci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("%s\n", __func__));
+}
+
+/* ----------------- */
+/* device isochronus */
+
+/* ----------- */
+/* device bulk */
+
+static usbd_status
+xhci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usbd_status err;
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (xhci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+xhci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+ struct xhci_ring * const tr = &xs->xs_ep[dci].xe_tr;
+ struct xhci_xfer * const xx = (void *)xfer;
+ const uint32_t len = xfer->length;
+ usb_dma_t * const dma = &xfer->dmabuf;
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+ u_int i = 0;
+
+#if 0
+ device_printf(sc->sc_dev, "%s %p slot %u dci %u\n", __func__, xfer,
+ xs->xs_idx, dci);
+#endif
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ KASSERT((xfer->rqflags & URQ_REQUEST) == 0);
+
+ parameter = DMAADDR(dma, 0);
+ KASSERT(len <= 0x10000);
+ status = XHCI_TRB_2_IRQ_SET(0) |
+ XHCI_TRB_2_TDSZ_SET(1) |
+ XHCI_TRB_2_BYTES_SET(len);
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL) |
+ XHCI_TRB_3_ISP_BIT | XHCI_TRB_3_IOC_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ mutex_enter(&tr->xr_lock);
+ xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
+ mutex_exit(&tr->xr_lock);
+
+ xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
+
+ if (sc->sc_bus.use_polling) {
+ device_printf(sc->sc_dev, "%s polling\n", __func__);
+ //xhci_waitintr(sc, xfer);
+ }
+
+ return USBD_IN_PROGRESS;
+}
+
+static void
+xhci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ //struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ //struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+ //const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+ const u_int endpt = xfer->pipe->endpoint->edesc->bEndpointAddress;
+ const bool isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+
+ DPRINTF(("%s\n", __func__));
+
+#if 0
+ device_printf(sc->sc_dev, "%s %p slot %u dci %u\n", __func__, xfer,
+ xs->xs_idx, dci);
+#endif
+
+ callout_stop(&xfer->timeout_handle); /* XXX wrong place */
+
+ usb_syncmem(&xfer->dmabuf, 0, xfer->length,
+ isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+
+
+}
+
+static void
+xhci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("%s\n", __func__));
+}
+
+static void
+xhci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("%s\n", __func__));
+}
+
+/* --------------- */
+/* device intrrupt */
+
+static usbd_status
+xhci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ usbd_status err;
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (xhci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+xhci_device_intr_start(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+ struct xhci_ring * const tr = &xs->xs_ep[dci].xe_tr;
+ struct xhci_xfer * const xx = (void *)xfer;
+ const uint32_t len = xfer->length;
+ usb_dma_t * const dma = &xfer->dmabuf;
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+ u_int i = 0;
+
+#if 0
+ device_printf(sc->sc_dev, "%s %p slot %u dci %u\n", __func__, xfer,
+ xs->xs_idx, dci);
+#endif
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ KASSERT((xfer->rqflags & URQ_REQUEST) == 0);
+
+ parameter = DMAADDR(dma, 0);
+ KASSERT(len <= 0x10000);
+ status = XHCI_TRB_2_IRQ_SET(0) |
+ XHCI_TRB_2_TDSZ_SET(1) |
+ XHCI_TRB_2_BYTES_SET(len);
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL) |
+ XHCI_TRB_3_ISP_BIT | XHCI_TRB_3_IOC_BIT;
+ xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+
+ mutex_enter(&tr->xr_lock);
+ xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
+ mutex_exit(&tr->xr_lock);
+
+ xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
+
+ if (sc->sc_bus.use_polling) {
+ device_printf(sc->sc_dev, "%s polling\n", __func__);
+ //xhci_waitintr(sc, xfer);
+ }
+
+ return USBD_IN_PROGRESS;
+}
+
+static void
+xhci_device_intr_done(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+ const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+ const u_int endpt = xfer->pipe->endpoint->edesc->bEndpointAddress;
+ const bool isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ DPRINTF(("%s\n", __func__));
+
+ device_printf(sc->sc_dev, "%s %p slot %u dci %u\n", __func__, xfer,
+ xs->xs_idx, dci);
+
+ KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock));
+
+ usb_syncmem(&xfer->dmabuf, 0, xfer->length,
+ isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+
+#if 0
+ device_printf(sc->sc_dev, "");
+ for (size_t i = 0; i < xfer->length; i++) {
+ printf(" %02x", ((uint8_t const *)xfer->buffer)[i]);
+ }
+ printf("\n");
+#endif
+
+ if (xfer->pipe->repeat) {
+ xfer->status = xhci_device_intr_start(xfer);
+ } else {
+ callout_stop(&xfer->timeout_handle); /* XXX */
+ }
+
+}
+
+static void
+xhci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+ DPRINTF(("%s\n", __func__));
+ device_printf(sc->sc_dev, "%s %p\n", __func__, xfer);
+ /* XXX */
+ if (xfer->pipe->intrxfer == xfer) {
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ mutex_enter(&sc->sc_lock);
+ usb_transfer_complete(xfer);
+ mutex_exit(&sc->sc_lock);
+}
+
+static void
+xhci_device_intr_close(usbd_pipe_handle pipe)
+{
+ struct xhci_softc * const sc = pipe->device->bus->hci_private;
+ DPRINTF(("%s\n", __func__));
+ device_printf(sc->sc_dev, "%s %p\n", __func__, pipe);
+ xhci_unconfigure_endpoint(pipe);
+}
+
+/* ------------ */
+
+static void
+xhci_timeout(void *addr)
+{
+ struct xhci_xfer * const xx = addr;
+ usbd_xfer_handle const xfer = &xx->xx_xfer;
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+
+ if (sc->sc_dying) {
+ return;
+ }
+
+ usb_init_task(&xx->xx_abort_task, xhci_timeout_task, addr,
+ USB_TASKQ_MPSAFE);
+ usb_add_task(xx->xx_xfer.pipe->device, &xx->xx_abort_task,
+ USB_TASKQ_HC);
+}
+
+static void
+xhci_timeout_task(void *addr)
+{
+ usbd_xfer_handle const xfer = addr;
+ struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+
+ mutex_enter(&sc->sc_lock);
+#if 0
+ xhci_abort_xfer(xfer, USBD_TIMEOUT);
+#else
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+#endif
+ mutex_exit(&sc->sc_lock);
+}
diff --git a/sys/dev/usb/xhcireg.h b/sys/dev/usb/xhcireg.h
new file mode 100644
index 00000000000..480b93fb9d8
--- /dev/null
+++ b/sys/dev/usb/xhcireg.h
@@ -0,0 +1,426 @@
+/* $NetBSD: xhcireg.h,v 1.1 2013/09/14 00:40:31 jakllsch Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XHCIREG_H_
+#define _XHCIREG_H_
+
+/* XHCI PCI config registers */
+#define PCI_CBMEM 0x10 /* configuration base MEM */
+#define PCI_INTERFACE_XHCI 0x30
+
+#define PCI_USBREV 0x60 /* RO USB protocol revision */
+#define PCI_USBREV_MASK 0xFF
+#define PCI_USBREV_3_0 0x30 /* USB 3.0 */
+#define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */
+
+/* XHCI capability registers */
+#define XHCI_CAPLENGTH 0x00 /* RO capability */
+#define XHCI_CAP_CAPLENGTH(x) ((x) & 0xFF)
+#define XHCI_CAP_HCIVERSION(x) (((x) >> 16) & 0xFFFF) /* RO Interface version number */
+#define XHCI_HCIVERSION_0_9 0x0090 /* xHCI version 0.9 */
+#define XHCI_HCIVERSION_1_0 0x0100 /* xHCI version 1.0 */
+#define XHCI_HCSPARAMS1 0x04 /* RO structual parameters 1 */
+#define XHCI_HCS1_MAXSLOTS(x) ((x) & 0xFF)
+#define XHCI_HCS1_MAXINTRS(x) (((x) >> 8) & 0x7FF)
+#define XHCI_HCS1_MAXPORTS(x) (((x) >> 24) & 0xFF)
+#define XHCI_HCSPARAMS2 0x08 /* RO structual parameters 2 */
+#define XHCI_HCS2_IST(x) ((x) & 0xF)
+#define XHCI_HCS2_ERST_MAX(x) (((x) >> 4) & 0xF)
+#define XHCI_HCS2_SPR(x) (((x) >> 24) & 0x1)
+#define XHCI_HCS2_MAXSPBUF(x) (((x) >> 27) & 0x7F)
+#define XHCI_HCSPARAMS3 0x0C /* RO structual parameters 3 */
+#define XHCI_HCS3_U1_DEL(x) ((x) & 0xFF)
+#define XHCI_HCS3_U2_DEL(x) (((x) >> 16) & 0xFFFF)
+#define XHCI_HCCPARAMS 0x10 /* RO capability parameters */
+#define XHCI_HCC_AC64(x) ((x) & 0x1) /* 64-bit capable */
+#define XHCI_HCC_BNC(x) (((x) >> 1) & 0x1) /* BW negotiation */
+#define XHCI_HCC_CSZ(x) (((x) >> 2) & 0x1) /* context size */
+#define XHCI_HCC_PPC(x) (((x) >> 3) & 0x1) /* port power control */
+#define XHCI_HCC_PIND(x) (((x) >> 4) & 0x1) /* port indicators */
+#define XHCI_HCC_LHRC(x) (((x) >> 5) & 0x1) /* light HC reset */
+#define XHCI_HCC_LTC(x) (((x) >> 6) & 0x1) /* latency tolerance msg */
+#define XHCI_HCC_NSS(x) (((x) >> 7) & 0x1) /* no secondary sid */
+#define XHCI_HCC_MAXPSASIZE(x) (((x) >> 12) & 0xF) /* max pri. stream array size */
+#define XHCI_HCC_XECP(x) (((x) >> 16) & 0xFFFF) /* extended capabilities pointer */
+#define XHCI_DBOFF 0x14 /* RO doorbell offset */
+#define XHCI_RTSOFF 0x18 /* RO runtime register space offset */
+
+/* XHCI operational registers. Offset given by XHCI_CAPLENGTH register */
+#define XHCI_USBCMD 0x00 /* XHCI command */
+#define XHCI_CMD_RS 0x00000001 /* RW Run/Stop */
+#define XHCI_CMD_HCRST 0x00000002 /* RW Host Controller Reset */
+#define XHCI_CMD_INTE 0x00000004 /* RW Interrupter Enable */
+#define XHCI_CMD_HSEE 0x00000008 /* RW Host System Error Enable */
+#define XHCI_CMD_LHCRST 0x00000080 /* RO/RW Light Host Controller Reset */
+#define XHCI_CMD_CSS 0x00000100 /* RW Controller Save State */
+#define XHCI_CMD_CRS 0x00000200 /* RW Controller Restore State */
+#define XHCI_CMD_EWE 0x00000400 /* RW Enable Wrap Event */
+#define XHCI_CMD_EU3S 0x00000800 /* RW Enable U3 MFINDEX Stop */
+#define XHCI_USBSTS 0x04 /* XHCI status */
+#define XHCI_STS_HCH 0x00000001 /* RO - Host Controller Halted */
+#define XHCI_STS_HSE 0x00000004 /* RW - Host System Error */
+#define XHCI_STS_EINT 0x00000008 /* RW - Event Interrupt */
+#define XHCI_STS_PCD 0x00000010 /* RW - Port Change Detect */
+#define XHCI_STS_SSS 0x00000100 /* RO - Save State Status */
+#define XHCI_STS_RSS 0x00000200 /* RO - Restore State Status */
+#define XHCI_STS_SRE 0x00000400 /* RW - Save/Restore Error */
+#define XHCI_STS_CNR 0x00000800 /* RO - Controller Not Ready */
+#define XHCI_STS_HCE 0x00001000 /* RO - Host Controller Error */
+#define XHCI_PAGESIZE 0x08 /* XHCI page size mask */
+#define XHCI_PAGESIZE_4K 0x00000001 /* 4K Page Size */
+#define XHCI_PAGESIZE_8K 0x00000002 /* 8K Page Size */
+#define XHCI_PAGESIZE_16K 0x00000004 /* 16K Page Size */
+#define XHCI_PAGESIZE_32K 0x00000008 /* 32K Page Size */
+#define XHCI_PAGESIZE_64K 0x00000010 /* 64K Page Size */
+#define XHCI_DNCTRL 0x14 /* XHCI device notification control */
+#define XHCI_DNCTRL_MASK(n) (1U << (n))
+#define XHCI_CRCR 0x18 /* XHCI command ring control */
+#define XHCI_CRCR_LO_RCS 0x00000001 /* RW - consumer cycle state */
+#define XHCI_CRCR_LO_CS 0x00000002 /* RW - command stop */
+#define XHCI_CRCR_LO_CA 0x00000004 /* RW - command abort */
+#define XHCI_CRCR_LO_CRR 0x00000008 /* RW - command ring running */
+#define XHCI_CRCR_LO_MASK 0x0000000F
+#define XHCI_CRCR_HI 0x1C /* XHCI command ring control */
+#define XHCI_DCBAAP 0x30 /* XHCI dev context BA pointer */
+#define XHCI_DCBAAP_HI 0x34 /* XHCI dev context BA pointer */
+#define XHCI_CONFIG 0x38
+#define XHCI_CONFIG_SLOTS_MASK 0x000000FF /* RW - number of device slots enabled */
+
+/* XHCI port status registers */
+#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) /* XHCI port status */
+#define XHCI_PS_CCS 0x00000001 /* RO - current connect status */
+#define XHCI_PS_PED 0x00000002 /* RW - port enabled / disabled */
+#define XHCI_PS_OCA 0x00000008 /* RO - over current active */
+#define XHCI_PS_PR 0x00000010 /* RW - port reset */
+#define XHCI_PS_PLS_GET(x) (((x) >> 5) & 0xF) /* RW - port link state */
+#define XHCI_PS_PLS_SET(x) (((x) & 0xF) << 5) /* RW - port link state */
+#define XHCI_PS_PP 0x00000200 /* RW - port power */
+#define XHCI_PS_SPEED_GET(x) (((x) >> 10) & 0xF) /* RO - port speed */
+#define XHCI_PS_PIC_GET(x) (((x) >> 14) & 0x3) /* RW - port indicator */
+#define XHCI_PS_PIC_SET(x) (((x) & 0x3) << 14) /* RW - port indicator */
+#define XHCI_PS_LWS 0x00010000 /* RW - port link state write strobe */
+#define XHCI_PS_CSC 0x00020000 /* RW - connect status change */
+#define XHCI_PS_PEC 0x00040000 /* RW - port enable/disable change */
+#define XHCI_PS_WRC 0x00080000 /* RW - warm port reset change */
+#define XHCI_PS_OCC 0x00100000 /* RW - over-current change */
+#define XHCI_PS_PRC 0x00200000 /* RW - port reset change */
+#define XHCI_PS_PLC 0x00400000 /* RW - port link state change */
+#define XHCI_PS_CEC 0x00800000 /* RW - config error change */
+#define XHCI_PS_CAS 0x01000000 /* RO - cold attach status */
+#define XHCI_PS_WCE 0x02000000 /* RW - wake on connect enable */
+#define XHCI_PS_WDE 0x04000000 /* RW - wake on disconnect enable */
+#define XHCI_PS_WOE 0x08000000 /* RW - wake on over-current enable */
+#define XHCI_PS_DR 0x40000000 /* RO - device removable */
+#define XHCI_PS_WPR 0x80000000U /* RW - warm port reset */
+#define XHCI_PS_CLEAR 0x80FF01FFU /* command bits */
+
+#define XHCI_PORTPMSC(n) (0x3F4 + (0x10 * (n))) /* XHCI status and control */
+#define XHCI_PM3_U1TO_GET(x) (((x) >> 0) & 0xFF) /* RW - U1 timeout */
+#define XHCI_PM3_U1TO_SET(x) (((x) & 0xFF) << 0) /* RW - U1 timeout */
+#define XHCI_PM3_U2TO_GET(x) (((x) >> 8) & 0xFF) /* RW - U2 timeout */
+#define XHCI_PM3_U2TO_SET(x) (((x) & 0xFF) << 8) /* RW - U2 timeout */
+#define XHCI_PM3_FLA 0x00010000 /* RW - Force Link PM Accept */
+#define XHCI_PM2_L1S_GET(x) (((x) >> 0) & 0x7) /* RO - L1 status */
+#define XHCI_PM2_RWE 0x00000008 /* RW - remote wakup enable */
+#define XHCI_PM2_HIRD_GET(x) (((x) >> 4) & 0xF) /* RW - host initiated resume duration */
+#define XHCI_PM2_HIRD_SET(x) (((x) & 0xF) << 4) /* RW - host initiated resume duration */
+#define XHCI_PM2_L1SLOT_GET(x) (((x) >> 8) & 0xFF) /* RW - L1 device slot */
+#define XHCI_PM2_L1SLOT_SET(x) (((x) & 0xFF) << 8) /* RW - L1 device slot */
+#define XHCI_PM2_HLE 0x00010000 /* RW - hardware LPM enable */
+#define XHCI_PORTLI(n) (0x3F8 + (0x10 * (n))) /* XHCI port link info */
+#define XHCI_PLI3_ERR_GET(x) (((x) >> 0) & 0xFFFF) /* RO - port link errors */
+#define XHCI_PORTRSV(n) (0x3FC + (0x10 * (n))) /* XHCI port reserved */
+
+/* XHCI runtime registers. Offset given by XHCI_CAPLENGTH + XHCI_RTSOFF registers */
+#define XHCI_MFINDEX 0x0000 /* RO - microframe index */
+#define XHCI_MFINDEX_GET(x) ((x) & 0x3FFF)
+#define XHCI_IMAN(n) (0x0020 + (0x20 * (n))) /* XHCI interrupt management */
+#define XHCI_IMAN_INTR_PEND 0x00000001 /* RW - interrupt pending */
+#define XHCI_IMAN_INTR_ENA 0x00000002 /* RW - interrupt enable */
+#define XHCI_IMOD(n) (0x0024 + (0x20 * (n))) /* XHCI interrupt moderation */
+#define XHCI_IMOD_IVAL_GET(x) (((x) >> 0) & 0xFFFF) /* 250ns unit */
+#define XHCI_IMOD_IVAL_SET(x) (((x) & 0xFFFF) << 0) /* 250ns unit */
+#define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */
+#define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */
+#define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQ/second */
+#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */
+#define XHCI_ERSTS_GET(x) ((x) & 0xFFFF)
+#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
+#define XHCI_ERSTBA(n) (0x0030 + (0x20 * (n))) /* XHCI event ring segment table BA */
+#define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n))) /* XHCI event ring segment table BA */
+#define XHCI_ERDP(n) (0x0038 + (0x20 * (n))) /* XHCI event ring dequeue pointer */
+#define XHCI_ERDP_LO_SINDEX(x) ((x) & 0x7) /* RO - dequeue segment index */
+#define XHCI_ERDP_LO_BUSY 0x00000008 /* RW - event handler busy */
+#define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n))) /* XHCI event ring dequeue pointer */
+
+/* XHCI doorbell registers. Offset given by XHCI_CAPLENGTH + XHCI_DBOFF registers */
+#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
+#define XHCI_DB_TARGET_GET(x) ((x) & 0xFF) /* RW - doorbell target */
+#define XHCI_DB_TARGET_SET(x) ((x) & 0xFF) /* RW - doorbell target */
+#define XHCI_DB_SID_GET(x) (((x) >> 16) & 0xFFFF) /* RW - doorbell stream ID */
+#define XHCI_DB_SID_SET(x) (((x) & 0xFFFF) << 16) /* RW - doorbell stream ID */
+
+/* XHCI legacy support */
+#define XHCI_XECP_ID(x) ((x) & 0xFF)
+#define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF)
+#if 0
+#define XHCI_XECP_BIOS_SEM 0x0002
+#define XHCI_XECP_OS_SEM 0x0003
+#endif
+
+/* XHCI capability ID's */
+#define XHCI_ID_USB_LEGACY 0x0001
+#define XHCI_ID_PROTOCOLS 0x0002
+#define XHCI_ID_POWER_MGMT 0x0003
+#define XHCI_ID_VIRTUALIZATION 0x0004
+#define XHCI_ID_MSG_IRQ 0x0005
+#define XHCI_ID_USB_LOCAL_MEM 0x0006
+
+#define XHCI_PAGE_SIZE(sc) ((sc)->sc_pgsz)
+
+/* Chapter 6, Table 49 */
+#define XHCI_DEVICE_CONTEXT_BASE_ADDRESS_ARRAY_ALIGN 64
+#define XHCI_DEVICE_CONTEXT_ALIGN 64
+#define XHCI_INPUT_CONTROL_CONTEXT_ALIGN 64
+#define XHCI_SLOT_CONTEXT_ALIGN 32
+#define XHCI_ENDPOINT_CONTEXT_ALIGN 32
+#define XHCI_STREAM_CONTEXT_ALIGN 16
+#define XHCI_STREAM_ARRAY_ALIGN 16
+#define XHCI_TRANSFER_RING_SEGMENTS_ALIGN 16
+#define XHCI_COMMAND_RING_SEGMENTS_ALIGN 16
+#define XHCI_EVENT_RING_SEGMENTS_ALIGN 64
+#define XHCI_EVENT_RING_SEGMENT_TABLE_ALIGN 64
+#define XHCI_SCRATCHPAD_BUFFER_ARRAY_ALIGN 64
+#define XHCI_SCRATCHPAD_BUFFERS_ALIGN XHCI_PAGE_SIZE
+
+#define XHCI_ERSTE_ALIGN 16
+#define XHCI_TRB_ALIGN 16
+
+struct xhci_trb {
+ uint64_t trb_0;
+ uint32_t trb_2;
+#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24)
+#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F)
+#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17)
+#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
+#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22)
+#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF)
+#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16)
+ uint32_t trb_3;
+#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
+#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10)
+#define XHCI_TRB_3_CYCLE_BIT (1U << 0)
+#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */
+#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */
+#define XHCI_TRB_3_ISP_BIT (1U << 2)
+#define XHCI_TRB_3_NSNOOP_BIT (1U << 3)
+#define XHCI_TRB_3_CHAIN_BIT (1U << 4)
+#define XHCI_TRB_3_IOC_BIT (1U << 5)
+#define XHCI_TRB_3_IDT_BIT (1U << 6)
+#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3)
+#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7)
+#define XHCI_TRB_3_BEI_BIT (1U << 9)
+#define XHCI_TRB_3_DCEP_BIT (1U << 9)
+#define XHCI_TRB_3_PRSV_BIT (1U << 9)
+#define XHCI_TRB_3_BSR_BIT (1U << 9)
+#define XHCI_TRB_3_TRT_MASK (3U << 16)
+#define XHCI_TRB_3_TRT_NONE (0U << 16)
+#define XHCI_TRB_3_TRT_OUT (2U << 16)
+#define XHCI_TRB_3_TRT_IN (3U << 16)
+#define XHCI_TRB_3_DIR_IN (1U << 16)
+#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF)
+#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16)
+#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F)
+#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16)
+#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF)
+#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
+#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
+#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
+#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
+
+ /* Commands */
+#define XHCI_TRB_TYPE_RESERVED 0x00
+#define XHCI_TRB_TYPE_NORMAL 0x01
+#define XHCI_TRB_TYPE_SETUP_STAGE 0x02
+#define XHCI_TRB_TYPE_DATA_STAGE 0x03
+#define XHCI_TRB_TYPE_STATUS_STAGE 0x04
+#define XHCI_TRB_TYPE_ISOCH 0x05
+#define XHCI_TRB_TYPE_LINK 0x06
+#define XHCI_TRB_TYPE_EVENT_DATA 0x07
+#define XHCI_TRB_TYPE_NOOP 0x08
+#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09
+#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A
+#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B
+#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C
+#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D
+#define XHCI_TRB_TYPE_RESET_EP 0x0E
+#define XHCI_TRB_TYPE_STOP_EP 0x0F
+#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10
+#define XHCI_TRB_TYPE_RESET_DEVICE 0x11
+#define XHCI_TRB_TYPE_FORCE_EVENT 0x12
+#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13
+#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14
+#define XHCI_TRB_TYPE_GET_PORT_BW 0x15
+#define XHCI_TRB_TYPE_FORCE_HEADER 0x16
+#define XHCI_TRB_TYPE_NOOP_CMD 0x17
+
+ /* Events */
+#define XHCI_TRB_EVENT_TRANSFER 0x20
+#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21
+#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22
+#define XHCI_TRB_EVENT_BW_REQUEST 0x23
+#define XHCI_TRB_EVENT_DOORBELL 0x24
+#define XHCI_TRB_EVENT_HOST_CTRL 0x25
+#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26
+#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27
+
+ /* Error codes */
+#define XHCI_TRB_ERROR_INVALID 0x00
+#define XHCI_TRB_ERROR_SUCCESS 0x01
+#define XHCI_TRB_ERROR_DATA_BUF 0x02
+#define XHCI_TRB_ERROR_BABBLE 0x03
+#define XHCI_TRB_ERROR_XACT 0x04
+#define XHCI_TRB_ERROR_TRB 0x05
+#define XHCI_TRB_ERROR_STALL 0x06
+#define XHCI_TRB_ERROR_RESOURCE 0x07
+#define XHCI_TRB_ERROR_BANDWIDTH 0x08
+#define XHCI_TRB_ERROR_NO_SLOTS 0x09
+#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A
+#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B
+#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C
+#define XHCI_TRB_ERROR_SHORT_PKT 0x0D
+#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E
+#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F
+#define XHCI_TRB_ERROR_VF_RING_FULL 0x10
+#define XHCI_TRB_ERROR_PARAMETER 0x11
+#define XHCI_TRB_ERROR_BW_OVERRUN 0x12
+#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13
+#define XHCI_TRB_ERROR_NO_PING_RESP 0x14
+#define XHCI_TRB_ERROR_EV_RING_FULL 0x15
+#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16
+#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17
+#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18
+#define XHCI_TRB_ERROR_CMD_ABORTED 0x19
+#define XHCI_TRB_ERROR_STOPPED 0x1A
+#define XHCI_TRB_ERROR_LENGTH 0x1B
+#define XHCI_TRB_ERROR_BAD_MELAT 0x1D
+#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F
+#define XHCI_TRB_ERROR_EVENT_LOST 0x20
+#define XHCI_TRB_ERROR_UNDEFINED 0x21
+#define XHCI_TRB_ERROR_INVALID_SID 0x22
+#define XHCI_TRB_ERROR_SEC_BW 0x23
+#define XHCI_TRB_ERROR_SPLIT_XACT 0x24
+} __packed __aligned(XHCI_TRB_ALIGN);
+#define XHCI_TRB_SIZE sizeof(struct xhci_trb)
+
+#define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF)
+#define XHCI_SCTX_0_ROUTE_GET(x) ((x) & 0xFFFFF)
+#define XHCI_SCTX_0_SPEED_SET(x) (((x) & 0xF) << 20)
+#define XHCI_SCTX_0_SPEED_GET(x) (((x) >> 20) & 0xF)
+#define XHCI_SCTX_0_MTT_SET(x) (((x) & 0x1) << 25)
+#define XHCI_SCTX_0_MTT_GET(x) (((x) >> 25) & 0x1)
+#define XHCI_SCTX_0_HUB_SET(x) (((x) & 0x1) << 26)
+#define XHCI_SCTX_0_HUB_GET(x) (((x) >> 26) & 0x1)
+#define XHCI_SCTX_0_CTX_NUM_SET(x) (((x) & 0x1F) << 27)
+#define XHCI_SCTX_0_CTX_NUM_GET(x) (((x) >> 27) & 0x1F)
+
+#define XHCI_SCTX_1_MAX_EL_SET(x) ((x) & 0xFFFF)
+#define XHCI_SCTX_1_MAX_EL_GET(x) ((x) & 0xFFFF)
+#define XHCI_SCTX_1_RH_PORT_SET(x) (((x) & 0xFF) << 16)
+#define XHCI_SCTX_1_RH_PORT_GET(x) (((x) >> 16) & 0xFF)
+#define XHCI_SCTX_1_NUM_PORTS_SET(x) (((x) & 0xFF) << 24)
+#define XHCI_SCTX_1_NUM_PORTS_GET(x) (((x) >> 24) & 0xFF)
+
+#define XHCI_SCTX_2_TT_HUB_SID_SET(x) ((x) & 0xFF)
+#define XHCI_SCTX_2_TT_HUB_SID_GET(x) ((x) & 0xFF)
+#define XHCI_SCTX_2_TT_PORT_NUM_SET(x) (((x) & 0xFF) << 8)
+#define XHCI_SCTX_2_TT_PORT_NUM_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_SCTX_2_TT_THINK_TIME_SET(x) (((x) & 0x3) << 16)
+#define XHCI_SCTX_2_TT_THINK_TIME_GET(x) (((x) >> 16) & 0x3)
+#define XHCI_SCTX_2_IRQ_TARGET_SET(x) (((x) & 0x3FF) << 22)
+#define XHCI_SCTX_2_IRQ_TARGET_GET(x) (((x) >> 22) & 0x3FF)
+
+#define XHCI_SCTX_3_DEV_ADDR_SET(x) ((x) & 0xFF)
+#define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF)
+#define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27)
+#define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F)
+
+
+#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
+#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
+#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
+#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F)
+#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15)
+#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1)
+#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16)
+#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF)
+
+#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1)
+#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3)
+#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3)
+#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7)
+#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7)
+#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1)
+#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8)
+#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF)
+
+#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
+
+#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF)
+
+
+#define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU
+
+#define XHCI_INCTX_0_DROP_MASK(n) (1U << (n))
+
+#define XHCI_INCTX_1_ADD_MASK(n) (1U << (n))
+
+
+struct xhci_erste {
+ uint64_t erste_0; /* 63:6 base */
+ uint32_t erste_2; /* 15:0 trb count (16 to 4096) */
+ uint32_t erste_3; /* RsvdZ */
+} __packed __aligned(XHCI_ERSTE_ALIGN);
+#define XHCI_ERSTE_SIZE sizeof(struct xhci_erste)
+
+#endif /* _XHCIREG_H_ */
diff --git a/sys/dev/usb/xhcivar.h b/sys/dev/usb/xhcivar.h
new file mode 100644
index 00000000000..43b47859ac3
--- /dev/null
+++ b/sys/dev/usb/xhcivar.h
@@ -0,0 +1,127 @@
+/* $NetBSD: xhcivar.h,v 1.1 2013/09/14 00:40:31 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2013 Jonathan A. Kollasch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_USB_XHCIVAR_H_
+#define _DEV_USB_XHCIVAR_H_
+
+#include <sys/pool.h>
+
+struct xhci_xfer {
+ struct usbd_xfer xx_xfer;
+ struct usb_task xx_abort_task;
+ struct xhci_trb xx_trb[20];
+};
+
+struct xhci_ring {
+ usb_dma_t xr_dma;
+ kmutex_t xr_lock;
+ struct xhci_trb * xr_trb;
+ void **xr_cookies;
+ u_int xr_ntrb; /* number of elements for above */
+ u_int xr_ep; /* enqueue pointer */
+ u_int xr_cs; /* cycle state */
+ bool is_halted;
+};
+
+struct xhci_endpoint {
+ struct xhci_ring xe_tr; /* transfer ring */
+};
+
+struct xhci_slot {
+ usb_dma_t xs_dc_dma; /* device context page */
+ usb_dma_t xs_ic_dma; /* input context page */
+ struct xhci_endpoint xs_ep[32]; /* endpoints */
+ u_int xs_idx; /* slot index */
+};
+
+struct xhci_softc {
+ device_t sc_dev;
+ device_t sc_child;
+ void *sc_ih;
+ bus_size_t sc_ios;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh; /* Base */
+ bus_space_handle_t sc_cbh; /* Capability Base */
+ bus_space_handle_t sc_obh; /* Operational Base */
+ bus_space_handle_t sc_rbh; /* Runtime Base */
+ bus_space_handle_t sc_dbh; /* Doorbell Registers */
+ struct usbd_bus sc_bus;
+
+ kmutex_t sc_lock;
+ kmutex_t sc_intr_lock;
+ kcondvar_t sc_softwake_cv;
+
+ usbd_xfer_handle sc_intrxfer;
+
+ pool_cache_t sc_xferpool;
+
+ bus_size_t sc_pgsz; /* xHCI page size */
+ uint32_t sc_ctxsz;
+ int sc_maxslots;
+ int sc_maxintrs;
+ int sc_maxports;
+
+ /* XXX suboptimal */
+ int sc_hs_port_start;
+ int sc_hs_port_count;
+ int sc_ss_port_start;
+ int sc_ss_port_count;
+
+ struct xhci_slot * sc_slots;
+
+ struct xhci_ring sc_cr; /* command ring */
+ struct xhci_ring sc_er; /* event ring */
+
+ usb_dma_t sc_eventst_dma;
+ usb_dma_t sc_dcbaa_dma;
+
+ //struct usb_dma_reserve sc_dma_reserve;
+
+ kcondvar_t sc_command_cv;
+ bus_addr_t sc_command_addr;
+ struct xhci_trb sc_result_trb;
+
+ bool sc_ac64;
+ bool sc_dying;
+
+ uint8_t sc_addr;
+ uint8_t sc_conf;
+};
+
+usbd_status xhci_init(struct xhci_softc *);
+int xhci_intr(void *);
+int xhci_detach(struct xhci_softc *, int);
+int xhci_activate(device_t, enum devact);
+void xhci_childdet(device_t, device_t);
+bool xhci_suspend(device_t, const pmf_qual_t *);
+bool xhci_resume(device_t, const pmf_qual_t *);
+bool xhci_shutdown(device_t, int);
+
+#define XHCI_TRANSFER_RING_TRBS 256
+
+#endif /* _DEV_USB_XHCIVAR_H_ */