Page MenuHomeFreeBSD

D48714.id150102.diff
No OneTemporary

D48714.id150102.diff

diff --git a/sys/dev/amdsmu/amdsmu.c b/sys/dev/amdsmu/amdsmu.c
--- a/sys/dev/amdsmu/amdsmu.c
+++ b/sys/dev/amdsmu/amdsmu.c
@@ -12,6 +12,7 @@
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
+#include <sys/sysctl.h>
#include <machine/bus.h>
#include <dev/pci/pcivar.h>
@@ -51,6 +52,36 @@
SMU_MSG_GET_SUP_CONSTRAINTS = 0x09,
};
+/* XXX Copied from Linux struct smu_metrics. */
+struct amdsmu_metrics {
+ uint32_t table_version;
+ uint32_t hint_count;
+ uint32_t s0i3_last_entry_status;
+ uint32_t time_last_in_s0i2;
+ uint64_t time_last_entering_s0i3;
+ uint64_t total_time_entering_s0i3;
+ uint64_t time_last_resuming;
+ uint64_t total_time_resuming;
+ uint64_t time_last_in_s0i3;
+ uint64_t total_time_in_s0i3;
+ uint64_t time_last_in_sw_drips;
+ uint64_t total_time_in_sw_drips;
+ /*
+ * This is how long each IP block was active for (us). In Linux, these
+ * are called "timecondition_notmet_*". I'm assuming this means "how
+ * long have the conditions for this IP block to deactivate not been
+ * met?" I'm not quite sure what these conditions are, however.
+ *
+ * XXX Total active time for IP blocks seems to be buggy and reporting
+ * garbage (at least on Phoenix), so it's disabled for now. The last
+ * active time for the USB4_0 IP block also seems to be buggy.
+ */
+ uint64_t ip_block_last_active_time[32];
+#ifdef IP_BLOCK_TOTAL_ACTIVE_TIME
+ uint64_t ip_block_total_active_time[32];
+#endif
+} __attribute__((packed));
+
/*
* TODO These are in common with amdtemp; should we find a way to factor these
* out? Also, there are way more of these. I couldn't find a centralized place
@@ -70,12 +101,37 @@
{ VENDORID_AMD, CPUID_AMD_STRIX_POINT },
};
+static const char *const amdsmu_ip_blocks_names[] = { "DISPLAY", "CPU", "GFX", "VDD",
+ "ACP", "VCN", "ISP", "NBIO", "DF", "USB3_0", "USB3_1", "LAPIC", "USB3_2",
+ "USB3_3", "USB3_4", "USB4_0", "USB4_1", "MPM", "JPEG", "IPU", "UMSCH",
+ "VPE" };
+
+CTASSERT(nitems(amdsmu_ip_blocks_names) <= 32);
+
struct amdsmu_softc {
+ struct sysctl_ctx_list *sysctlctx;
+ struct sysctl_oid *sysctlnode;
+
struct resource *res;
bus_space_tag_t bus_tag;
bus_space_handle_t smu_space;
bus_space_handle_t reg_space;
+
+ bool added_vers_sysctl;
+ uint8_t smu_program;
+ uint8_t smu_maj, smu_min, smu_rev;
+
+ uint32_t active_ip_blocks;
+ struct sysctl_oid *ip_blocks_sysctlnode;
+ size_t ip_block_count;
+ struct sysctl_oid *ip_block_sysctlnodes[nitems(amdsmu_ip_blocks_names)];
+ bool ip_blocks_active[nitems(amdsmu_ip_blocks_names)];
+
+ bool has_metrics;
+ bus_space_handle_t metrics_space;
+ bool added_metrics_sysctl;
+ struct amdsmu_metrics metrics;
};
static bool
@@ -186,31 +242,241 @@
}
static void
-amdsmu_print_vers(device_t dev)
+amdsmu_get_vers(device_t dev)
{
- uint32_t smu_vers;
- uint8_t smu_program;
- uint8_t smu_maj, smu_min, smu_rev;
+ uint32_t smu_vers;
+ struct amdsmu_softc *sc = device_get_softc(dev);
if (amdsmu_cmd(dev, SMU_MSG_GETSMUVERSION, 0, &smu_vers) != 0) {
device_printf(dev, "failed to get SMU version\n");
return;
}
- smu_program = (smu_vers >> 24) & 0xFF;
- smu_maj = (smu_vers >> 16) & 0xFF;
- smu_min = (smu_vers >> 8) & 0xFF;
- smu_rev = smu_vers & 0xFF;
+ sc->smu_program = (smu_vers >> 24) & 0xFF;
+ sc->smu_maj = (smu_vers >> 16) & 0xFF;
+ sc->smu_min = (smu_vers >> 8) & 0xFF;
+ sc->smu_rev = smu_vers & 0xFF;
device_printf(dev, "SMU version: %d.%d.%d (program %d)\n",
- smu_maj, smu_min, smu_rev, smu_program);
+ sc->smu_maj, sc->smu_min, sc->smu_rev, sc->smu_program);
+
+ /* Add sysctl nodes for SMU version. */
+ if (sc->added_vers_sysctl)
+ return;
+ sc->added_vers_sysctl = true;
+
+ SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO,
+ "program", CTLFLAG_RD, &sc->smu_program, 0, "SMU program number");
+ SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO,
+ "version_major", CTLFLAG_RD, &sc->smu_maj, 0,
+ "SMU firmware major version number");
+ SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO,
+ "version_minor", CTLFLAG_RD, &sc->smu_min, 0,
+ "SMU firmware minor version number");
+ SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO,
+ "version_revision", CTLFLAG_RD, &sc->smu_rev, 0,
+ "SMU firmware revision number");
+}
+
+static void
+amdsmu_get_ip_blocks(device_t dev)
+{
+ struct amdsmu_softc *sc = device_get_softc(dev);
+ const uint16_t deviceid = pci_get_device(dev);
+ struct amdsmu_metrics *m = &sc->metrics;
+ bool active;
+ char sysctl_descr[32];
+
+ /* Get IP block count. */
+ switch (deviceid) {
+ case CPUID_AMD_REMBRANDT:
+ sc->ip_block_count = 12;
+ break;
+ case CPUID_AMD_PHOENIX:
+ sc->ip_block_count = 21;
+ break;
+ /* TODO How many IP blocks does Strix Point (and the others) have? */
+ case CPUID_AMD_STRIX_POINT:
+ default:
+ sc->ip_block_count = nitems(amdsmu_ip_blocks_names);
+ }
+ KASSERT(sc->ip_block_count <= nitems(amdsmu_ip_blocks_names),
+ ("too many IP blocks for array"));
+
+ /* Get and print out IP blocks. */
+ if (amdsmu_cmd(dev, SMU_MSG_GET_SUP_CONSTRAINTS, 0, &sc->active_ip_blocks) != 0) {
+ device_printf(dev, "failed to get IP blocks\n");
+ return;
+ }
+ device_printf(dev, "Active IP blocks: ");
+ for (size_t i = 0; i < sc->ip_block_count; i++) {
+ active = (sc->active_ip_blocks & (1 << i)) != 0;
+ sc->ip_blocks_active[i] = active;
+ if (!active)
+ continue;
+ printf("%s%s", amdsmu_ip_blocks_names[i],
+ i + 1 < sc->ip_block_count ? " " : "\n");
+ }
+
+ /*
+ * Make sure we haven't already created the sysctl tree for IP blocks.
+ */
+ if (sc->ip_blocks_sysctlnode != NULL)
+ return;
+
+ /* Create a sysctl node for IP blocks. */
+ sc->ip_blocks_sysctlnode = SYSCTL_ADD_NODE(sc->sysctlctx,
+ SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, "ip_blocks",
+ CTLFLAG_RD, NULL, "SMU metrics");
+ if (sc->ip_blocks_sysctlnode == NULL) {
+ device_printf(dev, "could not add sysctl node for IP blocks\n");
+ return;
+ }
+
+ /* Create a sysctl node for each IP block. */
+ for (size_t i = 0; i < sc->ip_block_count; i++) {
+ /* Create the sysctl node itself for the IP block. */
+ snprintf(sysctl_descr, sizeof sysctl_descr,
+ "Metrics about the %s AMD IP block",
+ amdsmu_ip_blocks_names[i]);
+ sc->ip_block_sysctlnodes[i] = SYSCTL_ADD_NODE(sc->sysctlctx,
+ SYSCTL_CHILDREN(sc->ip_blocks_sysctlnode), OID_AUTO,
+ amdsmu_ip_blocks_names[i], CTLFLAG_RD, NULL, sysctl_descr);
+ if (sc->ip_block_sysctlnodes[i] == NULL) {
+ device_printf(dev,
+ "could not add sysctl node for \"%s\"\n", sysctl_descr);
+ continue;
+ }
+ /*
+ * Create sysctls for if the IP block is currently active, last
+ * active time, and total active time.
+ */
+ SYSCTL_ADD_BOOL(sc->sysctlctx,
+ SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO,
+ "active", CTLFLAG_RD, &sc->ip_blocks_active[i], 0,
+ "IP block is currently active");
+ SYSCTL_ADD_U64(sc->sysctlctx,
+ SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO,
+ "last_time", CTLFLAG_RD, &m->ip_block_last_active_time[i],
+ 0, "How long the IP block was active for during the last"
+ "sleep (us)");
+#ifdef IP_BLOCK_TOTAL_ACTIVE_TIME
+ SYSCTL_ADD_U64(sc->sysctlctx,
+ SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO,
+ "total_time", CTLFLAG_RD, &m->ip_block_total_active_time[i],
+ 0, "How long the IP block was active for during sleep in"
+ "total (us)");
+#endif
+ }
+}
+
+static void
+amdsmu_init_metrics(device_t dev)
+{
+ struct amdsmu_softc *sc = device_get_softc(dev);
+ uint32_t metrics_addr_lo, metrics_addr_hi;
+ uint64_t metrics_addr;
+
+ sc->has_metrics = false;
+
+ /* Get physical address of logging buffer. */
+ if (amdsmu_cmd(dev, SMU_MSG_LOG_GETDRAM_ADDR_LO, 0, &metrics_addr_lo)
+ != 0)
+ return;
+ if (amdsmu_cmd(dev, SMU_MSG_LOG_GETDRAM_ADDR_HI, 0, &metrics_addr_hi)
+ != 0)
+ return;
+ metrics_addr = ((uint64_t) metrics_addr_hi << 32) | metrics_addr_lo;
+
+ /* Map memory of logging buffer. */
+ if (bus_space_map(sc->bus_tag, metrics_addr,
+ sizeof(struct amdsmu_metrics), 0, &sc->metrics_space) != 0) {
+ device_printf(dev, "could not map bus space for SMU metrics\n");
+ return;
+ }
+
+ /* Start logging for metrics. */
+ amdsmu_cmd(dev, SMU_MSG_LOG_RESET, 0, NULL);
+ amdsmu_cmd(dev, SMU_MSG_LOG_START, 0, NULL);
+
+ sc->has_metrics = true;
+}
+
+static void
+amdsmu_dump_metrics(device_t dev)
+{
+ struct amdsmu_softc *sc = device_get_softc(dev);
+ struct sysctl_oid *node;
+
+ if (!sc->has_metrics) {
+ device_printf(dev, "can't dump metrics\n");
+ return;
+ }
+ if (amdsmu_cmd(dev, SMU_MSG_LOG_DUMP_DATA, 0, NULL) != 0) {
+ device_printf(dev, "failed to dump metrics\n");
+ return;
+ }
+ bus_space_read_region_4(sc->bus_tag, sc->metrics_space, 0,
+ (uint32_t *)&sc->metrics, sizeof(sc->metrics) / sizeof(uint32_t));
+
+ /* Add sysctl nodes for metrics. */
+ if (sc->added_metrics_sysctl)
+ return;
+ sc->added_metrics_sysctl = true;
+
+ node = SYSCTL_ADD_NODE(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode),
+ OID_AUTO, "metrics", CTLFLAG_RD, NULL, "SMU metrics");
+ if (node == NULL) {
+ device_printf(dev, "could not add sysctl node for metrics\n");
+ return;
+ }
+
+ SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "table_version", CTLFLAG_RD, &sc->metrics.table_version, 0,
+ "SMU metrics table version");
+ SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "hint_count", CTLFLAG_RD, &sc->metrics.hint_count, 0,
+ "How many times the sleep hint was set");
+ SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "s0i3_last_entry_status", CTLFLAG_RD,
+ &sc->metrics.s0i3_last_entry_status, 0,
+ "1 if last S0i3 entry was successful");
+ SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "time_last_in_s0i2", CTLFLAG_RD, &sc->metrics.time_last_in_s0i2, 0,
+ "Time spent in S0i2 during last sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "time_last_entering_s0i3", CTLFLAG_RD,
+ &sc->metrics.time_last_entering_s0i3, 0,
+ "Time spent entering S0i3 during last sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "total_time_entering_s0i3", CTLFLAG_RD,
+ &sc->metrics.total_time_entering_s0i3, 0,
+ "Total time spent entering S0i3 (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "time_last_resuming", CTLFLAG_RD, &sc->metrics.time_last_resuming,
+ 0, "Time spent resuming from last sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "total_time_resuming", CTLFLAG_RD, &sc->metrics.total_time_resuming,
+ 0, "Total time spent resuming from sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "time_last_in_s0i3", CTLFLAG_RD, &sc->metrics.time_last_in_s0i3, 0,
+ "Time spent in S0i3 during last sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "total_time_in_s0i3", CTLFLAG_RD, &sc->metrics.total_time_in_s0i3,
+ 0, "Total time spent in S0i3 (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "time_last_in_sw_drips", CTLFLAG_RD, &sc->metrics.time_last_in_sw_drips,
+ 0, "Time spent in software DRIPS (SW DRIPS) during last sleep (us)");
+ SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO,
+ "total_time_in_sw_drips", CTLFLAG_RD, &sc->metrics.total_time_in_sw_drips,
+ 0, "Total time spent in software DRIPS (SW DRIPS) (us)");
}
static int
amdsmu_attach(device_t dev)
{
- struct amdsmu_softc *sc = device_get_softc(dev);
- uint32_t physbase_addr_lo, physbase_addr_hi;
- uint64_t physbase_addr;
- int rid = 0;
+ struct amdsmu_softc *sc = device_get_softc(dev);
+ uint32_t physbase_addr_lo, physbase_addr_hi;
+ uint64_t physbase_addr;
+ int rid = 0;
/*
* Find physical base address for SMU.
@@ -238,24 +504,38 @@
SMU_MEM_SIZE, 0, &sc->smu_space) != 0) {
device_printf(dev, "could not map bus space for SMU\n");
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);
+ sc->res = NULL;
return (ENXIO);
}
if (bus_space_map(sc->bus_tag, physbase_addr + SMU_REG_SPACE_OFF,
SMU_MEM_SIZE, 0, &sc->reg_space) != 0) {
device_printf(dev, "could not map bus space for SMU regs\n");
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);
+ sc->res = NULL;
return (ENXIO);
}
- amdsmu_print_vers(dev);
+ /* sysctl stuff. */
+ sc->sysctlctx = device_get_sysctl_ctx(dev);
+ sc->sysctlnode = device_get_sysctl_tree(dev);
+
+ amdsmu_get_vers(dev);
+
+ /* Get IP blocks. */
+ amdsmu_get_ip_blocks(dev);
+
+ /* Set up for getting metrics. */
+ amdsmu_init_metrics(dev);
+ amdsmu_dump_metrics(dev);
+
return (0);
}
static int
amdsmu_detach(device_t dev)
{
- struct amdsmu_softc *sc = device_get_softc(dev);
- int rid = 0;
+ struct amdsmu_softc *sc = device_get_softc(dev);
+ int rid = 0;
if (sc->res != NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 25, 12:26 PM (11 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24173245
Default Alt Text
D48714.id150102.diff (12 KB)

Event Timeline