Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Jan 2012 07:57:17 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r230302 - in stable/9/sys/dev/usb: . controller
Message-ID:  <201201180757.q0I7vHiw029172@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Wed Jan 18 07:57:17 2012
New Revision: 230302
URL: http://svn.freebsd.org/changeset/base/230302

Log:
  MFC r230032, r230050, r230090, r230091 and r228493.
  - Various XHCI and USB 3.0 related issues.
  - USB 3.0 HUBs should work after this change.

Modified:
  stable/9/sys/dev/usb/controller/xhci.c
  stable/9/sys/dev/usb/usb.h
  stable/9/sys/dev/usb/usb_hub.c
  stable/9/sys/dev/usb/usb_request.c
  stable/9/sys/dev/usb/usb_request.h
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)

Modified: stable/9/sys/dev/usb/controller/xhci.c
==============================================================================
--- stable/9/sys/dev/usb/controller/xhci.c	Wed Jan 18 07:56:13 2012	(r230301)
+++ stable/9/sys/dev/usb/controller/xhci.c	Wed Jan 18 07:57:17 2012	(r230302)
@@ -2211,9 +2211,10 @@ xhci_configure_device(struct usb_device 
 	struct usb_device *hubdev;
 	uint32_t temp;
 	uint32_t route;
+	uint32_t rh_port;
 	uint8_t is_hub;
 	uint8_t index;
-	uint8_t rh_port;
+	uint8_t depth;
 
 	index = udev->controller_slot_id;
 
@@ -2235,6 +2236,8 @@ xhci_configure_device(struct usb_device 
 		if (hubdev->parent_hub == NULL)
 			break;
 
+		depth = hubdev->parent_hub->depth;
+
 		/*
 		 * NOTE: HS/FS/LS devices and the SS root HUB can have
 		 * more than 15 ports
@@ -2242,17 +2245,18 @@ xhci_configure_device(struct usb_device 
 
 		rh_port = hubdev->port_no;
 
-		if (hubdev->parent_hub->parent_hub == NULL)
+		if (depth == 0)
 			break;
 
-		route *= 16;
-
 		if (rh_port > 15)
-			route |= 15;
-		else
-			route |= rh_port;
+			rh_port = 15;
+
+		if (depth < 6)
+			route |= rh_port << (4 * (depth - 1));
 	}
 
+	DPRINTF("Route=0x%08x\n", route);
+
 	temp = XHCI_SCTX_0_ROUTE_SET(route);
 
 	switch (sc->sc_hw.devs[index].state) {
@@ -2260,7 +2264,7 @@ xhci_configure_device(struct usb_device 
 		temp |= XHCI_SCTX_0_CTX_NUM_SET(XHCI_MAX_ENDPOINTS - 1);
 		break;
 	default:
-		temp = XHCI_SCTX_0_CTX_NUM_SET(1);
+		temp |= XHCI_SCTX_0_CTX_NUM_SET(1);
 		break;
 	}
 
@@ -3063,6 +3067,7 @@ xhci_roothub_exec(struct usb_device *ude
 		case UHF_C_PORT_CONFIG_ERROR:
 			XWRITE4(sc, oper, port, v | XHCI_PS_CEC);
 			break;
+		case UHF_C_PORT_SUSPEND:
 		case UHF_C_PORT_LINK_STATE:
 			XWRITE4(sc, oper, port, v | XHCI_PS_PLC);
 			break;
@@ -3189,8 +3194,13 @@ xhci_roothub_exec(struct usb_device *ude
 			i |= UPS_OVERCURRENT_INDICATOR;
 		if (v & XHCI_PS_PR)
 			i |= UPS_RESET;
-		if (v & XHCI_PS_PP)
+		if (v & XHCI_PS_PP) {
+			/*
+			 * The USB 3.0 RH is using the
+			 * USB 2.0's power bit
+			 */
 			i |= UPS_PORT_POWER;
+		}
 		USETW(sc->sc_hub_desc.ps.wPortStatus, i);
 
 		i = 0;

Modified: stable/9/sys/dev/usb/usb.h
==============================================================================
--- stable/9/sys/dev/usb/usb.h	Wed Jan 18 07:56:13 2012	(r230301)
+++ stable/9/sys/dev/usb/usb.h	Wed Jan 18 07:57:17 2012	(r230302)
@@ -688,6 +688,7 @@ struct usb_port_status {
 #define	UPS_PORT_LS_LOOPBACK	0x0B
 #define	UPS_PORT_LS_RESUME	0x0F
 #define	UPS_PORT_POWER			0x0100
+#define	UPS_PORT_POWER_SS		0x0200	/* super-speed only */
 #define	UPS_LOW_SPEED			0x0200
 #define	UPS_HIGH_SPEED			0x0400
 #define	UPS_OTHER_SPEED			0x0600	/* currently FreeBSD specific */

Modified: stable/9/sys/dev/usb/usb_hub.c
==============================================================================
--- stable/9/sys/dev/usb/usb_hub.c	Wed Jan 18 07:56:13 2012	(r230301)
+++ stable/9/sys/dev/usb/usb_hub.c	Wed Jan 18 07:57:17 2012	(r230302)
@@ -327,6 +327,7 @@ uhub_reattach_port(struct uhub_softc *sc
 	enum usb_dev_speed speed;
 	enum usb_hc_mode mode;
 	usb_error_t err;
+	uint16_t power_mask;
 	uint8_t timeout;
 
 	DPRINTF("reattaching port %d\n", portno);
@@ -369,10 +370,27 @@ repeat:
 	}
 	/* check if there is no power on the port and print a warning */
 
-	if (!(sc->sc_st.port_status & UPS_PORT_POWER)) {
+	switch (udev->speed) {
+	case USB_SPEED_HIGH:
+	case USB_SPEED_FULL:
+	case USB_SPEED_LOW:
+		power_mask = UPS_PORT_POWER;
+		break;
+	case USB_SPEED_SUPER:
+		if (udev->parent_hub == NULL)
+			power_mask = UPS_PORT_POWER;
+		else
+			power_mask = UPS_PORT_POWER_SS;
+		break;
+	default:
+		power_mask = 0;
+		break;
+	}
+	if (!(sc->sc_st.port_status & power_mask)) {
 		DPRINTF("WARNING: strange, connected port %d "
 		    "has no power\n", portno);
 	}
+
 	/* check if the device is in Host Mode */
 
 	if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) {
@@ -609,13 +627,15 @@ uhub_suspend_resume_port(struct uhub_sof
 		}
 	} else {
 		switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
-		case UPS_PORT_LS_U0:
-		case UPS_PORT_LS_U1:
-		case UPS_PORT_LS_RESUME:
+		case UPS_PORT_LS_U3:
+			is_suspend = 1;
+			break;
+		case UPS_PORT_LS_SS_INA:
+			usbd_req_warm_reset_port(udev, NULL, portno);
 			is_suspend = 0;
 			break;
 		default:
-			is_suspend = 1;
+			is_suspend = 0;
 			break;
 		}
 	}
@@ -632,8 +652,7 @@ uhub_suspend_resume_port(struct uhub_sof
 		 */
 		if (is_suspend == 0)
 			usb_dev_resume_peer(child);
-		else if ((child->flags.usb_mode == USB_MODE_DEVICE) ||
-		    (usb_device_20_compatible(child) == 0))
+		else if (child->flags.usb_mode == USB_MODE_DEVICE)
 			usb_dev_suspend_peer(child);
 	}
 done:
@@ -775,7 +794,8 @@ uhub_explore(struct usb_device *udev)
 				break;
 			}
 		}
