jcs's openbsd hax
openbsd
1/* $OpenBSD: amdpmc.c,v 1.3 2026/01/19 21:13:36 kettenis Exp $ */
2/*
3 * Copyright (c) 2025 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21
22#include <machine/intr.h>
23#include <machine/bus.h>
24
25#include <dev/acpi/acpireg.h>
26#include <dev/acpi/acpivar.h>
27#include <dev/acpi/acpidev.h>
28#include <dev/acpi/amltypes.h>
29#include <dev/acpi/dsdt.h>
30
31#include <dev/pci/pcidevs.h>
32
33#define SMN_ADDR 0x60
34#define SMN_PMC_BASE_ADDR_LO 0x13b102e8
35#define SMN_PMC_BASE_ADDR_HI 0x13b102ec
36#define SMN_PMC_BASE_ADDR_LO_MASK 0x0000ffff
37#define SMN_PMC_BASE_ADDR_HI_MASK 0xfff00000
38#define SMN_PMC_SIZE 0x1000
39#define SMN_PMC_BASE_ADDR_OFFSET 0x00010000
40#define SMN_DATA 0x64
41
42#define PMC_MSG 0x538
43#define PMC_MSG_1A 0x938
44#define PMC_RESP 0x980
45#define PMC_RESP_OK 0x01
46#define PMC_RESP_BUSY 0xfc
47#define PMC_ARG 0x9bc
48
49#define SMU_MSG_GETVERSION 0x02
50#define SMU_MSG_OS_HINT 0x03
51#define SMU_MSG_LOG_GETDRAM_ADDR_HI 0x04
52#define SMU_MSG_LOG_GETDRAM_ADDR_LO 0x05
53#define SMU_MSG_LOG_START 0x06
54#define SMU_MSG_LOG_RESET 0x07
55#define SMU_MSG_LOG_DUMP_DATA 0x08
56#define SMU_MSG_GET_SUP_CONSTRAINTS 0x09
57
58struct smu_metrics {
59 uint32_t table_version;
60 uint32_t hint_count;
61 uint32_t s0i3_last_entry_status;
62 uint32_t timein_s0i2;
63 uint64_t timeentering_s0i3_lastcapture;
64 uint64_t timeentering_s0i3_totaltime;
65 uint64_t timeto_resume_to_os_lastcapture;
66 uint64_t timeto_resume_to_os_totaltime;
67 uint64_t timein_s0i3_lastcapture;
68 uint64_t timein_s0i3_totaltime;
69 uint64_t timein_swdrips_lastcapture;
70 uint64_t timein_swdrips_totaltime;
71 uint64_t timecondition_notmet_lastcapture[32];
72 uint64_t timecondition_notmet_totaltime[32];
73};
74
75const char *smu_blocks[] = {
76 "DISPLAY",
77 "CPU",
78 "GFX",
79 "VDD",
80 "ACP",
81 "VCN",
82 "ISP",
83 "NBIO",
84 "DF",
85 "USB3_0",
86 "USB3_1",
87 "LAPIC",
88 "USB3_2",
89 "USB3_3",
90 "USB3_4",
91 "USB4_0",
92 "USB4_1",
93 "MPM",
94 "JPEG",
95 "IPU",
96 "UMSCH",
97 "VPE",
98};
99
100const char *smu_blocks_1a_7x[] = {
101 "DISPLAY",
102 "CPU",
103 "GFX",
104 "VDD",
105 "VDD_CTX",
106 "ACP",
107 "VCN_0",
108 "VCN_1",
109 "ISP",
110 "NBIO",
111 "DF",
112 "USB3_0",
113 "USB3_1",
114 "LAPIC",
115 "USB3_2",
116 "USB4_RT0",
117 "USB4_RT1",
118 "USB4_0",
119 "USB4_1",
120 "MPM",
121 "JPEG_0",
122 "JPEG_1",
123 "IPU",
124 "UMSCH",
125 "VPE",
126};
127
128/* Low Power S0 Idle DSM methods */
129#define ACPI_LPS0_ENUM_FUNCTIONS 0
130#define ACPI_LPS0_GET_CONSTRAINTS 1
131#define ACPI_LPS0_ENTRY 2
132#define ACPI_LPS0_EXIT 3
133#define ACPI_LPS0_SCREEN_OFF 4
134#define ACPI_LPS0_SCREEN_ON 5
135
136struct amdpmc_softc {
137 struct device sc_dev;
138 bus_space_tag_t sc_iot;
139 bus_space_handle_t sc_ioh;
140
141 struct acpi_softc *sc_acpi;
142 struct aml_node *sc_node;
143 bus_addr_t sc_pmc_msg;
144 const char **sc_smu_blocks;
145 u_int sc_smu_nblocks;
146
147 struct smu_metrics *sc_metrics;
148 uint32_t sc_active_blocks;
149};
150
151int amdpmc_match(struct device *, void *, void *);
152void amdpmc_attach(struct device *, struct device *, void *);
153int amdpmc_activate(struct device *, int);
154
155const struct cfattach amdpmc_ca = {
156 sizeof (struct amdpmc_softc), amdpmc_match, amdpmc_attach,
157 NULL, amdpmc_activate
158};
159
160struct cfdriver amdpmc_cd = {
161 NULL, "amdpmc", DV_DULL
162};
163
164const char *amdpmc_hids[] = {
165 "AMDI0005",
166 "AMDI0006",
167 "AMDI0007",
168 "AMDI0008",
169 "AMDI0009",
170 "AMDI000A",
171 "AMDI000B",
172 NULL
173};
174
175void amdpmc_suspend(void *);
176void amdpmc_resume(void *);
177
178void amdpmc_smu_log_print(struct amdpmc_softc *);
179int amdpmc_send_msg(struct amdpmc_softc *, uint8_t, uint32_t, uint32_t *);
180
181int
182amdpmc_match(struct device *parent, void *match, void *aux)
183{
184 struct acpi_attach_args *aaa = aux;
185 struct cfdata *cf = match;
186
187 return acpi_matchhids(aaa, amdpmc_hids, cf->cf_driver->cd_name);
188}
189
190void
191amdpmc_attach(struct device *parent, struct device *self, void *aux)
192{
193 struct amdpmc_softc *sc = (struct amdpmc_softc *)self;
194 struct acpi_attach_args *aaa = aux;
195 pci_chipset_tag_t pc;
196 pcitag_t tag;
197 pcireg_t reg;
198 bus_space_handle_t ioh;
199 uint32_t addr_lo, addr_hi;
200 bus_addr_t addr;
201 uint32_t version;
202 int error;
203
204 sc->sc_acpi = (struct acpi_softc *)parent;
205 sc->sc_node = aaa->aaa_node;
206
207 printf(": %s\n", aaa->aaa_node->name);
208
209 sc->sc_acpi->sc_pmc_suspend = amdpmc_suspend;
210 sc->sc_acpi->sc_pmc_resume = amdpmc_resume;
211 sc->sc_acpi->sc_pmc_cookie = sc;
212
213 pc = pci_lookup_segment(0, 0);
214 tag = pci_make_tag(pc, 0, 0, 0);
215 reg = pci_conf_read(pc, tag, PCI_ID_REG);
216 KASSERT(PCI_VENDOR(reg) == PCI_VENDOR_AMD);
217
218 switch (PCI_PRODUCT(reg)) {
219 case PCI_PRODUCT_AMD_17_6X_RC: /* RN/CZN */
220 case PCI_PRODUCT_AMD_19_4X_RC: /* YC */
221 case PCI_PRODUCT_AMD_19_6X_RC: /* CB */
222 case PCI_PRODUCT_AMD_19_7X_RC: /* PS */
223 sc->sc_pmc_msg = PMC_MSG;
224 sc->sc_smu_blocks = smu_blocks;
225 sc->sc_smu_nblocks = nitems(smu_blocks);
226 break;
227 case PCI_PRODUCT_AMD_1A_2X_RC: /* Strix Point */
228 case PCI_PRODUCT_AMD_1A_6X_RC: /* Krackan Point */
229 sc->sc_pmc_msg = PMC_MSG_1A;
230 if (curcpu()->ci_model == 0x70) {
231 sc->sc_smu_blocks = smu_blocks_1a_7x;
232 sc->sc_smu_nblocks = nitems(smu_blocks_1a_7x);
233 } else {
234 sc->sc_smu_blocks = smu_blocks;
235 sc->sc_smu_nblocks = nitems(smu_blocks);
236 }
237 break;
238 case PCI_PRODUCT_AMD_17_1X_RC: /* RV/PCO */
239 case PCI_PRODUCT_AMD_19_1X_RC: /* SP */
240 default:
241 /* Unsupported */
242 return;
243 }
244
245 pci_conf_write(pc, tag, SMN_ADDR, SMN_PMC_BASE_ADDR_LO);
246 addr_lo = pci_conf_read(pc, tag, SMN_DATA);
247 pci_conf_write(pc, tag, SMN_ADDR, SMN_PMC_BASE_ADDR_HI);
248 addr_hi = pci_conf_read(pc, tag, SMN_DATA);
249 if (addr_lo == 0xffffffff || addr_hi == 0xffffffff)
250 return;
251
252 addr_lo &= SMN_PMC_BASE_ADDR_HI_MASK;
253 addr_hi &= SMN_PMC_BASE_ADDR_LO_MASK;
254 addr = (uint64_t)addr_hi << 32 | addr_lo;
255
256 sc->sc_iot = aaa->aaa_memt;
257 if (bus_space_map(sc->sc_iot, addr + SMN_PMC_BASE_ADDR_OFFSET,
258 SMN_PMC_SIZE, 0, &sc->sc_ioh)) {
259 printf("%s: can't map SMU registers\n", sc->sc_dev.dv_xname);
260 return;
261 }
262
263 error = amdpmc_send_msg(sc, SMU_MSG_GETVERSION, 0, &version);
264 if (error) {
265 printf("%s: can't read SMU version\n", sc->sc_dev.dv_xname);
266 return;
267 }
268
269 printf("%s: SMU program %u version %u.%u.%u\n", sc->sc_dev.dv_xname,
270 (version >> 24) & 0xff, (version >> 16) & 0xff,
271 (version >> 8) & 0xff, (version >> 0) & 0xff);
272
273 error = amdpmc_send_msg(sc, SMU_MSG_GET_SUP_CONSTRAINTS, 0,
274 &sc->sc_active_blocks);
275 if (error) {
276 printf("%s: can't read active blocks\n", sc->sc_dev.dv_xname);
277 return;
278 }
279
280 error = amdpmc_send_msg(sc, SMU_MSG_LOG_GETDRAM_ADDR_LO, 0, &addr_lo);
281 if (error) {
282 printf("%s: can't read SMU DRAM address (%d)\n",
283 sc->sc_dev.dv_xname, error);
284 return;
285 }
286 error = amdpmc_send_msg(sc, SMU_MSG_LOG_GETDRAM_ADDR_HI, 0, &addr_hi);
287 if (error) {
288 printf("%s: can't read SMU DRAM address (%d)\n",
289 sc->sc_dev.dv_xname, error);
290 return;
291 }
292
293 addr = (uint64_t)addr_hi << 32 | addr_lo;
294 if (bus_space_map(sc->sc_iot, addr, sizeof(struct smu_metrics),
295 0, &ioh)) {
296 printf("%s: can't map SMU metrics\n", sc->sc_dev.dv_xname);
297 return;
298 }
299
300 sc->sc_metrics = bus_space_vaddr(sc->sc_iot, ioh);
301 memset(sc->sc_metrics, 0, sizeof(struct smu_metrics));
302 error = amdpmc_send_msg(sc, SMU_MSG_LOG_RESET, 0, NULL);
303 if (error) {
304 printf("%s: can't reset SMU message log (%d)\n",
305 sc->sc_dev.dv_xname, error);
306 return;
307 }
308 error = amdpmc_send_msg(sc, SMU_MSG_LOG_START, 0, NULL);
309 if (error) {
310 printf("%s: can't start SMU message log (%d)\n",
311 sc->sc_dev.dv_xname, error);
312 return;
313 }
314}
315
316void
317amdpmc_smu_log_dump(struct amdpmc_softc *sc)
318{
319 int error;
320
321 error = amdpmc_send_msg(sc, SMU_MSG_LOG_DUMP_DATA, 0, NULL);
322 if (error) {
323 printf("%s: can't dump SMU message log (%d)\n",
324 sc->sc_dev.dv_xname, error);
325 return;
326 }
327}
328
329void
330amdpmc_smu_log_print(struct amdpmc_softc *sc)
331{
332 const char *devname = sc->sc_dev.dv_xname;
333 int i;
334
335 printf("%s: SMU table_version %u\n", devname,
336 sc->sc_metrics->table_version);
337 printf("%s: SMU hint_count %u\n", devname,
338 sc->sc_metrics->hint_count);
339 printf("%s: SMU s0i3_last_entry_status %u\n", devname,
340 sc->sc_metrics->s0i3_last_entry_status);
341 printf("%s: SMU timeentering_s0i3_lastcapture %llu\n", devname,
342 sc->sc_metrics->timeentering_s0i3_lastcapture);
343 printf("%s: SMU timein_s0i3_lastcapture %llu\n", devname,
344 sc->sc_metrics->timein_s0i3_lastcapture);
345 printf("%s: SMU timeto_resume_to_os_lastcapture %llu\n", devname,
346 sc->sc_metrics->timeto_resume_to_os_lastcapture);
347 for (i = 0; i < sc->sc_smu_nblocks; i++) {
348 if ((sc->sc_active_blocks & (1U << i)) == 0)
349 continue;
350 if (sc->sc_metrics->timecondition_notmet_lastcapture[i] == 0)
351 continue;
352 printf("%s: SMU %s: %llu\n", devname, sc->sc_smu_blocks[i],
353 sc->sc_metrics->timecondition_notmet_lastcapture[i]);
354 }
355}
356
357int
358amdpmc_activate(struct device *self, int act)
359{
360 struct amdpmc_softc *sc = (struct amdpmc_softc *)self;
361
362 switch (act) {
363 case DVACT_SUSPEND:
364 break;
365 case DVACT_RESUME:
366 if (sc->sc_metrics)
367 amdpmc_smu_log_print(sc);
368 break;
369 }
370
371 return 0;
372}
373
374int
375amdpmc_dsm(struct acpi_softc *sc, struct aml_node *node, int func)
376{
377 struct aml_value cmd[4];
378 struct aml_value res;
379
380 /* e3f32452-febc-43ce-9039-932122d37721 */
381 static uint8_t lps0_dsm_guid[] = {
382 0x52, 0x24, 0xF3, 0xE3, 0xBC, 0xFE, 0xCE, 0x43,
383 0x90, 0x39, 0x93, 0x21, 0x22, 0xD3, 0x77, 0x21,
384 };
385
386 bzero(&cmd, sizeof(cmd));
387 cmd[0].type = AML_OBJTYPE_BUFFER;
388 cmd[0].v_buffer = (uint8_t *)&lps0_dsm_guid;
389 cmd[0].length = sizeof(lps0_dsm_guid);
390 /* rev */
391 cmd[1].type = AML_OBJTYPE_INTEGER;
392 cmd[1].v_integer = 0;
393 cmd[1].length = 1;
394 /* func */
395 cmd[2].type = AML_OBJTYPE_INTEGER;
396 cmd[2].v_integer = func;
397 cmd[2].length = 1;
398 /* not used */
399 cmd[3].type = AML_OBJTYPE_PACKAGE;
400 cmd[3].length = 0;
401
402 if (aml_evalname(sc, node, "_DSM", 4, cmd, &res)) {
403 printf("%s: eval of _DSM at %s failed\n",
404 sc->sc_dev.dv_xname, aml_nodename(node));
405 return 1;
406 }
407 aml_freevalue(&res);
408
409 return 0;
410}
411
412void
413amdpmc_suspend(void *cookie)
414{
415 struct amdpmc_softc *sc = cookie;
416
417 if (sc->sc_acpi->sc_state != ACPI_STATE_S0)
418 return;
419
420 if (sc->sc_metrics) {
421 memset(sc->sc_metrics, 0, sizeof(struct smu_metrics));
422 amdpmc_send_msg(sc, SMU_MSG_LOG_RESET, 0, NULL);
423 amdpmc_send_msg(sc, SMU_MSG_LOG_START, 0, NULL);
424 amdpmc_send_msg(sc, SMU_MSG_OS_HINT, 1, NULL);
425 }
426
427 amdpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_OFF);
428 amdpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_ENTRY);
429}
430
431void
432amdpmc_resume(void *cookie)
433{
434 struct amdpmc_softc *sc = cookie;
435
436 if (sc->sc_acpi->sc_state != ACPI_STATE_S0)
437 return;
438
439 amdpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_EXIT);
440 amdpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_ON);
441
442 if (sc->sc_metrics) {
443 amdpmc_send_msg(sc, SMU_MSG_OS_HINT, 0, NULL);
444 amdpmc_smu_log_dump(sc);
445 }
446}
447
448int
449amdpmc_send_msg(struct amdpmc_softc *sc, uint8_t msg, uint32_t arg,
450 uint32_t *data)
451{
452 uint32_t val;
453 int timo;
454
455 for (timo = 1000000; timo > 0; timo -= 50) {
456 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PMC_RESP);
457 if (val)
458 break;
459 delay(50);
460 }
461 if (timo == 0)
462 return ETIMEDOUT;
463
464 bus_space_write_4(sc->sc_iot, sc->sc_ioh, PMC_RESP, 0);
465 bus_space_write_4(sc->sc_iot, sc->sc_ioh, PMC_ARG, arg);
466 bus_space_write_4(sc->sc_iot, sc->sc_ioh, sc->sc_pmc_msg, msg);
467
468 for (timo = 1000000; timo > 0; timo -= 50) {
469 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PMC_RESP);
470 if (val)
471 break;
472 delay(50);
473 }
474 if (timo == 0)
475 return ETIMEDOUT;
476
477 switch (val) {
478 case PMC_RESP_OK:
479 if (data) {
480 delay(50);
481 *data = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
482 PMC_ARG);
483 }
484 return 0;
485 case PMC_RESP_BUSY:
486 return EBUSY;
487 default:
488 return EIO;
489 }
490}