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