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