xref: /freebsd/sys/dev/amdsmu/amdsmu.c (revision c4fde87a3236b500246d4fce76dbca2a3d64f73a)
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