jcs's openbsd hax
openbsd
at jcs 490 lines 12 kB view raw
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}