summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/xhci.c72
1 files changed, 70 insertions, 2 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index cf9ef254b9f..e2ca8e8056d 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xhci.c,v 1.131 2024/05/23 03:21:09 jsg Exp $ */
+/* $OpenBSD: xhci.c,v 1.132 2024/08/06 17:30:04 kettenis Exp $ */
/*
* Copyright (c) 2014-2015 Martin Pieuchot
@@ -79,6 +79,7 @@ struct xhci_pipe {
};
int xhci_reset(struct xhci_softc *);
+void xhci_suspend(struct xhci_softc *);
int xhci_intr1(struct xhci_softc *);
void xhci_event_dequeue(struct xhci_softc *);
void xhci_event_xfer(struct xhci_softc *, uint64_t, uint32_t, uint32_t);
@@ -533,7 +534,7 @@ xhci_activate(struct device *self, int act)
break;
case DVACT_POWERDOWN:
rv = config_activate_children(self, act);
- xhci_reset(sc);
+ xhci_suspend(sc);
break;
default:
rv = config_activate_children(self, act);
@@ -578,6 +579,73 @@ xhci_reset(struct xhci_softc *sc)
}
void
+xhci_suspend(struct xhci_softc *sc)
+{
+ uint32_t hcr;
+ int i;
+
+ XOWRITE4(sc, XHCI_USBCMD, 0); /* Halt controller */
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = XOREAD4(sc, XHCI_USBSTS) & XHCI_STS_HCH;
+ if (hcr)
+ break;
+ }
+
+ if (!hcr) {
+ printf("%s: halt timeout\n", DEVNAME(sc));
+ xhci_reset(sc);
+ return;
+ }
+
+ /*
+ * Some Intel controllers will not power down completely
+ * unless they have seen a save state command. This in turn
+ * will prevent the SoC from reaching its lowest idle state.
+ * So save the state here.
+ *
+ * Note that we don't restore this saved state anywhere.
+ * Instead we reset the controller and reinitialize it from
+ * scratch when we resume.
+ */
+
+ XOWRITE4(sc, XHCI_USBCMD, XHCI_CMD_CSS); /* Save state */
+ hcr = XOREAD4(sc, XHCI_USBSTS);
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = XOREAD4(sc, XHCI_USBSTS) & XHCI_STS_SSS;
+ if (!hcr)
+ break;
+ }
+
+ if (hcr) {
+ printf("%s: save state timeout\n", DEVNAME(sc));
+ xhci_reset(sc);
+ return;
+ }
+
+ /* Disable interrupts. */
+ XRWRITE4(sc, XHCI_IMOD(0), 0);
+ XRWRITE4(sc, XHCI_IMAN(0), 0);
+
+ /* Clear the event ring address. */
+ XRWRITE4(sc, XHCI_ERDP_LO(0), 0);
+ XRWRITE4(sc, XHCI_ERDP_HI(0), 0);
+
+ XRWRITE4(sc, XHCI_ERSTBA_LO(0), 0);
+ XRWRITE4(sc, XHCI_ERSTBA_HI(0), 0);
+
+ XRWRITE4(sc, XHCI_ERSTSZ(0), 0);
+
+ /* Clear the command ring address. */
+ XOWRITE4(sc, XHCI_CRCR_LO, 0);
+ XOWRITE4(sc, XHCI_CRCR_HI, 0);
+
+ XOWRITE4(sc, XHCI_DCBAAP_LO, 0);
+ XOWRITE4(sc, XHCI_DCBAAP_HI, 0);
+}
+
+void
xhci_reinit(struct xhci_softc *sc)
{
xhci_reset(sc);