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
amdsmu_match(device_t dev,const struct amdsmu_product ** product_out)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
amdsmu_identify(driver_t * driver,device_t parent)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
amdsmu_probe(device_t dev)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
amdsmu_wait_res(device_t dev)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
amdsmu_cmd(device_t dev,enum amdsmu_msg msg,uint32_t arg,uint32_t * ret)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
amdsmu_get_vers(device_t dev)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
amdsmu_get_ip_blocks(device_t dev)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
amdsmu_init_metrics(device_t dev)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
amdsmu_dump_metrics(device_t dev)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
amdsmu_fetch_idlemask(device_t dev)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
amdsmu_suspend(device_t dev,enum power_stype stype)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
amdsmu_resume(device_t dev,enum power_stype stype)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
amdsmu_resume_check(device_t dev,enum power_stype stype)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
amdsmu_attach(device_t dev)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
amdsmu_detach(device_t dev)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