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 device_printf(dev, 328 "spent %lu seconds in S0i3 during last suspend\n", 329 m->time_last_in_s0i3 / 1000000); 330 return; 331 } 332 333 device_printf(dev, 334 "failed to enter S0i3 during last suspend; " 335 "battery drain will be excessive\n"); 336 337 if (sc->product->amdsmu_deviceid != PCI_DEVICEID_AMD_PHOENIX_ROOT) { 338 device_printf(dev, "only limited diagnostic information " 339 "available for %x:%x; check dev.amdsmu.0 sysctl tree\n", 340 sc->product->amdsmu_vendorid, 341 sc->product->amdsmu_deviceid); 342 return; 343 } 344 345 for (size_t i = 0; i < sc->product->ip_block_count; i++) { 346 /* 347 * An IP block only truly blocked S0i3 entry if it was active 348 * for the entire time spent in "SWDRIPS". 349 */ 350 if (m->ip_block_last_active_time[i] < m->time_last_in_sw_drips) 351 continue; 352 /* Check if this IP block should be ignored. */ 353 d = NULL; 354 for (size_t j = 0; j < nitems(amdsmu_diagnostics); j++) { 355 if (strcmp(amdsmu_diagnostics[j].blocking_ip_block, 356 sc->product->ip_blocks_names[i]) != 0) 357 continue; 358 d = &amdsmu_diagnostics[j]; 359 break; 360 } 361 if (d != NULL && d->ignore) 362 continue; 363 364 if (!any_blocking) { 365 device_printf(dev, 366 "IP blocks that blocked S0i3 entry:\n"); 367 any_blocking = true; 368 } 369 device_printf(dev, " %s (active for %ju us)\n", 370 sc->product->ip_blocks_names[i], 371 (uintmax_t)m->ip_block_last_active_time[i]); 372 373 if (d == NULL) 374 continue; 375 if (d->expected_module != NULL && 376 devclass_find(d->expected_module) == NULL) 377 device_printf(dev, 378 " hint: load %s to allow this block to " 379 "suspend\n", d->expected_module); 380 if (d->extra != NULL) 381 device_printf(dev, " hint: %s\n", d->extra); 382 } 383 384 if (!any_blocking) 385 device_printf(dev, "no IP block info available\n"); 386 } 387 388 static int 389 amdsmu_attach(device_t dev) 390 { 391 struct amdsmu_softc *sc = device_get_softc(dev); 392 int err; 393 uint32_t physbase_addr_lo, physbase_addr_hi; 394 uint64_t physbase_addr; 395 int rid = 0; 396 struct sysctl_oid *node; 397 398 /* 399 * Find physical base address for SMU. 400 * XXX I am a little confused about the masks here. I'm just copying 401 * what Linux does in the amd-pmc driver to get the base address. 402 */ 403 pci_write_config(dev, SMU_INDEX_ADDRESS, SMU_PHYSBASE_ADDR_LO, 4); 404 physbase_addr_lo = pci_read_config(dev, SMU_INDEX_DATA, 4) & 0xFFF00000; 405 406 pci_write_config(dev, SMU_INDEX_ADDRESS, SMU_PHYSBASE_ADDR_HI, 4); 407 physbase_addr_hi = pci_read_config(dev, SMU_INDEX_DATA, 4) & 0x0000FFFF; 408 409 physbase_addr = (uint64_t)physbase_addr_hi << 32 | physbase_addr_lo; 410 411 /* Map memory for SMU and its registers. */ 412 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 413 if (sc->res == NULL) { 414 device_printf(dev, "could not allocate resource\n"); 415 return (ENXIO); 416 } 417 418 sc->bus_tag = rman_get_bustag(sc->res); 419 420 if (bus_space_map(sc->bus_tag, physbase_addr, 421 SMU_MEM_SIZE, 0, &sc->smu_space) != 0) { 422 device_printf(dev, "could not map bus space for SMU\n"); 423 err = ENXIO; 424 goto err_smu_space; 425 } 426 if (bus_space_map(sc->bus_tag, physbase_addr + SMU_REG_SPACE_OFF, 427 SMU_MEM_SIZE, 0, &sc->reg_space) != 0) { 428 device_printf(dev, "could not map bus space for SMU regs\n"); 429 err = ENXIO; 430 goto err_reg_space; 431 } 432 433 /* sysctl stuff. */ 434 sc->sysctlctx = device_get_sysctl_ctx(dev); 435 sc->sysctlnode = device_get_sysctl_tree(dev); 436 437 /* Get version & add sysctls. */ 438 if ((err = amdsmu_get_vers(dev)) != 0) 439 goto err_dump; 440 441 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 442 "program", CTLFLAG_RD, &sc->smu_program, 0, "SMU program number"); 443 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 444 "version_major", CTLFLAG_RD, &sc->smu_maj, 0, 445 "SMU firmware major version number"); 446 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 447 "version_minor", CTLFLAG_RD, &sc->smu_min, 0, 448 "SMU firmware minor version number"); 449 SYSCTL_ADD_U8(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 450 "version_revision", CTLFLAG_RD, &sc->smu_rev, 0, 451 "SMU firmware revision number"); 452 453 /* Set up for getting metrics & add sysctls. */ 454 if ((err = amdsmu_init_metrics(dev)) != 0) 455 goto err_dump; 456 if ((err = amdsmu_dump_metrics(dev)) != 0) 457 goto err_dump; 458 459 node = SYSCTL_ADD_NODE(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), 460 OID_AUTO, "metrics", CTLFLAG_RD, NULL, "SMU metrics"); 461 if (node == NULL) { 462 device_printf(dev, "could not add sysctl node for metrics\n"); 463 err = ENOMEM; 464 goto err_dump; 465 } 466 467 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 468 "table_version", CTLFLAG_RD, &sc->metrics.table_version, 0, 469 "SMU metrics table version"); 470 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 471 "hint_count", CTLFLAG_RD, &sc->metrics.hint_count, 0, 472 "How many times the sleep hint was set"); 473 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 474 "s0i3_last_entry_status", CTLFLAG_RD, 475 &sc->metrics.s0i3_last_entry_status, 0, 476 "1 if last S0i3 entry was successful"); 477 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 478 "time_last_in_s0i2", CTLFLAG_RD, &sc->metrics.time_last_in_s0i2, 0, 479 "Time spent in S0i2 during last sleep (us)"); 480 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 481 "time_last_entering_s0i3", CTLFLAG_RD, 482 &sc->metrics.time_last_entering_s0i3, 0, 483 "Time spent entering S0i3 during last sleep (us)"); 484 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 485 "total_time_entering_s0i3", CTLFLAG_RD, 486 &sc->metrics.total_time_entering_s0i3, 0, 487 "Total time spent entering S0i3 (us)"); 488 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 489 "time_last_resuming", CTLFLAG_RD, &sc->metrics.time_last_resuming, 490 0, "Time spent resuming from last sleep (us)"); 491 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 492 "total_time_resuming", CTLFLAG_RD, &sc->metrics.total_time_resuming, 493 0, "Total time spent resuming from sleep (us)"); 494 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 495 "time_last_in_s0i3", CTLFLAG_RD, &sc->metrics.time_last_in_s0i3, 0, 496 "Time spent in S0i3 during last sleep (us)"); 497 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 498 "total_time_in_s0i3", CTLFLAG_RD, &sc->metrics.total_time_in_s0i3, 499 0, "Total time spent in S0i3 (us)"); 500 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 501 "time_last_in_sw_drips", CTLFLAG_RD, 502 &sc->metrics.time_last_in_sw_drips, 0, 503 "Time spent in awake during last sleep (us)"); 504 SYSCTL_ADD_U64(sc->sysctlctx, SYSCTL_CHILDREN(node), OID_AUTO, 505 "total_time_in_sw_drips", CTLFLAG_RD, 506 &sc->metrics.total_time_in_sw_drips, 0, 507 "Total time spent awake (us)"); 508 509 /* Get IP blocks & add sysctls. */ 510 err = amdsmu_get_ip_blocks(dev); 511 if (err != 0) 512 goto err_dump; 513 514 /* Get idlemask & add sysctl. */ 515 amdsmu_fetch_idlemask(dev); 516 SYSCTL_ADD_U32(sc->sysctlctx, SYSCTL_CHILDREN(sc->sysctlnode), OID_AUTO, 517 "idlemask", CTLFLAG_RD, &sc->idlemask, 0, "SMU idlemask. This " 518 "value is not documented - only used to help AMD internally debug " 519 "issues"); 520 521 #if defined(DEV_ACPI) 522 /* 523 * Register post device suspend/pre device resume eventhandlers. We use 524 * a lower priority for the suspend event as we want this to be called 525 * after the SPMC suspend hook, and a higher priority for the resume 526 * event as we want this to be called before the SPMC hook. 527 */ 528 sc->eh_suspend = EVENTHANDLER_REGISTER(acpi_post_dev_suspend, 529 amdsmu_suspend, dev, EVENTHANDLER_PRI_LAST); 530 sc->eh_resume = EVENTHANDLER_REGISTER(acpi_pre_dev_resume, 531 amdsmu_resume, dev, EVENTHANDLER_PRI_FIRST); 532 sc->eh_resume_check = EVENTHANDLER_REGISTER(power_resume_check, 533 amdsmu_resume_check, dev, EVENTHANDLER_PRI_ANY); 534 #endif 535 536 return (0); 537 err_dump: 538 bus_space_unmap(sc->bus_tag, sc->reg_space, SMU_MEM_SIZE); 539 err_reg_space: 540 bus_space_unmap(sc->bus_tag, sc->smu_space, SMU_MEM_SIZE); 541 err_smu_space: 542 bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); 543 return (err); 544 } 545 546 static int 547 amdsmu_detach(device_t dev) 548 { 549 struct amdsmu_softc *sc = device_get_softc(dev); 550 int rid = 0; 551 552 #if defined(DEV_ACPI) 553 EVENTHANDLER_DEREGISTER(acpi_post_dev_suspend, sc->eh_suspend); 554 EVENTHANDLER_DEREGISTER(acpi_pre_dev_resume, sc->eh_resume); 555 EVENTHANDLER_DEREGISTER(power_resume_check, sc->eh_resume_check); 556 #endif 557 558 bus_space_unmap(sc->bus_tag, sc->smu_space, SMU_MEM_SIZE); 559 bus_space_unmap(sc->bus_tag, sc->reg_space, SMU_MEM_SIZE); 560 561 bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); 562 return (0); 563 } 564 565 static device_method_t amdsmu_methods[] = { 566 DEVMETHOD(device_identify, amdsmu_identify), 567 DEVMETHOD(device_probe, amdsmu_probe), 568 DEVMETHOD(device_attach, amdsmu_attach), 569 DEVMETHOD(device_detach, amdsmu_detach), 570 DEVMETHOD_END 571 }; 572 573 static driver_t amdsmu_driver = { 574 "amdsmu", 575 amdsmu_methods, 576 sizeof(struct amdsmu_softc), 577 }; 578 579 DRIVER_MODULE(amdsmu, hostb, amdsmu_driver, NULL, NULL); 580 MODULE_VERSION(amdsmu, 1); 581 MODULE_DEPEND(amdsmu, amdsmn, 1, 1, 1); 582 MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdsmu, amdsmu_products, 583 nitems(amdsmu_products)); 584