svn commit: r215944 - in stable/8/sys: conf dev/usb
dev/usb/controller dev/usb/storage modules/usb modules/usb/xhci
Andrew Thompson
thompsa at FreeBSD.org
Sat Nov 27 19:35:12 UTC 2010
Author: thompsa
Date: Sat Nov 27 19:35:12 2010
New Revision: 215944
URL: http://svn.freebsd.org/changeset/base/215944
Log:
MFC r213379, r213426, r213426, r213427, r213432, r213435, r213437, r213439,
r214804
Merge the XHCI usb controller and supporting code.
Added:
stable/8/sys/dev/usb/controller/xhci.c
- copied unchanged from r213379, head/sys/dev/usb/controller/xhci.c
stable/8/sys/dev/usb/controller/xhci.h
- copied unchanged from r213379, head/sys/dev/usb/controller/xhci.h
stable/8/sys/dev/usb/controller/xhci_pci.c
- copied unchanged from r213379, head/sys/dev/usb/controller/xhci_pci.c
stable/8/sys/dev/usb/controller/xhcireg.h
- copied unchanged from r213379, head/sys/dev/usb/controller/xhcireg.h
stable/8/sys/modules/usb/xhci/
- copied from r213437, head/sys/modules/usb/xhci/
Modified:
stable/8/sys/conf/files
stable/8/sys/dev/usb/controller/usb_controller.c
stable/8/sys/dev/usb/storage/umass.c
stable/8/sys/dev/usb/usb.h
stable/8/sys/dev/usb/usb_controller.h
stable/8/sys/dev/usb/usb_dev.c
stable/8/sys/dev/usb/usb_device.c
stable/8/sys/dev/usb/usb_device.h
stable/8/sys/dev/usb/usb_generic.c
stable/8/sys/dev/usb/usb_hub.c
stable/8/sys/dev/usb/usb_hub.h
stable/8/sys/dev/usb/usb_parse.c
stable/8/sys/dev/usb/usb_request.c
stable/8/sys/dev/usb/usb_request.h
stable/8/sys/dev/usb/usb_transfer.c
stable/8/sys/dev/usb/usb_transfer.h
stable/8/sys/dev/usb/usbdi.h
stable/8/sys/dev/usb/usbdi_util.h
stable/8/sys/modules/usb/Makefile
Directory Properties:
stable/8/sys/ (props changed)
stable/8/sys/amd64/include/xen/ (props changed)
stable/8/sys/cddl/contrib/opensolaris/ (props changed)
stable/8/sys/contrib/dev/acpica/ (props changed)
stable/8/sys/contrib/pf/ (props changed)
stable/8/sys/dev/usb/controller/ (props changed)
stable/8/sys/mips/alchemy/ (props changed)
stable/8/sys/mips/atheros/ (props changed)
stable/8/sys/mips/cavium/ (props changed)
stable/8/sys/mips/cavium/dev/ (props changed)
stable/8/sys/mips/rmi/ (props changed)
stable/8/sys/mips/rmi/dev/ (props changed)
stable/8/sys/mips/sibyte/ (props changed)
Modified: stable/8/sys/conf/files
==============================================================================
--- stable/8/sys/conf/files Sat Nov 27 18:18:09 2010 (r215943)
+++ stable/8/sys/conf/files Sat Nov 27 19:35:12 2010 (r215944)
@@ -1699,6 +1699,8 @@ dev/usb/controller/ohci_atmelarm.c optio
dev/usb/controller/ohci_pci.c optional ohci pci
dev/usb/controller/uhci.c optional uhci
dev/usb/controller/uhci_pci.c optional uhci pci
+dev/usb/controller/xhci.c optional xhci
+dev/usb/controller/xhci_pci.c optional xhci pci
dev/usb/controller/uss820dci.c optional uss820dci
dev/usb/controller/uss820dci_atmelarm.c optional uss820dci at91rm9200
dev/usb/controller/usb_controller.c optional usb
Modified: stable/8/sys/dev/usb/controller/usb_controller.c
==============================================================================
--- stable/8/sys/dev/usb/controller/usb_controller.c Sat Nov 27 18:18:09 2010 (r215943)
+++ stable/8/sys/dev/usb/controller/usb_controller.c Sat Nov 27 19:35:12 2010 (r215944)
@@ -103,10 +103,15 @@ static driver_t usb_driver = {
.size = 0,
};
+/* Host Only Drivers */
DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0);
+
+/* Device Only Drivers */
DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usbus, musbotg, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0);
/*------------------------------------------------------------------------*
@@ -351,6 +356,11 @@ usb_bus_attach(struct usb_proc_msg *pm)
device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n");
break;
+ case USB_REV_3_0:
+ speed = USB_SPEED_SUPER;
+ device_printf(bus->bdev, "4.8Gbps Super Speed USB v3.0\n");
+ break;
+
default:
device_printf(bus->bdev, "Unsupported USB revision\n");
return;
Copied: stable/8/sys/dev/usb/controller/xhci.c (from r213379, head/sys/dev/usb/controller/xhci.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/8/sys/dev/usb/controller/xhci.c Sat Nov 27 19:35:12 2010 (r215944, copy of r213379, head/sys/dev/usb/controller/xhci.c)
@@ -0,0 +1,3862 @@
+/*-
+ * 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.
+ */
+
+/*
+ * USB eXtensible Host Controller Interface, a.k.a. USB 3.0 controller.
+ *
+ * The XHCI 1.0 spec can be found at
+ * http://www.intel.com/technology/usb/download/xHCI_Specification_for_USB.pdf
+ * and the USB 3.0 spec at
+ * http://www.usb.org/developers/docs/usb_30_spec_060910.zip
+ */
+
+/*
+ * A few words about the design implementation: This driver emulates
+ * the concept about TDs which is found in EHCI specification. This
+ * way we avoid too much diveration among USB drivers.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define USB_DEBUG_VAR xhcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/xhci.h>
+#include <dev/usb/controller/xhcireg.h>
+
+#define XHCI_BUS2SC(bus) \
+ ((struct xhci_softc *)(((uint8_t *)(bus)) - \
+ ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus))))
+
+#ifdef USB_DEBUG
+static int xhcidebug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI");
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW,
+ &xhcidebug, 0, "Debug level");
+
+TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug);
+
+#endif
+
+#define XHCI_INTR_ENDPT 1
+
+struct xhci_std_temp {
+ struct xhci_softc *sc;
+ struct usb_page_cache *pc;
+ struct xhci_td *td;
+ struct xhci_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint32_t max_packet_size;
+ uint32_t average;
+ uint16_t isoc_delta;
+ uint16_t isoc_frame;
+ uint8_t shortpkt;
+ uint8_t multishort;
+ uint8_t last_frame;
+ uint8_t trb_type;
+ uint8_t direction;
+ uint8_t tbc;
+ uint8_t tlbpc;
+ uint8_t step_td;
+};
+
+static void xhci_do_poll(struct usb_bus *);
+static void xhci_device_done(struct usb_xfer *, usb_error_t);
+static void xhci_root_intr(struct xhci_softc *);
+static void xhci_free_device_ext(struct usb_device *);
+static struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_device *,
+ struct usb_endpoint_descriptor *);
+static usb_proc_callback_t xhci_configure_msg;
+static usb_error_t xhci_configure_device(struct usb_device *);
+static usb_error_t xhci_configure_endpoint(struct usb_device *,
+ struct usb_endpoint_descriptor *, uint64_t, uint16_t,
+ uint8_t, uint8_t, uint8_t, uint16_t, uint16_t);
+static usb_error_t xhci_configure_mask(struct usb_device *,
+ uint32_t, uint8_t);
+static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *,
+ uint64_t, uint8_t);
+static void xhci_endpoint_doorbell(struct usb_xfer *);
+
+extern struct usb_bus_methods xhci_bus_methods;
+
+#ifdef USB_DEBUG
+static void
+xhci_dump_trb(struct xhci_trb *trb)
+{
+ DPRINTFN(5, "trb = %p\n", trb);
+ DPRINTFN(5, "qwTrb0 = 0x%016llx\n", (long long)le64toh(trb->qwTrb0));
+ DPRINTFN(5, "dwTrb2 = 0x%08x\n", le32toh(trb->dwTrb2));
+ DPRINTFN(5, "dwTrb3 = 0x%08x\n", le32toh(trb->dwTrb3));
+}
+
+static void
+xhci_dump_endpoint(struct xhci_endp_ctx *pep)
+{
+ DPRINTFN(5, "pep = %p\n", pep);
+ DPRINTFN(5, "dwEpCtx0=0x%08x\n", pep->dwEpCtx0);
+ DPRINTFN(5, "dwEpCtx1=0x%08x\n", pep->dwEpCtx1);
+ DPRINTFN(5, "qwEpCtx2=0x%016llx\n", (long long)pep->qwEpCtx2);
+ DPRINTFN(5, "dwEpCtx4=0x%08x\n", pep->dwEpCtx4);
+ DPRINTFN(5, "dwEpCtx5=0x%08x\n", pep->dwEpCtx5);
+ DPRINTFN(5, "dwEpCtx6=0x%08x\n", pep->dwEpCtx6);
+ DPRINTFN(5, "dwEpCtx7=0x%08x\n", pep->dwEpCtx7);
+}
+
+static void
+xhci_dump_device(struct xhci_slot_ctx *psl)
+{
+ DPRINTFN(5, "psl = %p\n", psl);
+ DPRINTFN(5, "dwSctx0=0x%08x\n", psl->dwSctx0);
+ DPRINTFN(5, "dwSctx1=0x%08x\n", psl->dwSctx1);
+ DPRINTFN(5, "dwSctx2=0x%08x\n", psl->dwSctx2);
+ DPRINTFN(5, "dwSctx3=0x%08x\n", psl->dwSctx3);
+}
+#endif
+
+static void
+xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
+{
+ struct xhci_softc *sc = XHCI_BUS2SC(bus);
+ uint8_t i;
+
+ cb(bus, &sc->sc_hw.root_pc, &sc->sc_hw.root_pg,
+ sizeof(struct xhci_hw_root), XHCI_PAGE_SIZE);
+
+ cb(bus, &sc->sc_hw.ctx_pc, &sc->sc_hw.ctx_pg,
+ sizeof(struct xhci_dev_ctx_addr), XHCI_PAGE_SIZE);
+
+ for (i = 0; i != XHCI_MAX_SCRATCHPADS; i++) {
+ cb(bus, &sc->sc_hw.scratch_pc[i], &sc->sc_hw.scratch_pg[i],
+ XHCI_PAGE_SIZE, XHCI_PAGE_SIZE);
+ }
+}
+
+usb_error_t
+xhci_start_controller(struct xhci_softc *sc)
+{
+ struct usb_page_search buf_res;
+ struct xhci_hw_root *phwr;
+ struct xhci_dev_ctx_addr *pdctxa;
+ uint64_t addr;
+ uint32_t temp;
+ uint16_t i;
+
+ DPRINTF("\n");
+
+ sc->sc_capa_off = 0;
+ sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
+ sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F;
+ sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3;
+
+ DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off);
+ DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off);
+ DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off);
+
+ sc->sc_event_ccs = 1;
+ sc->sc_event_idx = 0;
+ sc->sc_command_ccs = 1;
+ sc->sc_command_idx = 0;
+
+ DPRINTF("xHCI version = 0x%04x\n", XREAD2(sc, capa, XHCI_HCIVERSION));
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS0);
+
+ DPRINTF("HCS0 = 0x%08x\n", temp);
+
+ if (XHCI_HCS0_CSZ(temp)) {
+ device_printf(sc->sc_bus.parent, "Driver does not "
+ "support 64-byte contexts.");
+ return (USB_ERR_IOERROR);
+ }
+
+ /* Reset controller */
+ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_HCRST);
+
+ for (i = 0; i != 100; i++) {
+ usb_pause_mtx(NULL, hz / 1000);
+ temp = XREAD4(sc, oper, XHCI_USBCMD) &
+ (XHCI_CMD_HCRST | XHCI_STS_CNR);
+ if (!temp)
+ break;
+ }
+
+ if (temp) {
+ device_printf(sc->sc_bus.parent, "Controller "
+ "reset timeout.\n");
+ return (USB_ERR_IOERROR);
+ }
+
+ if (!(XREAD4(sc, oper, XHCI_PAGESIZE) & XHCI_PAGESIZE_4K)) {
+ device_printf(sc->sc_bus.parent, "Controller does "
+ "not support 4K page size.\n");
+ return (USB_ERR_IOERROR);
+ }
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS1);
+
+ i = XHCI_HCS1_N_PORTS(temp);
+
+ if (i == 0) {
+ device_printf(sc->sc_bus.parent, "Invalid number "
+ "of ports: %u\n", i);
+ return (USB_ERR_IOERROR);
+ }
+
+ sc->sc_noport = i;
+ sc->sc_noslot = XHCI_HCS1_DEVSLOT_MAX(temp);
+
+ if (sc->sc_noslot > XHCI_MAX_DEVICES)
+ sc->sc_noslot = XHCI_MAX_DEVICES;
+
+ /* setup number of device slots */
+
+ DPRINTF("CONFIG=0x%08x -> 0x%08x\n",
+ XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot);
+
+ XWRITE4(sc, oper, XHCI_CONFIG, sc->sc_noslot);
+
+ DPRINTF("Max slots: %u\n", sc->sc_noslot);
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS2);
+
+ sc->sc_noscratch = XHCI_HCS2_SPB_MAX(temp);
+
+ if (sc->sc_noscratch > XHCI_MAX_SCRATCHPADS) {
+ device_printf(sc->sc_bus.parent, "XHCI request "
+ "too many scratchpads\n");
+ return (USB_ERR_NOMEM);
+ }
+
+ DPRINTF("Max scratch: %u\n", sc->sc_noscratch);
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS3);
+
+ sc->sc_exit_lat_max = XHCI_HCS3_U1_DEL(temp) +
+ XHCI_HCS3_U2_DEL(temp) + 250 /* us */;
+
+ temp = XREAD4(sc, oper, XHCI_USBSTS);
+
+ /* clear interrupts */
+ XWRITE4(sc, oper, XHCI_USBSTS, temp);
+ /* disable all device notifications */
+ XWRITE4(sc, oper, XHCI_DNCTRL, 0);
+
+ /* setup device context base address */
+ usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res);
+ pdctxa = buf_res.buffer;
+ memset(pdctxa, 0, sizeof(*pdctxa));
+
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_dev_ctx_addr *)0)->qwSpBufPtr[0];
+
+ /* slot 0 points to the table of scratchpad pointers */
+ pdctxa->qwBaaDevCtxAddr[0] = htole64(addr);
+
+ for (i = 0; i != sc->sc_noscratch; i++) {
+ struct usb_page_search buf_scp;
+ usbd_get_page(&sc->sc_hw.scratch_pc[i], 0, &buf_scp);
+ pdctxa->qwSpBufPtr[i] = htole64((uint64_t)buf_scp.physaddr);
+ }
+
+ addr = buf_res.physaddr;
+
+ XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
+ XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
+ XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
+ XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
+
+ /* Setup event table size */
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS2);
+
+ DPRINTF("HCS2=0x%08x\n", temp);
+
+ temp = XHCI_HCS2_ERST_MAX(temp);
+ temp = 1U << temp;
+ if (temp > XHCI_MAX_RSEG)
+ temp = XHCI_MAX_RSEG;
+
+ sc->sc_erst_max = temp;
+
+ DPRINTF("ERSTSZ=0x%08x -> 0x%08x\n",
+ XREAD4(sc, runt, XHCI_ERSTSZ(0)), temp);
+
+ XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(temp));
+
+ /* Setup interrupt rate */
+ XWRITE4(sc, runt, XHCI_IMOD(0), XHCI_IMOD_DEFAULT);
+
+ usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
+
+ phwr = buf_res.buffer;
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[0];
+
+ /* reset hardware root structure */
+ memset(phwr, 0, sizeof(*phwr));
+
+ phwr->hwr_ring_seg[0].qwEvrsTablePtr = htole64(addr);
+ phwr->hwr_ring_seg[0].dwEvrsTableSize = htole32(XHCI_MAX_EVENTS);
+
+ DPRINTF("ERDP(0)=0x%016llx\n", (unsigned long long)addr);
+
+ XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
+ XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
+
+ addr = (uint64_t)buf_res.physaddr;
+
+ DPRINTF("ERSTBA(0)=0x%016llx\n", (unsigned long long)addr);
+
+ XWRITE4(sc, runt, XHCI_ERSTBA_LO(0), (uint32_t)addr);
+ XWRITE4(sc, runt, XHCI_ERSTBA_HI(0), (uint32_t)(addr >> 32));
+
+ /* Setup interrupter registers */
+
+ temp = XREAD4(sc, runt, XHCI_IMAN(0));
+ temp |= XHCI_IMAN_INTR_ENA;
+ XWRITE4(sc, runt, XHCI_IMAN(0), temp);
+
+ /* setup command ring control base address */
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0];
+
+ DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr);
+
+ XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS);
+ XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32));
+
+ phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr);
+
+ usb_bus_mem_flush_all(&sc->sc_bus, &xhci_iterate_hw_softc);
+
+ /* Go! */
+ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_RS |
+ XHCI_CMD_INTE | XHCI_CMD_HSEE);
+
+ for (i = 0; i != 100; i++) {
+ usb_pause_mtx(NULL, hz / 1000);
+ temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH;
+ if (!temp)
+ break;
+ }
+ if (temp) {
+ XWRITE4(sc, oper, XHCI_USBCMD, 0);
+ device_printf(sc->sc_bus.parent, "Run timeout.\n");
+ return (USB_ERR_IOERROR);
+ }
+
+ /* catch any lost interrupts */
+ xhci_do_poll(&sc->sc_bus);
+
+ return (0);
+}
+
+usb_error_t
+xhci_halt_controller(struct xhci_softc *sc)
+{
+ uint32_t temp;
+ uint16_t i;
+
+ DPRINTF("\n");
+
+ sc->sc_capa_off = 0;
+ sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
+ sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0xF;
+ sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3;
+
+ /* Halt controller */
+ XWRITE4(sc, oper, XHCI_USBCMD, 0);
+
+ for (i = 0; i != 100; i++) {
+ usb_pause_mtx(NULL, hz / 1000);
+ temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH;
+ if (temp)
+ break;
+ }
+
+ if (!temp) {
+ device_printf(sc->sc_bus.parent, "Controller halt timeout.\n");
+ return (USB_ERR_IOERROR);
+ }
+ return (0);
+}
+
+usb_error_t
+xhci_init(struct xhci_softc *sc, device_t self)
+{
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+
+ /* set the bus revision */
+ sc->sc_bus.usbrev = USB_REV_3_0;
+
+ /* set up the bus struct */
+ sc->sc_bus.methods = &xhci_bus_methods;
+
+ /* setup devices array */
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = XHCI_MAX_DEVICES;
+
+ /* setup command queue mutex and condition varible */
+ cv_init(&sc->sc_cmd_cv, "CMDQ");
+ sx_init(&sc->sc_cmd_sx, "CMDQ lock");
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &xhci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg;
+ sc->sc_config_msg[0].bus = &sc->sc_bus;
+ sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg;
+ sc->sc_config_msg[1].bus = &sc->sc_bus;
+
+ if (usb_proc_create(&sc->sc_config_proc,
+ &sc->sc_bus.bus_mtx, device_get_nameunit(self), USB_PRI_MED)) {
+ printf("WARNING: Creation of XHCI configure "
+ "callback process failed.\n");
+ }
+ return (0);
+}
+
+void
+xhci_uninit(struct xhci_softc *sc)
+{
+ usb_proc_free(&sc->sc_config_proc);
+
+ usb_bus_mem_free_all(&sc->sc_bus, &xhci_iterate_hw_softc);
+
+ cv_destroy(&sc->sc_cmd_cv);
+ sx_destroy(&sc->sc_cmd_sx);
+}
+
+void
+xhci_suspend(struct xhci_softc *sc)
+{
+ /* XXX TODO */
+}
+
+void
+xhci_resume(struct xhci_softc *sc)
+{
+ /* XXX TODO */
+}
+
+void
+xhci_shutdown(struct xhci_softc *sc)
+{
+ DPRINTF("Stopping the XHCI\n");
+
+ xhci_halt_controller(sc);
+}
+
+static usb_error_t
+xhci_generic_done_sub(struct usb_xfer *xfer)
+{
+ struct xhci_td *td;
+ struct xhci_td *td_alt_next;
+ uint32_t len;
+ uint8_t status;
+
+ td = xfer->td_transfer_cache;
+ td_alt_next = td->alt_next;
+
+ if (xfer->aframes != xfer->nframes)
+ usbd_xfer_set_frame_len(xfer, xfer->aframes, 0);
+
+ while (1) {
+
+ usb_pc_cpu_invalidate(td->page_cache);
+
+ status = td->status;
+ len = td->remainder;
+
+ DPRINTFN(4, "xfer=%p[%u/%u] rem=%u/%u status=%u\n",
+ xfer, (unsigned int)xfer->aframes,
+ (unsigned int)xfer->nframes,
+ (unsigned int)len, (unsigned int)td->len,
+ (unsigned int)status);
+
+ /*
+ * Verify the status length and
+ * add the length to "frlengths[]":
+ */
+ if (len > td->len) {
+ /* should not happen */
+ DPRINTF("Invalid status length, "
+ "0x%04x/0x%04x bytes\n", len, td->len);
+ status = XHCI_TRB_ERROR_LENGTH;
+ } else if (xfer->aframes != xfer->nframes) {
+ xfer->frlengths[xfer->aframes] += td->len - len;
+ }
+ /* Check for last transfer */
+ if (((void *)td) == xfer->td_transfer_last) {
+ td = NULL;
+ break;
+ }
+ /* Check for transfer error */
+ if (status != XHCI_TRB_ERROR_SHORT_PKT &&
+ status != XHCI_TRB_ERROR_SUCCESS) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr ||
+ xfer->flags_int.control_xfr) {
+ /* follow alt next */
+ td = td->alt_next;
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ break;
+ }
+ td = td->obj_next;
+
+ if (td->alt_next != td_alt_next) {
+ /* this USB frame is complete */
+ break;
+ }
+ }
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED :
+ (status != XHCI_TRB_ERROR_SHORT_PKT &&
+ status != XHCI_TRB_ERROR_SUCCESS) ? USB_ERR_IOERROR :
+ USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+xhci_generic_done(struct usb_xfer *xfer)
+{
+ usb_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
+ xfer, xfer->endpoint);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr)
+ err = xhci_generic_done_sub(xfer);
+
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL)
+ goto done;
+ }
+
+ while (xfer->aframes != xfer->nframes) {
+
+ err = xhci_generic_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL)
+ goto done;
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act)
+ err = xhci_generic_done_sub(xfer);
+done:
+ /* transfer is complete */
+ xhci_device_done(xfer, err);
+}
+
+static void
+xhci_activate_transfer(struct usb_xfer *xfer)
+{
+ struct xhci_td *td;
+
+ td = xfer->td_transfer_cache;
+
+ usb_pc_cpu_invalidate(td->page_cache);
+
+ if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) {
+
+ /* activate the transfer */
+
+ td->td_trb[0].dwTrb3 |= htole32(XHCI_TRB_3_CYCLE_BIT);
+ usb_pc_cpu_flush(td->page_cache);
+
+ xhci_endpoint_doorbell(xfer);
+ }
+}
+
+static void
+xhci_skip_transfer(struct usb_xfer *xfer)
+{
+ struct xhci_td *td;
+ struct xhci_td *td_last;
+
+ td = xfer->td_transfer_cache;
+ td_last = xfer->td_transfer_last;
+
+ td = td->alt_next;
+
+ usb_pc_cpu_invalidate(td->page_cache);
+
+ if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) {
+
+ usb_pc_cpu_invalidate(td_last->page_cache);
+
+ /* copy LINK TRB to current waiting location */
+
+ td->td_trb[0].qwTrb0 = td_last->td_trb[td_last->ntrb].qwTrb0;
+ td->td_trb[0].dwTrb2 = td_last->td_trb[td_last->ntrb].dwTrb2;
+ usb_pc_cpu_flush(td->page_cache);
+
+ td->td_trb[0].dwTrb3 = td_last->td_trb[td_last->ntrb].dwTrb3;
+ usb_pc_cpu_flush(td->page_cache);
+
+ xhci_endpoint_doorbell(xfer);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * xhci_check_transfer
+ *------------------------------------------------------------------------*/
+static void
+xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb)
+{
+ int64_t offset;
+ uint64_t td_event;
+ uint32_t temp;
+ uint32_t remainder;
+ uint8_t status;
+ uint8_t halted;
+ uint8_t epno;
+ uint8_t index;
+ uint8_t i;
+
+ /* decode TRB */
+ td_event = le64toh(trb->qwTrb0);
+ temp = le32toh(trb->dwTrb2);
+
+ remainder = XHCI_TRB_2_REM_GET(temp);
+ status = XHCI_TRB_2_ERROR_GET(temp);
+
+ temp = le32toh(trb->dwTrb3);
+ epno = XHCI_TRB_3_EP_GET(temp);
+ index = XHCI_TRB_3_SLOT_GET(temp);
+
+ /* check if error means halted */
+ halted = (status != XHCI_TRB_ERROR_SHORT_PKT &&
+ status != XHCI_TRB_ERROR_SUCCESS);
+
+ DPRINTF("slot=%u epno=%u remainder=%u status=%u\n",
+ index, epno, remainder, status);
+
+ if (index > sc->sc_noslot) {
+ DPRINTF("Invalid slot.\n");
+ return;
+ }
+
+ if ((epno == 0) || (epno >= XHCI_MAX_ENDPOINTS)) {
+ DPRINTF("Invalid endpoint.\n");
+ return;
+ }
+
+ /* try to find the USB transfer that generated the event */
+ for (i = 0; i != (XHCI_MAX_TRANSFERS - 1); i++) {
+ struct usb_xfer *xfer;
+ struct xhci_td *td;
+ struct xhci_endpoint_ext *pepext;
+
+ pepext = &sc->sc_hw.devs[index].endp[epno];
+
+ xfer = pepext->xfer[i];
+ if (xfer == NULL)
+ continue;
+
+ td = xfer->td_transfer_cache;
+
+ DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n",
+ (long long)td_event,
+ (long long)td->td_self,
+ (long long)td->td_self + sizeof(td->td_trb));
+
+ /*
+ * NOTE: Some XHCI implementations might not trigger
+ * an event on the last LINK TRB so we need to
+ * consider both the last and second last event
+ * address as conditions for a successful transfer.
+ *
+ * NOTE: We assume that the XHCI will only trigger one
+ * event per chain of TRBs.
+ */
+
+ offset = td_event - td->td_self;
+
+ if (offset >= 0 &&
+ offset < sizeof(td->td_trb)) {
+
+ usb_pc_cpu_invalidate(td->page_cache);
+
+ /* compute rest of remainder, if any */
+ for (i = (offset / 16) + 1; i < td->ntrb; i++) {
+ temp = le32toh(td->td_trb[i].dwTrb2);
+ remainder += XHCI_TRB_2_BYTES_GET(temp);
+ }
+
+ DPRINTFN(5, "New remainder: %u\n", remainder);
+
+ /* clear isochronous transfer errors */
+ if (xfer->flags_int.isochronous_xfr) {
+ if (halted) {
+ halted = 0;
+ status = XHCI_TRB_ERROR_SUCCESS;
+ remainder = td->len;
+ }
+ }
+
+ /* "td->remainder" is verified later */
+ td->remainder = remainder;
+ td->status = status;
+
+ usb_pc_cpu_flush(td->page_cache);
+
+ /*
+ * 1) Last transfer descriptor makes the
+ * transfer done
+ */
+ if (((void *)td) == xfer->td_transfer_last) {
+ DPRINTF("TD is last\n");
+ xhci_generic_done(xfer);
+ break;
+ }
+
+ /*
+ * 2) Any kind of error makes the transfer
+ * done
+ */
+ if (halted) {
+ DPRINTF("TD has I/O error\n");
+ xhci_generic_done(xfer);
+ break;
+ }
+
+ /*
+ * 3) If there is no alternate next transfer,
+ * a short packet also makes the transfer done
+ */
+ if (td->remainder > 0) {
+ DPRINTF("TD has short pkt\n");
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr ||
+ xfer->flags_int.control_xfr) {
+ /* follow the alt next */
+ xfer->td_transfer_cache = td->alt_next;
+ xhci_activate_transfer(xfer);
+ break;
+ }
+ xhci_skip_transfer(xfer);
+ xhci_generic_done(xfer);
+ break;
+ }
+
+ /*
+ * 4) Transfer complete - go to next TD
+ */
+ DPRINTF("Following next TD\n");
+ xfer->td_transfer_cache = td->obj_next;
+ xhci_activate_transfer(xfer);
+ break; /* there should only be one match */
+ }
+ }
+}
+
+static void
+xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb)
+{
+ if (sc->sc_cmd_addr == trb->qwTrb0) {
+ DPRINTF("Received command event\n");
+ sc->sc_cmd_result[0] = trb->dwTrb2;
+ sc->sc_cmd_result[1] = trb->dwTrb3;
+ cv_signal(&sc->sc_cmd_cv);
+ }
+}
+
+static void
+xhci_interrupt_poll(struct xhci_softc *sc)
+{
+ struct usb_page_search buf_res;
+ struct xhci_hw_root *phwr;
+ uint64_t addr;
+ uint32_t temp;
+ uint16_t i;
+ uint8_t event;
+ uint8_t j;
+ uint8_t k;
+ uint8_t t;
+
+ usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
+
+ phwr = buf_res.buffer;
+
+ /* Receive any events */
+
+ usb_pc_cpu_invalidate(&sc->sc_hw.root_pc);
+
+ i = sc->sc_event_idx;
+ j = sc->sc_event_ccs;
+ t = 2;
+
+ while (1) {
+
+ temp = le32toh(phwr->hwr_events[i].dwTrb3);
+
+ k = (temp & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0;
+
+ if (j != k)
+ break;
+
+ event = XHCI_TRB_3_TYPE_GET(temp);
+
+ DPRINTFN(10, "event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n",
+ i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0),
+ (long)le32toh(phwr->hwr_events[i].dwTrb2),
+ (long)le32toh(phwr->hwr_events[i].dwTrb3));
+
+ switch (event) {
+ case XHCI_TRB_EVENT_TRANSFER:
+ xhci_check_transfer(sc, &phwr->hwr_events[i]);
+ break;
+ case XHCI_TRB_EVENT_CMD_COMPLETE:
+ xhci_check_command(sc, &phwr->hwr_events[i]);
+ break;
+ default:
+ DPRINTF("Unhandled event = %u\n", event);
+ break;
+ }
+
+ i++;
+
+ if (i == XHCI_MAX_EVENTS) {
+ i = 0;
+ j ^= 1;
+
+ /* check for timeout */
+ if (!--t)
+ break;
+ }
+ }
+
+ sc->sc_event_idx = i;
+ sc->sc_event_ccs = j;
+
+ /*
+ * NOTE: The Event Ring Dequeue Pointer Register is 64-bit
+ * latched. That means to activate the register we need to
+ * write both the low and high double word of the 64-bit
+ * register.
+ */
+
+ addr = (uint32_t)buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i];
+
+ /* try to clear busy bit */
+ addr |= XHCI_ERDP_LO_BUSY;
+
+ XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
+ XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
+}
+
+static usb_error_t
+xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
+ uint16_t timeout_ms)
+{
+ struct usb_page_search buf_res;
+ struct xhci_hw_root *phwr;
+ uint64_t addr;
+ uint32_t temp;
+ uint8_t i;
+ uint8_t j;
+ int err;
+
+ XHCI_CMD_ASSERT_LOCKED(sc);
+
+ /* get hardware root structure */
+
+ usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
+
+ phwr = buf_res.buffer;
+
+ /* Queue command */
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable
mailing list