svn commit: r230302 - in stable/9/sys/dev/usb: . controller
Hans Petter Selasky
hselasky at FreeBSD.org
Wed Jan 18 07:57:18 UTC 2012
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_ */
More information about the svn-src-stable-9
mailing list