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