1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2025-2026 The FreeBSD Foundation 5 * 6 * This software was developed by Aymeric Wibo <obiwac@freebsd.org> 7 * under sponsorship from the FreeBSD Foundation. 8 */ 9 10 #include <sys/param.h> 11 #include <sys/bus.h> 12 #include <sys/kernel.h> 13 #include <sys/module.h> 14 #include <sys/rman.h> 15 #include <sys/sysctl.h> 16 17 #include "opt_acpi.h" 18 19 #if defined(DEV_ACPI) 20 #include <contrib/dev/acpica/include/acpi.h> 21 #include <dev/acpica/acpivar.h> 22 #endif 23 24 #include <dev/pci/pcivar.h> 25 #include <dev/amdsmu/amdsmu.h> 26 27 static bool 28 amdsmu_match(device_t dev, const struct amdsmu_product **product_out) 29 { 30 const uint16_t vendorid = pci_get_vendor(dev); 31 const uint16_t deviceid = pci_get_device(dev); 32 33 for (size_t i = 0; i < nitems(amdsmu_products); i++) { 34 const struct amdsmu_product *prod = &amdsmu_products[i]; 35 36 if (vendorid == prod->amdsmu_vendorid && 37 deviceid == prod->amdsmu_deviceid) { 38 if (product_out != NULL) 39 *product_out = prod; 40 return (true); 41 } 42 } 43 return (false); 44 } 45 46 static void 47 amdsmu_identify(driver_t *driver, device_t parent) 48 { 49 if (device_find_child(parent, "amdsmu", -1) != NULL) 50 return; 51 52 if (amdsmu_match(parent, NULL)) { 53 if (device_add_child(parent, "amdsmu", -1) == NULL) 54 device_printf(parent, "add amdsmu child failed\n"); 55 } 56 } 57 58 static int 59 amdsmu_probe(device_t dev) 60 { 61 if (resource_disabled("amdsmu", 0)) 62 return (ENXIO); 63 if (!amdsmu_match(device_get_parent(dev), NULL)) 64 return (ENXIO); 65 device_set_descf(dev, "AMD System Management Unit"); 66 67 return (BUS_PROBE_GENERIC); 68 } 69 70 static enum amdsmu_res 71 amdsmu_wait_res(device_t dev) 72 { 73 struct amdsmu_softc *sc = device_get_softc(dev); 74 enum amdsmu_res res; 75 76 /* 77 * The SMU has a response ready for us when the response register is 78 * set. Otherwise, we must wait. 79 */ 80 for (size_t i = 0; i < SMU_RES_READ_MAX; i++) { 81 res = amdsmu_read4(sc, SMU_REG_RESPONSE); 82 if (res != SMU_RES_WAIT) 83 return (res); 84 pause_sbt("amdsmu", ustosbt(SMU_RES_READ_PERIOD_US), 0, 85 C_HARDCLOCK); 86 } 87 device_printf(dev, "timed out waiting for response from SMU\n"); 88 return (SMU_RES_WAIT); 89 } 90 91 static int 92 amdsmu_cmd(device_t dev, enum amdsmu_msg msg, uint32_t arg, uint32_t *ret) 93 { 94 struct amdsmu_softc *sc = device_get_softc(dev); 95 enum amdsmu_res res; 96 97 /* Wait for SMU to be ready. */ 98 if (amdsmu_wait_res(dev) == SMU_RES_WAIT) 99 return (ETIMEDOUT); 100 101 /* Clear previous response. */ 102 amdsmu_write4(sc, SMU_REG_RESPONSE, SMU_RES_WAIT); 103 104 /* Write out command to registers. */ 105 amdsmu_write4(sc, SMU_REG_MESSAGE, msg); 106 amdsmu_write4(sc, SMU_REG_ARGUMENT, arg); 107 108 /* Wait for SMU response and handle it. */ 109 res = amdsmu_wait_res(dev); 110 111 switch (res) { 112 case SMU_RES_WAIT: 113 return (ETIMEDOUT); 114 case SMU_RES_OK: 115 if (ret != NULL) 116 *ret = amdsmu_read4(sc, SMU_REG_ARGUMENT); 117 return (0); 118 case SMU_RES_REJECT_BUSY: 119 device_printf(dev, "SMU is busy\n"); 120 return (EBUSY); 121 case SMU_RES_REJECT_PREREQ: 122 case SMU_RES_UNKNOWN: 123 case SMU_RES_FAILED: 124 device_printf(dev, "SMU error: %02x\n", res); 125 return (EIO); 126 } 127 128 return (EINVAL); 129 } 130 131 static int 132 amdsmu_get_vers(device_t dev) 133 { 134 int err; 135 uint32_t smu_vers; 136 struct amdsmu_softc *sc = device_get_softc(dev); 137 138 err = amdsmu_cmd(dev, SMU_MSG_GETSMUVERSION, 0, &smu_vers); 139 if (err != 0) { 140 device_printf(dev, "failed to get SMU version\n"); 141 return (err); 142 } 143 sc->smu_program = (smu_vers >> 24) & 0xFF; 144 sc->smu_maj = (smu_vers >> 16) & 0xFF; 145 sc->smu_min = (smu_vers >> 8) & 0xFF; 146 sc->smu_rev = smu_vers & 0xFF; 147 device_printf(dev, "SMU version: %d.%d.%d (program %d)\n", 148 sc->smu_maj, sc->smu_min, sc->smu_rev, sc->smu_program); 149 150 return (0); 151 } 152 153 static int 154 amdsmu_get_ip_blocks(device_t dev) 155 { 156 struct amdsmu_softc *sc = device_get_softc(dev); 157 const uint16_t deviceid = pci_get_device(dev); 158 int err; 159 struct amdsmu_metrics *m = &sc->metrics; 160 bool active; 161 char sysctl_descr[32]; 162 163 /* Get IP block count. */ 164 switch (deviceid) { 165 case PCI_DEVICEID_AMD_REMBRANDT_ROOT: 166 sc->ip_block_count = 12; 167 break; 168 case PCI_DEVICEID_AMD_PHOENIX_ROOT: 169 sc->ip_block_count = 21; 170 break; 171 /* TODO How many IP blocks does Strix Point (and the others) have? */ 172 case PCI_DEVICEID_AMD_STRIX_POINT_ROOT: 173 default: 174 sc->ip_block_count = nitems(amdsmu_ip_blocks_names); 175 } 176 KASSERT(sc->ip_block_count <= nitems(amdsmu_ip_blocks_names), 177 ("too many IP blocks for array")); 178 179 /* Get and print out IP blocks. */ 180 err = amdsmu_cmd(dev, SMU_MSG_GET_SUP_CONSTRAINTS, 0, 181 &sc->active_ip_blocks); 182 if (err != 0) { 183 device_printf(dev, "failed to get IP blocks\n"); 184 return (err); 185 } 186 device_printf(dev, "Active IP blocks: "); 187 for (size_t i = 0; i < sc->ip_block_count; i++) { 188 active = (sc->active_ip_blocks & (1 << i)) != 0; 189 sc->ip_blocks_active[i] = active; 190 if (!active) 191 continue; 192 printf("%s%s", amdsmu_ip_blocks_names[i], 193 i + 1 < sc->ip_block_count ? " " : "\n"); 194 } 195 196 /* Create a sysctl node for IP blocks. */ 197 sc->ip_blocks_sysctlnode = SYSCTL_ADD_NODE(sc->sysctlctx, 198 SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, "ip_blocks", 199 CTLFLAG_RD, NULL, "SMU metrics"); 200 if (sc->ip_blocks_sysctlnode == NULL) { 201 device_printf(dev, "could not add sysctl node for IP blocks\n"); 202 return (ENOMEM); 203 } 204 205 /* Create a sysctl node for each IP block. */ 206 for (size_t i = 0; i < sc->ip_block_count; i++) { 207 /* Create the sysctl node itself for the IP block. */ 208 snprintf(sysctl_descr, sizeof sysctl_descr, 209 "Metrics about the %s AMD IP block", 210 amdsmu_ip_blocks_names[i]); 211 sc->ip_block_sysctlnodes[i] = SYSCTL_ADD_NODE(sc->sysctlctx, 212 SYSCTL_CHILDREN(sc->ip_blocks_sysctlnode), OID_AUTO, 213 amdsmu_ip_blocks_names[i], CTLFLAG_RD, NULL, sysctl_descr); 214 if (sc->ip_block_sysctlnodes[i] == NULL) { 215 device_printf(dev, 216 "could not add sysctl node for \"%s\"\n", sysctl_descr); 217 continue; 218 } 219 /* 220 * Create sysctls for if the IP block is currently active, last 221 * active time, and total active time. 222 */ 223 SYSCTL_ADD_BOOL(sc->sysctlctx, 224 SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO, 225 "active", CTLFLAG_RD, &sc->ip_blocks_active[i], 0, 226 "IP block is currently active"); 227 SYSCTL_ADD_U64(sc->sysctlctx, 228 SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO, 229 "last_time", CTLFLAG_RD, &m->ip_block_last_active_time[i], 230 0, "How long the IP block was active for during the last" 231 " sleep (us)"); 232 #ifdef IP_BLOCK_TOTAL_ACTIVE_TIME 233 SYSCTL_ADD_U64(sc->sysctlctx, 234 SYSCTL_CHILDREN(sc->ip_block_sysctlnodes[i]), OID_AUTO, 235 "total_time", CTLFLAG_RD, &m->ip_block_total_active_time[i], 236 0, "How long the IP block was active for during sleep in" 237 " total (us)"); 238 #endif 239 } 240 return (0); 241 } 242 243 static int 244 amdsmu_init_metrics(device_t dev) 245 { 246 struct amdsmu_softc *sc = device_get_softc(dev); 247 int err; 248 uint32_t metrics_addr_lo, metrics_addr_hi; 249 uint64_t metrics_addr; 250 251 /* Get physical address of logging buffer. */ 252 err = amdsmu_cmd(dev, SMU_MSG_LOG_GETDRAM_ADDR_LO, 0, &metrics_addr_lo); 253 if (err != 0) 254 return (err); 255 err = amdsmu_cmd(dev, SMU_MSG_LOG_GETDRAM_ADDR_HI, 0, &metrics_addr_hi); 256 if (err != 0) 257 return (err); 258 metrics_addr = ((uint64_t) metrics_addr_hi << 32) | metrics_addr_lo; 259 260 /* Map memory of logging buffer. */ 261 err = bus_space_map(sc->bus_tag, metrics_addr, 262 sizeof(struct amdsmu_metrics), 0, &sc->metrics_space); 263 if (err != 0) { 264 device_printf(dev, "could not map bus space for SMU metrics\n"); 265 return (err); 266 } 267 268 /* Start logging for metrics. */ 269 amdsmu_cmd(dev, SMU_MSG_LOG_RESET, 0, NULL); 270 amdsmu_cmd(dev, SMU_MSG_LOG_START, 0, NULL); 271 return (0); 272 } 273 274 static int 275 amdsmu_dump_metrics(device_t dev) 276 { 277 struct amdsmu_softc *sc = device_get_softc(dev); 278 int err; 279 280 err = amdsmu_cmd(dev, SMU_MSG_LOG_DUMP_DATA, 0, NULL); 281 if (err != 0) { 282 device_printf(dev, "failed to dump metrics\n"); 283 return (err); 284 } 285 bus_space_read_region_4(sc->bus_tag, sc->metrics_space, 0, 286 (uint32_t *)&sc->metrics, sizeof(sc->metrics) / sizeof(uint32_t)); 287 288 return (0); 289 } 290 291 static void 292 amdsmu_fetch_idlemask(device_t dev) 293 { 294 struct amdsmu_softc *sc = device_get_softc(dev); 295 296 sc->idlemask = amdsmu_read4(sc, SMU_REG_IDLEMASK); 297 } 298 299 static void 300 amdsmu_suspend(device_t dev, enum power_stype stype) 301 { 302 if (stype != POWER_STYPE_SUSPEND_TO_IDLE) 303 return; 304 if (amdsmu_cmd(dev, SMU_MSG_SLEEP_HINT, true, NULL) != 0) 305 device_printf(dev, "failed to hint to SMU to enter sleep"); 306 } 307 308 static void 309 amdsmu_resume(device_t dev, enum power_stype stype) 310 { 311 if (stype != POWER_STYPE_SUSPEND_TO_IDLE) 312 return; 313 if (amdsmu_cmd(dev, SMU_MSG_SLEEP_HINT, false, NULL) != 0) 314 device_printf(dev, "failed to hint to SMU to exit sleep"); 315 /* Update metrics after resume. */ 316 amdsmu_dump_metrics(dev); 317 amdsmu_fetch_idlemask(dev); 318 } 319 320 static int 321 amdsmu_attach(device_t dev) 322 { 323 struct amdsmu_softc *sc = device_get_softc(dev); 324 int err; 325 uint32_t physbase_addr_lo, physbase_addr_hi; 326 uint64_t physbase_addr; 327 int rid = 0; 328 struct sysctl_oid *node; 329 330 /* 331 * Find physical base address for SMU. 332 * XXX I am a little confused about the masks here. I'm just copying 333 * what Linux does in the amd-pmc driver to get the base address. 334 */ 335 pci_write_config(dev, SMU_INDEX_ADDRESS, SMU_PHYSBASE_ADDR_LO, 4); 336 physbase_addr_lo = pci_read_config(dev, SMU_INDEX_DATA, 4) & 0xFFF00000; 337 338 pci_write_config(dev, SMU_INDEX_ADDRESS, SMU_PHYSBASE_ADDR_HI, 4); 339 physbase_addr_hi = pci_read_config(dev, SMU_INDEX_DATA, 4) & 0x0000FFFF; 340 341 physbase_addr = (uint64_t)physbase_addr_hi << 32 | physbase_addr_lo; 342 343 /* Map memory for SMU and its registers. */ 344 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 345 if (sc->res == NULL) { 346 device_printf(dev, "could not allocate resource\n"); 347 return (ENXIO); 348 } 349 350 sc->bus_tag = rman_get_bustag(sc->res); 351 352 if (bus_space_map(sc->bus_tag, physbase_addr, 353 SMU_MEM_SIZE, 0, &sc->smu_space) != 0) { 354 device_printf(dev, "could not map bus space for SMU\n"); 355 err = ENXIO; 356 goto err_smu_space; 357 } 358 if (bus_space_map(sc->bus_tag, physbase_addr + SMU_REG_SPACE_OFF, 359 SMU_MEM_SIZE, 0, &sc->reg_space) != 0) { 360 device_printf(dev, "could not map bus space for SMU regs\n"); 361 err = ENXIO; 362 goto err_reg_space; 363 } 364 365 /* sysctl stuff. */ 366 sc->sysctlctx = device_get_sysctl_ctx(dev); 367 sc->sysctlnode = device_get_sysctl_tree(dev); 368 369 /* Get version & add sysctls. */ 370 if ((err = amdsmu_get_vers(dev)) != 0) 371 goto err_dump; 372 373 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 374 "program", CTLFLAG_RD, &sc->smu_program, 0, "SMU program number"); 375 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 376 "version_major", CTLFLAG_RD, &sc->smu_maj, 0, 377 "SMU firmware major version number"); 378 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 379 "version_minor", CTLFLAG_RD, &sc->smu_min, 0, 380 "SMU firmware minor version number"); 381 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 382 "version_revision", CTLFLAG_RD, &sc->smu_rev, 0, 383 "SMU firmware revision number"); 384 385 /* Set up for getting metrics & add sysctls. */ 386 if ((err = amdsmu_init_metrics(dev)) != 0) 387 goto err_dump; 388 if ((err = amdsmu_dump_metrics(dev)) != 0) 389 goto err_dump; 390 391 node = SYSCTL_ADD_NODE(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), 392 OID_AUTO, "metrics", CTLFLAG_RD, NULL, "SMU metrics"); 393 if (node == NULL) { 394 device_printf(dev, "could not add sysctl node for metrics\n"); 395 err = ENOMEM; 396 goto err_dump; 397 } 398 399 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 400 "table_version", CTLFLAG_RD, &sc->metrics.table_version, 0, 401 "SMU metrics table version"); 402 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 403 "hint_count", CTLFLAG_RD, &sc->metrics.hint_count, 0, 404 "How many times the sleep hint was set"); 405 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 406 "s0i3_last_entry_status", CTLFLAG_RD, 407 &sc->metrics.s0i3_last_entry_status, 0, 408 "1 if last S0i3 entry was successful"); 409 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 410 "time_last_in_s0i2", CTLFLAG_RD, &sc->metrics.time_last_in_s0i2, 0, 411 "Time spent in S0i2 during last sleep (us)"); 412 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 413 "time_last_entering_s0i3", CTLFLAG_RD, 414 &sc->metrics.time_last_entering_s0i3, 0, 415 "Time spent entering S0i3 during last sleep (us)"); 416 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 417 "total_time_entering_s0i3", CTLFLAG_RD, 418 &sc->metrics.total_time_entering_s0i3, 0, 419 "Total time spent entering S0i3 (us)"); 420 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 421 "time_last_resuming", CTLFLAG_RD, &sc->metrics.time_last_resuming, 422 0, "Time spent resuming from last sleep (us)"); 423 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 424 "total_time_resuming", CTLFLAG_RD, &sc->metrics.total_time_resuming, 425 0, "Total time spent resuming from sleep (us)"); 426 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 427 "time_last_in_s0i3", CTLFLAG_RD, &sc->metrics.time_last_in_s0i3, 0, 428 "Time spent in S0i3 during last sleep (us)"); 429 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 430 "total_time_in_s0i3", CTLFLAG_RD, &sc->metrics.total_time_in_s0i3, 431 0, "Total time spent in S0i3 (us)"); 432 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 433 "time_last_in_sw_drips", CTLFLAG_RD, 434 &sc->metrics.time_last_in_sw_drips, 0, 435 "Time spent in awake during last sleep (us)"); 436 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 437 "total_time_in_sw_drips", CTLFLAG_RD, 438 &sc->metrics.total_time_in_sw_drips, 0, 439 "Total time spent awake (us)"); 440 441 /* Get IP blocks & add sysctls. */ 442 err = amdsmu_get_ip_blocks(dev); 443 if (err != 0) 444 goto err_dump; 445 446 /* Get idlemask & add sysctl. */ 447 amdsmu_fetch_idlemask(dev); 448 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 449 "idlemask", CTLFLAG_RD, &sc->idlemask, 0, "SMU idlemask. This " 450 "value is not documented - only used to help AMD internally debug " 451 "issues"); 452 453 #if defined(DEV_ACPI) 454 /* 455 * Register post device suspend/pre device resume eventhandlers. We use 456 * a lower priority for the suspend event as we want this to be called 457 * after the SPMC suspend hook, and a higher priority for the resume 458 * event as we want this to be called before the SPMC hook. 459 */ 460 sc->eh_suspend = EVENTHANDLER_REGISTER(acpi_post_dev_suspend, 461 amdsmu_suspend, dev, -10); 462 sc->eh_resume = EVENTHANDLER_REGISTER(acpi_pre_dev_resume, 463 amdsmu_resume, dev, 10); 464 #endif 465 466 return (0); 467 err_dump: 468 bus_space_unmap(sc->bus_tag, sc->reg_space, SMU_MEM_SIZE); 469 err_reg_space: 470 bus_space_unmap(sc->bus_tag, sc->smu_space, SMU_MEM_SIZE); 471 err_smu_space: 472 bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); 473 return (err); 474 } 475 476 static int 477 amdsmu_detach(device_t dev) 478 { 479 struct amdsmu_softc *sc = device_get_softc(dev); 480 int rid = 0; 481 482 #if defined(DEV_ACPI) 483 EVENTHANDLER_DEREGISTER(acpi_post_dev_suspend, sc->eh_suspend); 484 EVENTHANDLER_DEREGISTER(acpi_pre_dev_resume, sc->eh_resume); 485 #endif 486 487 bus_space_unmap(sc->bus_tag, sc->smu_space, SMU_MEM_SIZE); 488 bus_space_unmap(sc->bus_tag, sc->reg_space, SMU_MEM_SIZE); 489 490 bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); 491 return (0); 492 } 493 494 static device_method_t amdsmu_methods[] = { 495 DEVMETHOD(device_identify, amdsmu_identify), 496 DEVMETHOD(device_probe, amdsmu_probe), 497 DEVMETHOD(device_attach, amdsmu_attach), 498 DEVMETHOD(device_detach, amdsmu_detach), 499 DEVMETHOD_END 500 }; 501 502 static driver_t amdsmu_driver = { 503 "amdsmu", 504 amdsmu_methods, 505 sizeof(struct amdsmu_softc), 506 }; 507 508 DRIVER_MODULE(amdsmu, hostb, amdsmu_driver, NULL, NULL); 509 MODULE_VERSION(amdsmu, 1); 510 MODULE_DEPEND(amdsmu, amdsmn, 1, 1, 1); 511 MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdsmu, amdsmu_products, 512 nitems(amdsmu_products)); 513