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