summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2012-01-13 22:26:13 +0000
committerhselasky <hselasky@FreeBSD.org>2012-01-13 22:26:13 +0000
commitb05525eee189400e2e6bba9c83e024310df1ecc0 (patch)
tree2a8d3b88566699bb77c0385f87fb9066bd1ae433
parentd848d170333deb40ec475595eeab19c06dfcd677 (diff)
downloadFreeBSD-src-b05525eee189400e2e6bba9c83e024310df1ecc0.zip
FreeBSD-src-b05525eee189400e2e6bba9c83e024310df1ecc0.tar.gz
Improve support for USB 3.0 HUBs. In certain states we
should do a warm reset instead of the default reset. MFC after: 5 days
-rw-r--r--sys/dev/usb/usb_hub.c14
-rw-r--r--sys/dev/usb/usb_request.c74
2 files changed, 64 insertions, 24 deletions
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index b82fd76..fe40100 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -627,14 +627,15 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
}
} 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_U2:
- 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;
}
}
@@ -793,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 */
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index f95f883..c403a70 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -785,12 +785,17 @@ usbd_req_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
+
+ 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 *udev, struct mtx *mtx, uint8_t port)
#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 *udev, struct mtx *mtx, uint8_t port)
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 *udev, struct mtx *mtx, uint8_t port)
/* 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_device *udev, struct mtx *mtx, uint8_t port)
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_device *udev, struct mtx *mtx, uint8_t port)
/* 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) {
OpenPOWER on IntegriCloud