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