diff options
| author | jakllsch <jakllsch@NetBSD.org> | 2013-09-14 00:40:31 +0000 |
|---|---|---|
| committer | jakllsch <jakllsch@NetBSD.org> | 2013-09-14 00:40:31 +0000 |
| commit | 077c1e5a554ba66d4c6e4a09f337d2a8ded1bcee (patch) | |
| tree | ebd3ae33933b6e78a5701b381723558f8930f016 /sys/dev/usb | |
| parent | 70f74c9d46f323f05ffd392f89065bf28f6409cc (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/usb')
| -rw-r--r-- | sys/dev/usb/usb_subr.c | 17 | ||||
| -rw-r--r-- | sys/dev/usb/usbdivar.h | 13 | ||||
| -rw-r--r-- | sys/dev/usb/xhci.c | 2841 | ||||
| -rw-r--r-- | sys/dev/usb/xhcireg.h | 426 | ||||
| -rw-r--r-- | sys/dev/usb/xhcivar.h | 127 |
5 files changed, 3415 insertions, 9 deletions
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(¶meter, 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_ */ |
