1*29eb6aecSkettenis /* $OpenBSD: intelpmc.c,v 1.3 2024/08/08 18:46:13 kettenis Exp $ */ 26635b7e6Skettenis /* 36635b7e6Skettenis * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org> 46635b7e6Skettenis * 56635b7e6Skettenis * Permission to use, copy, modify, and distribute this software for any 66635b7e6Skettenis * purpose with or without fee is hereby granted, provided that the above 76635b7e6Skettenis * copyright notice and this permission notice appear in all copies. 86635b7e6Skettenis * 96635b7e6Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 106635b7e6Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 116635b7e6Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 126635b7e6Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 136635b7e6Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 146635b7e6Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 156635b7e6Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 166635b7e6Skettenis */ 176635b7e6Skettenis 186635b7e6Skettenis #include <sys/param.h> 196635b7e6Skettenis #include <sys/systm.h> 206635b7e6Skettenis #include <sys/device.h> 216635b7e6Skettenis 226635b7e6Skettenis #include <machine/intr.h> 236635b7e6Skettenis #include <machine/bus.h> 246635b7e6Skettenis 256635b7e6Skettenis #include <dev/acpi/acpireg.h> 266635b7e6Skettenis #include <dev/acpi/acpivar.h> 276635b7e6Skettenis #include <dev/acpi/acpidev.h> 286635b7e6Skettenis #include <dev/acpi/amltypes.h> 296635b7e6Skettenis #include <dev/acpi/dsdt.h> 306635b7e6Skettenis 316635b7e6Skettenis #define INTELPMC_DEBUG 326635b7e6Skettenis 336635b7e6Skettenis /* Low Power S0 Idle DSM methods */ 346635b7e6Skettenis #define ACPI_LPS0_ENUM_FUNCTIONS 0 356635b7e6Skettenis #define ACPI_LPS0_GET_CONSTRAINTS 1 366635b7e6Skettenis #define ACPI_LPS0_SCREEN_OFF 3 376635b7e6Skettenis #define ACPI_LPS0_SCREEN_ON 4 386635b7e6Skettenis #define ACPI_LPS0_ENTRY 5 396635b7e6Skettenis #define ACPI_LPS0_EXIT 6 406635b7e6Skettenis 416635b7e6Skettenis struct intelpmc_softc { 426635b7e6Skettenis struct device sc_dev; 436635b7e6Skettenis bus_space_tag_t sc_iot; 446635b7e6Skettenis bus_space_handle_t sc_ioh; 456635b7e6Skettenis 466635b7e6Skettenis struct acpi_softc *sc_acpi; 476635b7e6Skettenis struct aml_node *sc_node; 486635b7e6Skettenis 4926571a80Skettenis struct acpi_gas sc_counter[4]; 5026571a80Skettenis int sc_num_counters; 5126571a80Skettenis 526635b7e6Skettenis #ifdef INTELPMC_DEBUG 536635b7e6Skettenis uint64_t sc_c3[2]; 546635b7e6Skettenis uint64_t sc_c6[2]; 556635b7e6Skettenis uint64_t sc_c7[2]; 566635b7e6Skettenis uint64_t sc_pc2[2]; 576635b7e6Skettenis uint64_t sc_pc3[2]; 586635b7e6Skettenis uint64_t sc_pc6[2]; 596635b7e6Skettenis uint64_t sc_pc7[2]; 606635b7e6Skettenis uint64_t sc_pc8[2]; 616635b7e6Skettenis uint64_t sc_pc9[2]; 626635b7e6Skettenis uint64_t sc_pc10[2]; 6326571a80Skettenis uint64_t sc_lpit[4][2]; 646635b7e6Skettenis #endif 656635b7e6Skettenis }; 666635b7e6Skettenis 676635b7e6Skettenis int intelpmc_match(struct device *, void *, void *); 686635b7e6Skettenis void intelpmc_attach(struct device *, struct device *, void *); 696635b7e6Skettenis int intelpmc_activate(struct device *, int); 706635b7e6Skettenis 716635b7e6Skettenis const struct cfattach intelpmc_ca = { 726635b7e6Skettenis sizeof (struct intelpmc_softc), intelpmc_match, intelpmc_attach, 736635b7e6Skettenis NULL, intelpmc_activate 746635b7e6Skettenis }; 756635b7e6Skettenis 766635b7e6Skettenis struct cfdriver intelpmc_cd = { 776635b7e6Skettenis NULL, "intelpmc", DV_DULL 786635b7e6Skettenis }; 796635b7e6Skettenis 806635b7e6Skettenis const char *intelpmc_hids[] = { 816635b7e6Skettenis "INT33A1", 826635b7e6Skettenis NULL 836635b7e6Skettenis }; 846635b7e6Skettenis 8526571a80Skettenis void intelpmc_parse_lpit(struct intelpmc_softc *, struct acpi_lpit *); 866635b7e6Skettenis void intelpmc_suspend(void *); 876635b7e6Skettenis void intelpmc_resume(void *); 886635b7e6Skettenis 896635b7e6Skettenis int 906635b7e6Skettenis intelpmc_match(struct device *parent, void *match, void *aux) 916635b7e6Skettenis { 926635b7e6Skettenis struct acpi_attach_args *aaa = aux; 936635b7e6Skettenis struct cfdata *cf = match; 946635b7e6Skettenis 956635b7e6Skettenis return acpi_matchhids(aaa, intelpmc_hids, cf->cf_driver->cd_name); 966635b7e6Skettenis } 976635b7e6Skettenis 986635b7e6Skettenis void 996635b7e6Skettenis intelpmc_attach(struct device *parent, struct device *self, void *aux) 1006635b7e6Skettenis { 1016635b7e6Skettenis struct intelpmc_softc *sc = (struct intelpmc_softc *)self; 1026635b7e6Skettenis struct acpi_attach_args *aaa = aux; 10326571a80Skettenis struct acpi_q *entry; 10426571a80Skettenis struct acpi_lpit *lpit = NULL; 1056635b7e6Skettenis 1066635b7e6Skettenis sc->sc_acpi = (struct acpi_softc *)parent; 1076635b7e6Skettenis sc->sc_node = aaa->aaa_node; 1086635b7e6Skettenis 1096635b7e6Skettenis printf(": %s\n", aaa->aaa_node->name); 1106635b7e6Skettenis 11126571a80Skettenis SIMPLEQ_FOREACH(entry, &sc->sc_acpi->sc_tables, q_next) { 11226571a80Skettenis if (memcmp(entry->q_table, LPIT_SIG, 11326571a80Skettenis sizeof(LPIT_SIG) - 1) == 0) { 11426571a80Skettenis lpit = entry->q_table; 11526571a80Skettenis break; 11626571a80Skettenis } 11726571a80Skettenis } 11826571a80Skettenis 11926571a80Skettenis if (lpit) 12026571a80Skettenis intelpmc_parse_lpit(sc, lpit); 12126571a80Skettenis 1226635b7e6Skettenis sc->sc_acpi->sc_pmc_suspend = intelpmc_suspend; 1236635b7e6Skettenis sc->sc_acpi->sc_pmc_resume = intelpmc_resume; 1246635b7e6Skettenis sc->sc_acpi->sc_pmc_cookie = sc; 1256635b7e6Skettenis } 1266635b7e6Skettenis 1276635b7e6Skettenis int 1286635b7e6Skettenis intelpmc_activate(struct device *self, int act) 1296635b7e6Skettenis { 1306635b7e6Skettenis #ifdef INTELPMC_DEBUG 1316635b7e6Skettenis struct intelpmc_softc *sc = (struct intelpmc_softc *)self; 13226571a80Skettenis int i; 1336635b7e6Skettenis 1346635b7e6Skettenis switch (act) { 1356635b7e6Skettenis case DVACT_RESUME: 1366635b7e6Skettenis printf("C3: %lld -> %lld\n", sc->sc_c3[0], sc->sc_c3[1]); 1376635b7e6Skettenis printf("C6: %lld -> %lld\n", sc->sc_c6[0], sc->sc_c6[1]); 1386635b7e6Skettenis printf("C7: %lld -> %lld\n", sc->sc_c7[0], sc->sc_c7[1]); 1396635b7e6Skettenis printf("PC2: %lld -> %lld\n", sc->sc_pc2[0], sc->sc_pc2[1]); 1406635b7e6Skettenis printf("PC3: %lld -> %lld\n", sc->sc_pc3[0], sc->sc_pc3[1]); 1416635b7e6Skettenis printf("PC6: %lld -> %lld\n", sc->sc_pc6[0], sc->sc_pc6[1]); 1426635b7e6Skettenis printf("PC7: %lld -> %lld\n", sc->sc_pc7[0], sc->sc_pc7[1]); 1436635b7e6Skettenis printf("PC8: %lld -> %lld\n", sc->sc_pc8[0], sc->sc_pc8[1]); 1446635b7e6Skettenis printf("PC9: %lld -> %lld\n", sc->sc_pc9[0], sc->sc_pc9[1]); 1456635b7e6Skettenis printf("PC10: %lld -> %lld\n", sc->sc_pc10[0], sc->sc_pc10[1]); 14626571a80Skettenis for (i = 0; i < sc->sc_num_counters; i++) { 14726571a80Skettenis printf("LPIT%d: %lld -> %lld\n", i, 14826571a80Skettenis sc->sc_lpit[i][0], sc->sc_lpit[i][1]); 14926571a80Skettenis } 1506635b7e6Skettenis break; 1516635b7e6Skettenis } 1526635b7e6Skettenis #endif 1536635b7e6Skettenis 1546635b7e6Skettenis return 0; 1556635b7e6Skettenis } 1566635b7e6Skettenis 15726571a80Skettenis void 15826571a80Skettenis intelpmc_parse_lpit(struct intelpmc_softc *sc, struct acpi_lpit *lpit) 15926571a80Skettenis { 16026571a80Skettenis caddr_t addr = (caddr_t)(lpit + 1); 16126571a80Skettenis 16226571a80Skettenis while (addr < (caddr_t)lpit + lpit->hdr.length) { 16326571a80Skettenis struct acpi_lpit_entry *entry = (struct acpi_lpit_entry *)addr; 16426571a80Skettenis uint32_t length = entry->length; 16526571a80Skettenis 16626571a80Skettenis if (length < 8) 16726571a80Skettenis return; 16826571a80Skettenis 16926571a80Skettenis if (addr + length > (caddr_t)lpit + lpit->hdr.length) 17026571a80Skettenis return; 17126571a80Skettenis 17226571a80Skettenis switch (entry->type) { 17326571a80Skettenis case 0: 17426571a80Skettenis if (length != sizeof(struct acpi_lpit_entry)) 17526571a80Skettenis return; 17626571a80Skettenis 17726571a80Skettenis if (entry->flags & LPIT_DISABLED) 17826571a80Skettenis break; 17926571a80Skettenis 18026571a80Skettenis #ifdef INTELPMC_DEBUG 18126571a80Skettenis printf("state %d: 0x%02x:%d:%d:0x%02x:0x%016llx\n", 18226571a80Skettenis entry->uid, entry->entry_trigger.address_space_id, 18326571a80Skettenis entry->entry_trigger.register_bit_width, 18426571a80Skettenis entry->entry_trigger.register_bit_offset, 18526571a80Skettenis entry->entry_trigger.access_size, 18626571a80Skettenis entry->entry_trigger.address); 18726571a80Skettenis #endif 18826571a80Skettenis 18926571a80Skettenis if (entry->flags & LPIT_COUNTER_NOT_AVAILABLE) 19026571a80Skettenis break; 19126571a80Skettenis 19226571a80Skettenis #ifdef INTELPMC_DEBUG 19326571a80Skettenis printf("counter: 0x%02x:%d:%d:0x%02x:0x%016llx\n", 19426571a80Skettenis entry->residency_counter.address_space_id, 19526571a80Skettenis entry->residency_counter.register_bit_width, 19626571a80Skettenis entry->residency_counter.register_bit_offset, 19726571a80Skettenis entry->residency_counter.access_size, 19826571a80Skettenis entry->residency_counter.address); 19926571a80Skettenis printf("frequency: %lld\n", 20026571a80Skettenis entry->residency_frequency); 20126571a80Skettenis #endif 20226571a80Skettenis 20326571a80Skettenis if (sc->sc_num_counters >= nitems(sc->sc_counter)) 20426571a80Skettenis break; 20526571a80Skettenis memcpy(&sc->sc_counter[sc->sc_num_counters++], 20626571a80Skettenis &entry->residency_counter, sizeof(struct acpi_gas)); 20726571a80Skettenis break; 20826571a80Skettenis } 20926571a80Skettenis 21026571a80Skettenis addr += length; 21126571a80Skettenis } 21226571a80Skettenis } 21326571a80Skettenis 2146635b7e6Skettenis int 2156635b7e6Skettenis intelpmc_dsm(struct acpi_softc *sc, struct aml_node *node, int func) 2166635b7e6Skettenis { 2176635b7e6Skettenis struct aml_value cmd[4]; 2186635b7e6Skettenis struct aml_value res; 2196635b7e6Skettenis 2206635b7e6Skettenis /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */ 2216635b7e6Skettenis static uint8_t lps0_dsm_guid[] = { 2226635b7e6Skettenis 0xA0, 0x40, 0xEB, 0xC4, 0xD2, 0x6C, 0xE2, 0x11, 2236635b7e6Skettenis 0xBC, 0xFD, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66, 2246635b7e6Skettenis }; 2256635b7e6Skettenis 2266635b7e6Skettenis bzero(&cmd, sizeof(cmd)); 2276635b7e6Skettenis cmd[0].type = AML_OBJTYPE_BUFFER; 2286635b7e6Skettenis cmd[0].v_buffer = (uint8_t *)&lps0_dsm_guid; 2296635b7e6Skettenis cmd[0].length = sizeof(lps0_dsm_guid); 2306635b7e6Skettenis /* rev */ 2316635b7e6Skettenis cmd[1].type = AML_OBJTYPE_INTEGER; 2326635b7e6Skettenis cmd[1].v_integer = 0; 2336635b7e6Skettenis cmd[1].length = 1; 2346635b7e6Skettenis /* func */ 2356635b7e6Skettenis cmd[2].type = AML_OBJTYPE_INTEGER; 2366635b7e6Skettenis cmd[2].v_integer = func; 2376635b7e6Skettenis cmd[2].length = 1; 2386635b7e6Skettenis /* not used */ 2396635b7e6Skettenis cmd[3].type = AML_OBJTYPE_PACKAGE; 2406635b7e6Skettenis cmd[3].length = 0; 2416635b7e6Skettenis 2426635b7e6Skettenis if (aml_evalname(sc, node, "_DSM", 4, cmd, &res)) { 2436635b7e6Skettenis printf("%s: eval of _DSM at %s failed\n", 2446635b7e6Skettenis sc->sc_dev.dv_xname, aml_nodename(node)); 2456635b7e6Skettenis return 1; 2466635b7e6Skettenis } 2476635b7e6Skettenis aml_freevalue(&res); 2486635b7e6Skettenis 2496635b7e6Skettenis return 0; 2506635b7e6Skettenis } 2516635b7e6Skettenis 2526635b7e6Skettenis void 2536635b7e6Skettenis intelpmc_suspend(void *cookie) 2546635b7e6Skettenis { 2556635b7e6Skettenis struct intelpmc_softc *sc = cookie; 25626571a80Skettenis #ifdef INTELPMC_DEBUG 25726571a80Skettenis int i; 25826571a80Skettenis #endif 2596635b7e6Skettenis 2606635b7e6Skettenis if (sc->sc_acpi->sc_state != ACPI_STATE_S0) 2616635b7e6Skettenis return; 2626635b7e6Skettenis 2636635b7e6Skettenis #ifdef INTELPMC_DEBUG 2646635b7e6Skettenis rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[0]); 2656635b7e6Skettenis rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[0]); 2666635b7e6Skettenis rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[0]); 2676635b7e6Skettenis rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[0]); 2686635b7e6Skettenis rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[0]); 2696635b7e6Skettenis rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[0]); 2706635b7e6Skettenis rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[0]); 2716635b7e6Skettenis rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[0]); 2726635b7e6Skettenis rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[0]); 2736635b7e6Skettenis rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[0]); 27426571a80Skettenis for (i = 0; i < sc->sc_num_counters; i++) { 27526571a80Skettenis if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED) 27626571a80Skettenis rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][0]); 27726571a80Skettenis else { 27826571a80Skettenis acpi_gasio(sc->sc_acpi, ACPI_IOREAD, 27926571a80Skettenis sc->sc_counter[i].address_space_id, 28026571a80Skettenis sc->sc_counter[i].address, 281*29eb6aecSkettenis (1 << (sc->sc_counter[i].access_size - 1)), 28226571a80Skettenis sc->sc_counter[i].register_bit_width / 8, 28326571a80Skettenis &sc->sc_lpit[i][0]); 28426571a80Skettenis } 28526571a80Skettenis } 2866635b7e6Skettenis #endif 2876635b7e6Skettenis 2886635b7e6Skettenis intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_OFF); 2896635b7e6Skettenis intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_ENTRY); 2906635b7e6Skettenis } 2916635b7e6Skettenis 2926635b7e6Skettenis void 2936635b7e6Skettenis intelpmc_resume(void *cookie) 2946635b7e6Skettenis { 2956635b7e6Skettenis struct intelpmc_softc *sc = cookie; 29626571a80Skettenis #ifdef INTELPMC_DEBUG 29726571a80Skettenis int i; 29826571a80Skettenis #endif 2996635b7e6Skettenis 3006635b7e6Skettenis if (sc->sc_acpi->sc_state != ACPI_STATE_S0) 3016635b7e6Skettenis return; 3026635b7e6Skettenis 3036635b7e6Skettenis intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_EXIT); 3046635b7e6Skettenis intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_ON); 3056635b7e6Skettenis 3066635b7e6Skettenis #ifdef INTELPMC_DEBUG 3076635b7e6Skettenis rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[1]); 3086635b7e6Skettenis rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[1]); 3096635b7e6Skettenis rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[1]); 3106635b7e6Skettenis rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[1]); 3116635b7e6Skettenis rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[1]); 3126635b7e6Skettenis rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[1]); 3136635b7e6Skettenis rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[1]); 3146635b7e6Skettenis rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[1]); 3156635b7e6Skettenis rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[1]); 3166635b7e6Skettenis rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[1]); 31726571a80Skettenis for (i = 0; i < sc->sc_num_counters; i++) { 31826571a80Skettenis if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED) 31926571a80Skettenis rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][1]); 32026571a80Skettenis else { 32126571a80Skettenis acpi_gasio(sc->sc_acpi, ACPI_IOREAD, 32226571a80Skettenis sc->sc_counter[i].address_space_id, 32326571a80Skettenis sc->sc_counter[i].address, 324*29eb6aecSkettenis (1 << (sc->sc_counter[i].access_size - 1)), 32526571a80Skettenis sc->sc_counter[i].register_bit_width / 8, 32626571a80Skettenis &sc->sc_lpit[i][1]); 32726571a80Skettenis } 32826571a80Skettenis } 3296635b7e6Skettenis #endif 3306635b7e6Skettenis } 331