- Try to fix support for USB 3.0 HUBs.

- Try to fix support for USB 3.0 suspend and resume.

MFC after:	1 week
This commit is contained in:
Hans Petter Selasky 2012-01-12 21:21:20 +00:00
parent ab71f27172
commit 4131f6fb60
5 changed files with 79 additions and 20 deletions

View file

@ -2211,9 +2211,10 @@ xhci_configure_device(struct usb_device *udev)
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 *udev)
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 *udev)
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 *udev,
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 *udev,
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;

View file

@ -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 */

View file

@ -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_softc *sc, uint8_t portno)
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_softc *sc, uint8_t portno)
*/
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)
(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 *udev)
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 *udev)
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;

View file

@ -2164,3 +2164,27 @@ usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx,
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));
}

View file

@ -89,5 +89,7 @@ usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx,
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_ */