summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2011-10-26 17:43:27 +0000
committerhselasky <hselasky@FreeBSD.org>2011-10-26 17:43:27 +0000
commitd16be1d43585dd21b7cefb1b2d1c7d65968ee7cb (patch)
tree61cae9efa2e722177b98d8b8f0321329ef7db193 /sys/dev/usb
parent6ae1a53eda68ab65a69718a023f2b6cd2b3dce28 (diff)
downloadFreeBSD-src-d16be1d43585dd21b7cefb1b2d1c7d65968ee7cb.zip
FreeBSD-src-d16be1d43585dd21b7cefb1b2d1c7d65968ee7cb.tar.gz
Fix suspend and resume of FULL and HIGH speed USB devices
in the generic XHCI driver. There appears to be some minor logic missing for this feature to work. MFC after: 3 days
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/controller/xhci.c15
-rw-r--r--sys/dev/usb/controller/xhcireg.h2
-rw-r--r--sys/dev/usb/usb.h1
-rw-r--r--sys/dev/usb/usb_hub.c1
4 files changed, 17 insertions, 2 deletions
diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
index f6c8c0e..0df4c4d 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -3048,7 +3048,9 @@ xhci_roothub_exec(struct usb_device *udev,
}
port = XHCI_PORTSC(index);
- v = XREAD4(sc, oper, port) & ~XHCI_PS_CLEAR;
+ v = XREAD4(sc, oper, port);
+ i = XHCI_PS_PLS_GET(v);
+ v &= ~XHCI_PS_CLEAR;
switch (value) {
case UHF_C_BH_PORT_RESET:
@@ -3082,6 +3084,17 @@ xhci_roothub_exec(struct usb_device *udev,
XWRITE4(sc, oper, port, v & ~XHCI_PS_PIC_SET(3));
break;
case UHF_PORT_SUSPEND:
+
+ /* U3 -> U15 */
+ if (i == 3) {
+ XWRITE4(sc, oper, port, v |
+ XHCI_PS_PLS_SET(0xF) | XHCI_PS_LWS);
+ }
+
+ /* wait 20ms for resume sequence to complete */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
+
+ /* U0 */
XWRITE4(sc, oper, port, v |
XHCI_PS_PLS_SET(0) | XHCI_PS_LWS);
break;
diff --git a/sys/dev/usb/controller/xhcireg.h b/sys/dev/usb/controller/xhcireg.h
index 71c5c28..8be502e 100644
--- a/sys/dev/usb/controller/xhcireg.h
+++ b/sys/dev/usb/controller/xhcireg.h
@@ -133,7 +133,7 @@
#define XHCI_PS_WOE 0x08000000 /* RW - wake on over-current enable */
#define XHCI_PS_DR 0x40000000 /* RO - device removable */
#define XHCI_PS_WPR 0x80000000U /* RW - warm port reset */
-#define XHCI_PS_CLEAR 0x80FF00F7U /* command bits */
+#define XHCI_PS_CLEAR 0x80FF01FFU /* command bits */
#define XHCI_PORTPMSC(n) (0x3F4 + (0x10 * (n))) /* XHCI status and control */
#define XHCI_PM3_U1TO_GET(x) (((x) >> 0) & 0xFF) /* RW - U1 timeout */
diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h
index 89bc2c4..6a9b126 100644
--- a/sys/dev/usb/usb.h
+++ b/sys/dev/usb/usb.h
@@ -686,6 +686,7 @@ struct usb_port_status {
#define UPS_PORT_LS_HOT_RST 0x09
#define UPS_PORT_LS_COMP_MODE 0x0A
#define UPS_PORT_LS_LOOPBACK 0x0B
+#define UPS_PORT_LS_RESUME 0x0F
#define UPS_PORT_POWER 0x0100
#define UPS_LOW_SPEED 0x0200
#define UPS_HIGH_SPEED 0x0400
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index 12898ec..5795d56 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -611,6 +611,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_RESUME:
is_suspend = 0;
break;
default:
OpenPOWER on IntegriCloud