Author: hselasky
Date: Thu Jan 12 21:21:20 2012
New Revision: 230032
URL: http://svn.freebsd.org/changeset/base/230032

Log:
  - Try to fix support for USB 3.0 HUBs.
  - Try to fix support for USB 3.0 suspend and resume.
  
  MFC after:    1 week

Modified:
  head/sys/dev/usb/controller/xhci.c
  head/sys/dev/usb/usb.h
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_request.c
  head/sys/dev/usb/usb_request.h

Modified: head/sys/dev/usb/controller/xhci.c
==============================================================================
--- head/sys/dev/usb/controller/xhci.c  Thu Jan 12 21:12:16 2012        
(r230031)
+++ head/sys/dev/usb/controller/xhci.c  Thu Jan 12 21:21:20 2012        
(r230032)
@@ -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) {
@@ -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;
@@ -3190,7 +3195,7 @@ xhci_roothub_exec(struct usb_device *ude
                if (v & XHCI_PS_PR)
                        i |= UPS_RESET;
                if (v & XHCI_PS_PP)
-                       i |= UPS_PORT_POWER;
+                       i |= UPS_PORT_POWER_SS;
                USETW(sc->sc_hub_desc.ps.wPortStatus, i);
 
                i = 0;

Modified: head/sys/dev/usb/usb.h
==============================================================================
--- head/sys/dev/usb/usb.h      Thu Jan 12 21:12:16 2012        (r230031)
+++ head/sys/dev/usb/usb.h      Thu Jan 12 21:21:20 2012        (r230032)
@@ -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: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c  Thu Jan 12 21:12:16 2012        (r230031)
+++ head/sys/dev/usb/usb_hub.c  Thu Jan 12 21:21:20 2012        (r230032)
@@ -369,10 +369,25 @@ repeat:
        }
        /* check if there is no power on the port and print a warning */
 
-       if (!(sc->sc_st.port_status & UPS_PORT_POWER)) {
-               DPRINTF("WARNING: strange, connected port %d "
-                   "has no power\n", portno);
+       switch (udev->speed) {
+       case USB_SPEED_HIGH:
+       case USB_SPEED_FULL:
+       case USB_SPEED_LOW:
+               if (!(sc->sc_st.port_status & UPS_PORT_POWER)) {
+                       DPRINTF("WARNING: strange, connected port %d "
+                           "has no power\n", portno);
+               }
+               break;
+       case USB_SPEED_SUPER:
+               if (!(sc->sc_st.port_status & UPS_PORT_POWER_SS)) {
+                       DPRINTF("WARNING: strange, connected port %d "
+                           "has no power\n", portno);
+               }
+               break;
+       default:
+               break;
        }
+
        /* check if the device is in Host Mode */
 
        if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) {
@@ -611,6 +626,7 @@ uhub_suspend_resume_port(struct uhub_sof
                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:
                        is_suspend = 0;
                        break;
@@ -632,8 +648,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:
@@ -2064,7 +2079,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 +2258,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 +2307,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 +2368,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 +2400,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 +2456,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: head/sys/dev/usb/usb_request.c
==============================================================================
--- head/sys/dev/usb/usb_request.c      Thu Jan 12 21:12:16 2012        
(r230031)
+++ head/sys/dev/usb/usb_request.c      Thu Jan 12 21:21:20 2012        
(r230032)
@@ -2164,3 +2164,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: head/sys/dev/usb/usb_request.h
==============================================================================
--- head/sys/dev/usb/usb_request.h      Thu Jan 12 21:12:16 2012        
(r230031)
+++ head/sys/dev/usb/usb_request.h      Thu Jan 12 21:21:20 2012        
(r230032)
@@ -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_ */
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to