Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb xhci(4): Draft suspend/resume.



details:   https://anonhg.NetBSD.org/src/rev/d39159ce3dfa
branches:  trunk
changeset: 379251:d39159ce3dfa
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun May 23 11:49:45 2021 +0000

description:
xhci(4): Draft suspend/resume.

Work almost entirely done and tested by maya@ based on xhci 1.2 spec;
tidied up and tweaked by me.

Not sure about issuing Stop Endpoint commands or ensuring the Command
Ring is in the Stopped or Idle state, but this seems to work as is,
so it's already an improvement over what we had before which was no
xhci suspend/resume at all.

In particular, it's not clear to us:

- if we don't have any pending USB activity whether we need to issue
  the Stop Endpoints or quiesce the command ring; but

- if we do have any pending USB activity whether issuing Stop
  Endpoint is enough or whether we also need to do anything to
  synchronize with other software logic to quiesce it too.

diffstat:

 sys/dev/usb/xhci.c    |  357 ++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/usb/xhcireg.h |    6 +-
 sys/dev/usb/xhcivar.h |   14 +-
 3 files changed, 366 insertions(+), 11 deletions(-)

diffs (truncated from 452 to 300 lines):

diff -r c61024034816 -r d39159ce3dfa sys/dev/usb/xhci.c
--- a/sys/dev/usb/xhci.c        Sun May 23 08:59:08 2021 +0000
+++ b/sys/dev/usb/xhci.c        Sun May 23 11:49:45 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: xhci.c,v 1.138 2021/01/05 18:00:21 skrll Exp $ */
+/*     $NetBSD: xhci.c,v 1.139 2021/05/23 11:49:45 riastradh Exp $     */
 
 /*
  * Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.138 2021/01/05 18:00:21 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.139 2021/05/23 11:49:45 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -388,7 +388,6 @@ xhci_rt_write_4(const struct xhci_softc 
        bus_space_write_4(sc->sc_iot, sc->sc_rbh, offset, value);
 }
 
-#if 0 /* unused */
 static inline uint64_t
 xhci_rt_read_8(const struct xhci_softc * const sc, bus_size_t offset)
 {
@@ -408,7 +407,6 @@ xhci_rt_read_8(const struct xhci_softc *
 
        return value;
 }
-#endif /* unused */
 
 static inline void
 xhci_rt_write_8(const struct xhci_softc * const sc, bus_size_t offset,
@@ -698,15 +696,357 @@ xhci_activate(device_t self, enum devact
 }
 
 bool
-xhci_suspend(device_t dv, const pmf_qual_t *qual)
+xhci_suspend(device_t self, const pmf_qual_t *qual)
 {
-       return false;
+       struct xhci_softc * const sc = device_private(self);
+       size_t i, j, bn;
+       int port;
+       uint32_t v;
+
+       XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+       /*
+        * First, suspend all the ports:
+        *
+        * xHCI Requirements Specification 1.2, May 2019, Sec. 4.15:
+        * Suspend-Resume, pp. 276-283
+        * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf#page=276
+        */
+       for (bn = 0; bn < 2; bn++) {
+               for (i = 1; i <= sc->sc_rhportcount[bn]; i++) {
+                       /* 4.15.1: Port Suspend.  */
+                       port = XHCI_PORTSC(xhci_rhport2ctlrport(sc, bn, i));
+
+                       /*
+                        * `System software places individual ports
+                        *  into suspend mode by writing a ``3'' into
+                        *  the appropriate PORTSC register Port Link
+                        *  State (PLS) field (refer to Section 5.4.8).
+                        *  Software should only set the PLS field to
+                        *  ``3'' when the port is in the Enabled
+                        *  state.'
+                        *
+                        * `Software should not attempt to suspend a
+                        *  port unless the port reports that it is in
+                        *  the enabled (PED = ``1''; PLS < ``3'')
+                        *  state (refer to Section 5.4.8 for more
+                        *  information about PED and PLS).'
+                        */
+                       v = xhci_op_read_4(sc, port);
+                       if (((v & XHCI_PS_PED) == 0) ||
+                           XHCI_PS_PLS_GET(v) >= XHCI_PS_PLS_U3)
+                               continue;
+                       v &= ~(XHCI_PS_PLS_MASK | XHCI_PS_CLEAR);
+                       v |= XHCI_PS_LWS | XHCI_PS_PLS_SET(XHCI_PS_PLS_SETU3);
+                       xhci_op_write_4(sc, port, v);
+
+                       /*
+                        * `When the PLS field is written with U3
+                        *  (``3''), the status of the PLS bit will not
+                        *  change to the target U state U3 until the
+                        *  suspend signaling has completed to the
+                        *  attached device (which may be as long as
+                        *  10ms.).'
+                        *
+                        * `Software is required to wait for U3
+                        *  transitions to complete before it puts the
+                        *  xHC into a low power state, and before
+                        *  resuming the port.'
+                        *
+                        * XXX Take advantage of the technique to
+                        * reduce polling on host controllers that
+                        * support the U3C capability.
+                        */
+                       for (j = 0; j < XHCI_WAIT_PLS_U3; j++) {
+                               v = xhci_op_read_4(sc, port);
+                               if (XHCI_PS_PLS_GET(v) == XHCI_PS_PLS_U3)
+                                       break;
+                               usb_delay_ms(&sc->sc_bus, 1);
+                       }
+                       if (j == XHCI_WAIT_PLS_U3) {
+                               device_printf(self,
+                                   "suspend timeout on bus %zu port %zu\n",
+                                   bn, i);
+                               return false;
+                       }
+               }
+       }
+
+       /*
+        * xHCI Requirements Specification 1.2, May 2019, Sec. 4.23.2:
+        * xHCI Power Management, p. 342
+        * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf#page=342
+        */
+
+       /*
+        * `1. Stop all USB activity by issuing Stop Endpoint Commands
+        *     for Busy endpoints in the Running state.  If the Force
+        *     Save Context Capability (FSC = ``0'') is not supported,
+        *     then Stop Endpoint Commands shall be issued for all Idle
+        *     endpoints in the Running state as well.  The Stop
+        *     Endpoint Command causes the xHC to update the respective
+        *     Endpoint or Stream Contexts in system memory, e.g. the
+        *     TR Dequeue Pointer, DCS, etc. fields.  Refer to
+        *     Implementation Note "0".'
+        *
+        * XXX Not entirely sure if this is necessary for us; also it
+        * probably has to happen before suspending the ports.
+        */
+
+       /*
+        * `2. Ensure that the Command Ring is in the Stopped state
+        *     (CRR = ``0'') or Idle (i.e. the Command Transfer Ring is
+        *     empty), and all Command Completion Events associated
+        *     with them have been received.'
+        *
+        * XXX
+        */
+
+       /* `3. Stop the controller by setting Run/Stop (R/S) = ``0''.'  */
+       xhci_op_write_4(sc, XHCI_USBCMD,
+           xhci_op_read_4(sc, XHCI_USBCMD) & ~XHCI_CMD_RS);
+
+       /*
+        * `4. Read the Operational Runtime, and VTIO registers in the
+        *     following order: USBCMD, DNCTRL, DCBAAP, CONFIG, ERSTSZ,
+        *     ERSTBA, ERDP, IMAN, IMOD, and VTIO and save their
+        *     state.'
+        *
+        * (We don't use VTIO here (XXX for now?).)
+        */
+       sc->sc_regs.usbcmd = xhci_op_read_4(sc, XHCI_USBCMD);
+       sc->sc_regs.dnctrl = xhci_op_read_4(sc, XHCI_DNCTRL);
+       sc->sc_regs.dcbaap = xhci_op_read_8(sc, XHCI_DCBAAP);
+       sc->sc_regs.config = xhci_op_read_4(sc, XHCI_CONFIG);
+       sc->sc_regs.erstsz0 = xhci_rt_read_4(sc, XHCI_ERSTSZ(0));
+       sc->sc_regs.erstba0 = xhci_rt_read_8(sc, XHCI_ERSTBA(0));
+       sc->sc_regs.erdp0 = xhci_rt_read_8(sc, XHCI_ERDP(0));
+       sc->sc_regs.iman0 = xhci_rt_read_4(sc, XHCI_IMAN(0));
+       sc->sc_regs.imod0 = xhci_rt_read_4(sc, XHCI_IMOD(0));
+
+       /*
+        * `5. Set the Controller Save State (CSS) flag in the USBCMD
+        *     register (5.4.1)...'
+        */
+       xhci_op_write_4(sc, XHCI_USBCMD,
+           xhci_op_read_4(sc, XHCI_USBCMD) | XHCI_CMD_CSS);
+
+       /*
+        *    `...and wait for the Save State Status (SSS) flag in the
+        *     USBSTS register (5.4.2) to transition to ``0''.'
+        */
+       for (i = 0; i < XHCI_WAIT_SSS; i++) {
+               if ((xhci_op_read_4(sc, XHCI_USBSTS) & XHCI_STS_SSS) == 0)
+                       break;
+               usb_delay_ms(&sc->sc_bus, 1);
+       }
+       if (i >= XHCI_WAIT_SSS) {
+               device_printf(self, "suspend timeout, USBSTS.SSS\n");
+               /*
+                * Just optimistically go on and check SRE anyway --
+                * what's the worst that could happen?
+                */
+       }
+
+       /*
+        * `Note: After a Save or Restore operation completes, the
+        *  Save/Restore Error (SRE) flag in the USBSTS register should
+        *  be checked to ensure that the operation completed
+        *  successfully.'
+        */
+       if (xhci_op_read_4(sc, XHCI_USBSTS) & XHCI_STS_SRE) {
+               device_printf(self, "suspend error, USBSTS.SRE\n");
+               return false;
+       }
+
+       return true;
 }
 
 bool
-xhci_resume(device_t dv, const pmf_qual_t *qual)
+xhci_resume(device_t self, const pmf_qual_t *qual)
 {
-       return false;
+       struct xhci_softc * const sc = device_private(self);
+       size_t i, j, bn, dci;
+       int port;
+       uint32_t v;
+
+       XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+       /*
+        * xHCI Requirements Specification 1.2, May 2019, Sec. 4.23.2:
+        * xHCI Power Management, p. 343
+        * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf#page=343
+        */
+
+       /*
+        * `4. Restore the Operational Runtime, and VTIO registers with
+        *     their previously saved state in the following order:
+        *     DNCTRL, DCBAAP, CONFIG, ERSTSZ, ERSTBA, ERDP, IMAN,
+        *     IMOD, and VTIO.'
+        *
+        * (We don't use VTIO here (for now?).)
+        */
+       xhci_op_write_4(sc, XHCI_USBCMD, sc->sc_regs.usbcmd);
+       xhci_op_write_4(sc, XHCI_DNCTRL, sc->sc_regs.dnctrl);
+       xhci_op_write_8(sc, XHCI_DCBAAP, sc->sc_regs.dcbaap);
+       xhci_op_write_4(sc, XHCI_CONFIG, sc->sc_regs.config);
+       xhci_rt_write_4(sc, XHCI_ERSTSZ(0), sc->sc_regs.erstsz0);
+       xhci_rt_write_8(sc, XHCI_ERSTBA(0), sc->sc_regs.erstba0);
+       xhci_rt_write_8(sc, XHCI_ERDP(0), sc->sc_regs.erdp0);
+       xhci_rt_write_4(sc, XHCI_IMAN(0), sc->sc_regs.iman0);
+       xhci_rt_write_4(sc, XHCI_IMOD(0), sc->sc_regs.imod0);
+
+       memset(&sc->sc_regs, 0, sizeof(sc->sc_regs)); /* paranoia */
+
+       /*
+        * `5. Set the Controller Restore State (CRS) flag in the
+        *     USBCMD register (5.4.1) to ``1''...'
+        */
+       xhci_op_write_4(sc, XHCI_USBCMD,
+           xhci_op_read_4(sc, XHCI_USBCMD) | XHCI_CMD_CRS);
+
+       /*
+        *    `...and wait for the Restore State Status (RSS) in the
+        *     USBSTS register (5.4.2) to transition to ``0''.'
+        */
+       for (i = 0; i < XHCI_WAIT_RSS; i++) {
+               if ((xhci_op_read_4(sc, XHCI_USBSTS) & XHCI_STS_RSS) == 0)
+                       break;
+               usb_delay_ms(&sc->sc_bus, 1);
+       }
+       if (i >= XHCI_WAIT_RSS) {
+               device_printf(self, "suspend timeout, USBSTS.RSS\n");
+               return false;
+       }
+
+       /*
+        * `6. Reinitialize the Command Ring, i.e. so its Cycle bits
+        *     are consistent with the RCS values to be written to the
+        *     CRCR.'
+        *
+        * XXX Hope just zeroing it is good enough!
+        */
+       xhci_host_dequeue(sc->sc_cr);
+
+       /*
+        * `7. Write the CRCR with the address and RCS value of the
+        *     reinitialized Command Ring.  Note that this write will
+        *     cause the Command Ring to restart at the address
+        *     specified by the CRCR.'
+        */
+       xhci_op_write_8(sc, XHCI_CRCR, xhci_ring_trbp(sc->sc_cr, 0) |
+           sc->sc_cr->xr_cs);
+
+       /*
+        * `8. Enable the controller by setting Run/Stop (R/S) =
+        *     ``1''.'
+        */
+       xhci_op_write_4(sc, XHCI_USBCMD,
+           xhci_op_read_4(sc, XHCI_USBCMD) | XHCI_CMD_RS);
+
+       /*
+        * `9. Software shall walk the USB topology and initialize each
+        *     of the xHC PORTSC, PORTPMSC, and PORTLI registers, and
+        *     external hub ports attached to USB devices.'
+        *
+        * This follows the procedure in 4.15 `Suspend-Resume', 4.15.2
+        * `Port Resume', 4.15.2.1 `Host Initiated'.
+        *
+        * XXX We should maybe batch up initiating the state
+        * transitions, and then wait for them to complete all at once.



Home | Main Index | Thread Index | Old Index