git: a23fa3fe1c0a - main - amdsmu: Diagnose S0i3 entry after resume

From: Aymeric Wibo <obiwac_at_FreeBSD.org>
Date: Fri, 18 Sep 2026 10:59:27 UTC
The branch main has been updated by obiwac:

URL: https://cgit.FreeBSD.org/src/commit/?id=a23fa3fe1c0aa4e529b26bbec17d17571f8708fd

commit a23fa3fe1c0aa4e529b26bbec17d17571f8708fd
Author:     Aymeric Wibo <obiwac@FreeBSD.org>
AuthorDate: 2026-09-18 10:50:22 +0000
Commit:     Aymeric Wibo <obiwac@FreeBSD.org>
CommitDate: 2026-09-18 10:59:14 +0000

    amdsmu: Diagnose S0i3 entry after resume
    
    Print out diagnostic information after resuming from suspend-to-idle if
    we failed to enter S0i3, i.e. the IP blocks that were blocking entry to
    S0i3.
    
    Don't give detailed IP block info for other SMUs than for Phoenix, as I
    have not had a chance to test these yet and the SMU seems to be very
    quirky.
    
    Reviewed by:    olce
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D59673
---
 sys/dev/amdsmu/amdsmu.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/amdsmu/amdsmu.h |  1 +
 2 files changed, 49 insertions(+)

diff --git a/sys/dev/amdsmu/amdsmu.c b/sys/dev/amdsmu/amdsmu.c
index ffffe0b5219f..5902e5e9af89 100644
--- a/sys/dev/amdsmu/amdsmu.c
+++ b/sys/dev/amdsmu/amdsmu.c
@@ -317,6 +317,51 @@ amdsmu_resume(device_t dev, enum power_stype stype)
 	amdsmu_fetch_idlemask(dev);
 }
 
+static void
+amdsmu_resume_check(device_t dev, enum power_stype stype)
+{
+	struct amdsmu_softc *sc = device_get_softc(dev);
+	struct amdsmu_metrics *m = &sc->metrics;
+	bool any_blocking = false;
+
+	if (stype != POWER_STYPE_SUSPEND_TO_IDLE)
+		return;
+	if (m->s0i3_last_entry_status != 0)
+		return;
+
+	device_printf(dev,
+	    "failed to enter S0i3 during last suspend; "
+	    "battery drain will be excessive\n");
+
+	if (sc->product->amdsmu_deviceid != PCI_DEVICEID_AMD_PHOENIX_ROOT) {
+		device_printf(dev, "only limited diagnostic information "
+		    "available for %x:%x; check dev.amdsmu.0 sysctl tree\n",
+		    sc->product->amdsmu_vendorid,
+		    sc->product->amdsmu_deviceid);
+		return;
+	}
+
+	for (size_t i = 0; i < sc->product->ip_block_count; i++) {
+		/*
+		 * An IP block only truly blocked S0i3 entry if it was active
+		 * for the entire time spent in "SWDRIPS".
+		 */
+		if (m->ip_block_last_active_time[i] < m->time_last_in_sw_drips)
+			continue;
+		if (!any_blocking) {
+			device_printf(dev,
+			    "IP blocks that blocked S0i3 entry:\n");
+			any_blocking = true;
+		}
+		device_printf(dev, "  %s (active for %ju us)\n",
+		    sc->product->ip_blocks_names[i],
+		    (uintmax_t)m->ip_block_last_active_time[i]);
+	}
+
+	if (!any_blocking)
+		device_printf(dev, "no IP block info available\n");
+}
+
 static int
 amdsmu_attach(device_t dev)
 {
@@ -461,6 +506,8 @@ amdsmu_attach(device_t dev)
 	    amdsmu_suspend, dev, EVENTHANDLER_PRI_LAST);
 	sc->eh_resume = EVENTHANDLER_REGISTER(acpi_pre_dev_resume,
 	    amdsmu_resume, dev, EVENTHANDLER_PRI_FIRST);
+	sc->eh_resume_check = EVENTHANDLER_REGISTER(power_resume_check,
+	    amdsmu_resume_check, dev, EVENTHANDLER_PRI_ANY);
 #endif
 
 	return (0);
@@ -482,6 +529,7 @@ amdsmu_detach(device_t dev)
 #if defined(DEV_ACPI)
 	EVENTHANDLER_DEREGISTER(acpi_post_dev_suspend, sc->eh_suspend);
 	EVENTHANDLER_DEREGISTER(acpi_pre_dev_resume, sc->eh_resume);
+	EVENTHANDLER_DEREGISTER(power_resume_check, sc->eh_resume_check);
 #endif
 
 	bus_space_unmap(sc->bus_tag, sc->smu_space, SMU_MEM_SIZE);
diff --git a/sys/dev/amdsmu/amdsmu.h b/sys/dev/amdsmu/amdsmu.h
index 0e7c4df37a43..3b600fab118d 100644
--- a/sys/dev/amdsmu/amdsmu.h
+++ b/sys/dev/amdsmu/amdsmu.h
@@ -123,6 +123,7 @@ struct amdsmu_softc {
 
 	struct eventhandler_entry	*eh_suspend;
 	struct eventhandler_entry	*eh_resume;
+	struct eventhandler_entry	*eh_resume_check;
 
 	struct resource		*res;
 	bus_space_tag_t 	bus_tag;