-		if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) {
+		if (sc->sc_st.port_change & (UPS_C_SUSPEND |
+		    UPS_C_PORT_LINK_STATE)) {
 			err = uhub_suspend_resume_port(sc, portno);
 			if (err) {
 				/* most likely the HUB is gone */
@@ -2064,7 +2084,6 @@ usb_peer_should_wakeup(struct usb_device
 	    (udev->pwr_save.write_refs != 0) ||
 	    ((udev->pwr_save.read_refs != 0) &&
 	    (udev->flags.usb_mode == USB_MODE_HOST) &&
-	    (usb_device_20_compatible(udev) != 0) &&
 	    (usb_peer_can_wakeup(udev) == 0)));
 }
 
@@ -2244,6 +2263,14 @@ usb_dev_resume_peer(struct usb_device *u
 			DPRINTFN(0, "Resuming port failed\n");
 			return;
 		}
+	} else {
+		/* resume current port (Valid in Host and Device Mode) */
+		err = usbd_req_set_port_link_state(udev->parent_hub,
+		    NULL, udev->port_no, UPS_PORT_LS_U0);
+		if (err) {
+			DPRINTFN(0, "Resuming port failed\n");
+			return;
+		}
 	}
 
 	/* resume settle time */
@@ -2285,8 +2312,7 @@ usb_dev_resume_peer(struct usb_device *u
 	usbd_sr_unlock(udev);
 
 	/* check if peer has wakeup capability */
-	if (usb_peer_can_wakeup(udev) &&
-	    usb_device_20_compatible(udev)) {
+	if (usb_peer_can_wakeup(udev)) {
 		/* clear remote wakeup */
 		err = usbd_req_clear_device_feature(udev,
 		    NULL, UF_DEVICE_REMOTE_WAKEUP);
@@ -2347,8 +2373,7 @@ repeat:
 		}
 	}
 
-	if (usb_peer_can_wakeup(udev) &&
-	    usb_device_20_compatible(udev)) {
+	if (usb_peer_can_wakeup(udev)) {
 		/*
 		 * This request needs to be done before we set
 		 * "udev->flags.self_suspended":
@@ -2380,8 +2405,7 @@ repeat:
 	USB_BUS_UNLOCK(udev->bus);
 
 	if (err != 0) {
-		if (usb_peer_can_wakeup(udev) &&
-		    usb_device_20_compatible(udev)) {
+		if (usb_peer_can_wakeup(udev)) {
 			/* allow device to do remote wakeup */
 			err = usbd_req_clear_device_feature(udev,
 			    NULL, UF_DEVICE_REMOTE_WAKEUP);
@@ -2437,6 +2461,14 @@ repeat:
 			DPRINTFN(0, "Suspending port failed\n");
 			return;
 		}
+	} else {
+		/* suspend current port */
+		err = usbd_req_set_port_link_state(udev->parent_hub,
+		    NULL, udev->port_no, UPS_PORT_LS_U3);
+		if (err) {
+			DPRINTFN(0, "Suspending port failed\n");
+			return;
+		}
 	}
 
 	udev = udev->parent_hub;

Modified: stable/9/sys/dev/usb/usb_request.c
==============================================================================
--- stable/9/sys/dev/usb/usb_request.c	Wed Jan 18 07:56:13 2012	(r230301)
+++ stable/9/sys/dev/usb/usb_request.c	Wed Jan 18 07:57:17 2012	(r230302)
@@ -785,12 +785,17 @@ usbd_req_reset_port(struct usb_device *u
 	struct usb_port_status ps;
 	usb_error_t err;
 	uint16_t n;
+	uint16_t status;
+	uint16_t change;
 
 #ifdef USB_DEBUG
 	uint16_t pr_poll_delay;
 	uint16_t pr_recovery_delay;
 
 #endif
+
+	DPRINTF("\n");
+
 	/* clear any leftover port reset changes first */
 	usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_PORT_RESET);
@@ -817,9 +822,6 @@ usbd_req_reset_port(struct usb_device *u
 #endif
 	n = 0;
 	while (1) {
-		uint16_t status;
-		uint16_t change;
-
 #ifdef USB_DEBUG
 		/* wait for the device to recover from reset */
 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
@@ -830,9 +832,9 @@ usbd_req_reset_port(struct usb_device *u
 		n += USB_PORT_RESET_DELAY;
 #endif
 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
-		if (err) {
+		if (err)
 			goto done;
-		}
+
 		status = UGETW(ps.wPortStatus);
 		change = UGETW(ps.wPortChange);
 
@@ -862,9 +864,9 @@ usbd_req_reset_port(struct usb_device *u
 	/* clear port reset first */
 	err = usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_PORT_RESET);
-	if (err) {
+	if (err)
 		goto done;
-	}
+
 	/* check for timeout */
 	if (n == 0) {
 		err = USB_ERR_TIMEOUT;
@@ -898,21 +900,50 @@ done:
  *       disabled.
  *------------------------------------------------------------------------*/
 usb_error_t
-usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
+usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx,
+    uint8_t port)
 {
 	struct usb_port_status ps;
 	usb_error_t err;
 	uint16_t n;
+	uint16_t status;
+	uint16_t change;
 
 #ifdef USB_DEBUG
 	uint16_t pr_poll_delay;
 	uint16_t pr_recovery_delay;
 
 #endif
-	err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET);
-	if (err) {
+
+	DPRINTF("\n");
+
+	err = usbd_req_get_port_status(udev, mtx, &ps, port);
+	if (err)
 		goto done;
+
+	status = UGETW(ps.wPortStatus);
+
+	switch (UPS_PORT_LINK_STATE_GET(status)) {
+	case UPS_PORT_LS_U3:
+	case UPS_PORT_LS_COMP_MODE:
+	case UPS_PORT_LS_LOOPBACK:
+	case UPS_PORT_LS_SS_INA:
+		break;
+	default:
+		DPRINTF("Wrong state for warm reset\n");
+		return (0);
 	}
+
+	/* clear any leftover warm port reset changes first */
+	usbd_req_clear_port_feature(udev, mtx,
+	    port, UHF_C_BH_PORT_RESET);
+
+	/* set warm port reset */
+	err = usbd_req_set_port_feature(udev, mtx,
+	    port, UHF_BH_PORT_RESET);
+	if (err)
+		goto done;
+
 #ifdef USB_DEBUG
 	/* range check input parameters */
 	pr_poll_delay = usb_pr_poll_delay;
@@ -938,17 +969,20 @@ usbd_req_warm_reset_port(struct usb_devi
 		n += USB_PORT_RESET_DELAY;
 #endif
 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
-		if (err) {
+		if (err)
 			goto done;
-		}
+
+		status = UGETW(ps.wPortStatus);
+		change = UGETW(ps.wPortChange);
+
 		/* if the device disappeared, just give up */
-		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
+		if (!(status & UPS_CURRENT_CONNECT_STATUS))
 			goto done;
-		}
+
 		/* check if reset is complete */
-		if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) {
+		if (change & UPS_C_BH_PORT_RESET)
 			break;
-		}
+
 		/* check for timeout */
 		if (n > 1000) {
 			n = 0;
@@ -959,9 +993,9 @@ usbd_req_warm_reset_port(struct usb_devi
 	/* clear port reset first */
 	err = usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_BH_PORT_RESET);
-	if (err) {
+	if (err)
 		goto done;
-	}
+
 	/* check for timeout */
 	if (n == 0) {
 		err = USB_ERR_TIMEOUT;
@@ -2004,6 +2038,10 @@ retry:
 		}
 	}
 
+	/* Try to warm reset first */
+	if (parent_hub->speed == USB_SPEED_SUPER)
+		usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no);
+
 	/* Try to reset the parent HUB port. */
 	err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
 	if (err) {
@@ -2164,3 +2202,27 @@ usbd_req_clear_tt_buffer(struct usb_devi
 	USETW(req.wLength, 0);
 	return (usbd_do_request(udev, mtx, &req, 0));
 }
+
+/*------------------------------------------------------------------------*
+ *	usbd_req_set_port_link_state
+ *
+ * USB 3.0 specific request
+ *
+ * Returns:
+ *    0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx,
+    uint8_t port, uint8_t link_state)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_CLASS_OTHER;
+	req.bRequest = UR_SET_FEATURE;
+	USETW(req.wValue, UHF_PORT_LINK_STATE);
+	req.wIndex[0] = port;
+	req.wIndex[1] = link_state;
+	USETW(req.wLength, 0);
+	return (usbd_do_request(udev, mtx, &req, 0));
+}

Modified: stable/9/sys/dev/usb/usb_request.h
==============================================================================
--- stable/9/sys/dev/usb/usb_request.h	Wed Jan 18 07:56:13 2012	(r230301)
+++ stable/9/sys/dev/usb/usb_request.h	Wed Jan 18 07:57:17 2012	(r230302)
@@ -89,5 +89,7 @@ usb_error_t usbd_req_reset_tt(struct usb
 		    uint8_t port);
 usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx,
 		    uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint);
+usb_error_t usbd_req_set_port_link_state(struct usb_device *udev,
+		    struct mtx *mtx, uint8_t port, uint8_t link_state);
 
 #endif					/* _USB_REQUEST_H_ */



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