Source-Changes-HG archive

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

[src/netbsd-9]: src/sys/dev/usb Pull up following revision(s) (requested by r...



details:   https://anonhg.NetBSD.org/src/rev/c38fe85d2b3a
branches:  netbsd-9
changeset: 984095:c38fe85d2b3a
user:      martin <martin%NetBSD.org@localhost>
date:      Mon Jun 21 17:11:46 2021 +0000

description:
Pull up following revision(s) (requested by riastradh in ticket #1301):

        sys/dev/usb/xhci.c: revision 1.140
        sys/dev/usb/xhci.c: revision 1.141
        sys/dev/usb/xhci.c: revision 1.143
        sys/dev/usb/xhcivar.h: revision 1.18
        sys/dev/usb/xhcivar.h: revision 1.19
        sys/dev/usb/xhcireg.h: revision 1.19
        sys/dev/usb/xhci.c: revision 1.139

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.

xhci(4): Block commands and issue Stop Endpoint on suspend.

xhci: Fix logic in waiting for command queue access.
_Either_ an existing command in progress, _or_ an existing suspend in
progress that is not done by us, should block us; the logic I wrote
previously erroneously blocked only if both conditions happened at
the same time.

Should fix issue reported by Andrius V in the PR kern/56050 followup
discussion.

xhci(4): Wait USB_RESUME_WAIT ms, not 20 ms.
Better to use the named constant, and although the spec says 20 ms is
enough, apparently for some devices it's not.

diffstat:

 sys/dev/usb/xhci.c    |  437 ++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/usb/xhcireg.h |    6 +-
 sys/dev/usb/xhcivar.h |   15 +-
 3 files changed, 441 insertions(+), 17 deletions(-)

diffs (truncated from 577 to 300 lines):

diff -r ad80eba5ea9e -r c38fe85d2b3a sys/dev/usb/xhci.c
--- a/sys/dev/usb/xhci.c        Mon Jun 21 16:41:02 2021 +0000
+++ b/sys/dev/usb/xhci.c        Mon Jun 21 17:11:46 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: xhci.c,v 1.107.2.7 2020/12/23 12:34:38 martin Exp $    */
+/*     $NetBSD: xhci.c,v 1.107.2.8 2021/06/21 17:11:46 martin Exp $    */
 
 /*
  * Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.107.2.7 2020/12/23 12:34:38 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.107.2.8 2021/06/21 17:11:46 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -154,6 +154,8 @@
 static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
 //static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
 static usbd_status xhci_reset_endpoint(struct usbd_pipe *);
+static usbd_status xhci_stop_endpoint_cmd(struct xhci_softc *,
+    struct xhci_slot *, u_int, uint32_t);
 static usbd_status xhci_stop_endpoint(struct usbd_pipe *);
 
 static void xhci_host_dequeue(struct xhci_ring * const);
@@ -369,7 +371,6 @@
        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)
 {
@@ -389,7 +390,6 @@
 
        return value;
 }
-#endif /* unused */
 
 static inline void
 xhci_rt_write_8(const struct xhci_softc * const sc, bus_size_t offset,
@@ -678,15 +678,408 @@
 }
 
 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, dci;
+       int port;
+       uint32_t v;
+       usbd_status err;
+       bool ok = false;
+
+       XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+       mutex_enter(&sc->sc_lock);
+
+       /*
+        * Block issuance of new commands, and wait for all pending
+        * commands to complete.
+        */
+       KASSERT(sc->sc_suspender == NULL);
+       sc->sc_suspender = curlwp;
+       while (sc->sc_command_addr != 0)
+               cv_wait(&sc->sc_cmdbusy_cv, &sc->sc_lock);
+
+       /*
+        * 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".'
+        */
+       for (i = 0; i < sc->sc_maxslots; i++) {
+               struct xhci_slot *xs = &sc->sc_slots[i];
+
+               /* Skip if the slot is not in use.  */
+               if (xs->xs_idx == 0)
+                       continue;
+
+               for (dci = XHCI_DCI_SLOT; dci <= XHCI_MAX_DCI; dci++) {
+                       /* Skip if the endpoint is not Running.  */
+                       /* XXX What about Busy?  */
+                       if (xhci_get_epstate(sc, xs, dci) !=
+                           XHCI_EPSTATE_RUNNING)
+                               continue;
+
+                       /* Stop endpoint.  */
+                       err = xhci_stop_endpoint_cmd(sc, xs, dci,
+                           XHCI_TRB_3_SUSP_EP_BIT);
+                       if (err) {
+                               device_printf(self, "failed to stop endpoint"
+                                   " slot %zu dci %zu err %d\n",
+                                   i, dci, err);
+                               goto out;
+                       }
+               }
+       }
+
+       /*
+        * Next, 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);
+                               goto out;
+                       }
+               }
+       }
+
+       /*
+        * `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");
+               goto out;
+       }
+
+       /* Success!  */
+       ok = true;
+
+out:   mutex_exit(&sc->sc_lock);
+       return ok;
 }
 
 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;
+       bool ok = false;
+
+       XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+       mutex_enter(&sc->sc_lock);
+       KASSERT(sc->sc_suspender);
+
+       /*
+        * 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''...'
+        */



Home | Main Index | Thread Index | Old Index