Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 Nov 2010 19:35:12 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r215944 - in stable/8/sys: conf dev/usb dev/usb/controller dev/usb/storage modules/usb modules/usb/xhci
Message-ID:  <201011271935.oARJZC41089624@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201011271935.oARJZC41089624>