1 /* $OpenBSD: intelpmc.c,v 1.3 2024/08/08 18:46:13 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2024 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 #define INTELPMC_DEBUG 32 33 /* Low Power S0 Idle DSM methods */ 34 #define ACPI_LPS0_ENUM_FUNCTIONS 0 35 #define ACPI_LPS0_GET_CONSTRAINTS 1 36 #define ACPI_LPS0_SCREEN_OFF 3 37 #define ACPI_LPS0_SCREEN_ON 4 38 #define ACPI_LPS0_ENTRY 5 39 #define ACPI_LPS0_EXIT 6 40 41 struct intelpmc_softc { 42 struct device sc_dev; 43 bus_space_tag_t sc_iot; 44 bus_space_handle_t sc_ioh; 45 46 struct acpi_softc *sc_acpi; 47 struct aml_node *sc_node; 48 49 struct acpi_gas sc_counter[4]; 50 int sc_num_counters; 51 52 #ifdef INTELPMC_DEBUG 53 uint64_t sc_c3[2]; 54 uint64_t sc_c6[2]; 55 uint64_t sc_c7[2]; 56 uint64_t sc_pc2[2]; 57 uint64_t sc_pc3[2]; 58 uint64_t sc_pc6[2]; 59 uint64_t sc_pc7[2]; 60 uint64_t sc_pc8[2]; 61 uint64_t sc_pc9[2]; 62 uint64_t sc_pc10[2]; 63 uint64_t sc_lpit[4][2]; 64 #endif 65 }; 66 67 int intelpmc_match(struct device *, void *, void *); 68 void intelpmc_attach(struct device *, struct device *, void *); 69 int intelpmc_activate(struct device *, int); 70 71 const struct cfattach intelpmc_ca = { 72 sizeof (struct intelpmc_softc), intelpmc_match, intelpmc_attach, 73 NULL, intelpmc_activate 74 }; 75 76 struct cfdriver intelpmc_cd = { 77 NULL, "intelpmc", DV_DULL 78 }; 79 80 const char *intelpmc_hids[] = { 81 "INT33A1", 82 NULL 83 }; 84 85 void intelpmc_parse_lpit(struct intelpmc_softc *, struct acpi_lpit *); 86 void intelpmc_suspend(void *); 87 void intelpmc_resume(void *); 88 89 int 90 intelpmc_match(struct device *parent, void *match, void *aux) 91 { 92 struct acpi_attach_args *aaa = aux; 93 struct cfdata *cf = match; 94 95 return acpi_matchhids(aaa, intelpmc_hids, cf->cf_driver->cd_name); 96 } 97 98 void 99 intelpmc_attach(struct device *parent, struct device *self, void *aux) 100 { 101 struct intelpmc_softc *sc = (struct intelpmc_softc *)self; 102 struct acpi_attach_args *aaa = aux; 103 struct acpi_q *entry; 104 struct acpi_lpit *lpit = NULL; 105 106 sc->sc_acpi = (struct acpi_softc *)parent; 107 sc->sc_node = aaa->aaa_node; 108 109 printf(": %s\n", aaa->aaa_node->name); 110 111 SIMPLEQ_FOREACH(entry, &sc->sc_acpi->sc_tables, q_next) { 112 if (memcmp(entry->q_table, LPIT_SIG, 113 sizeof(LPIT_SIG) - 1) == 0) { 114 lpit = entry->q_table; 115 break; 116 } 117 } 118 119 if (lpit) 120 intelpmc_parse_lpit(sc, lpit); 121 122 sc->sc_acpi->sc_pmc_suspend = intelpmc_suspend; 123 sc->sc_acpi->sc_pmc_resume = intelpmc_resume; 124 sc->sc_acpi->sc_pmc_cookie = sc; 125 } 126 127 int 128 intelpmc_activate(struct device *self, int act) 129 { 130 #ifdef INTELPMC_DEBUG 131 struct intelpmc_softc *sc = (struct intelpmc_softc *)self; 132 int i; 133 134 switch (act) { 135 case DVACT_RESUME: 136 printf("C3: %lld -> %lld\n", sc->sc_c3[0], sc->sc_c3[1]); 137 printf("C6: %lld -> %lld\n", sc->sc_c6[0], sc->sc_c6[1]); 138 printf("C7: %lld -> %lld\n", sc->sc_c7[0], sc->sc_c7[1]); 139 printf("PC2: %lld -> %lld\n", sc->sc_pc2[0], sc->sc_pc2[1]); 140 printf("PC3: %lld -> %lld\n", sc->sc_pc3[0], sc->sc_pc3[1]); 141 printf("PC6: %lld -> %lld\n", sc->sc_pc6[0], sc->sc_pc6[1]); 142 printf("PC7: %lld -> %lld\n", sc->sc_pc7[0], sc->sc_pc7[1]); 143 printf("PC8: %lld -> %lld\n", sc->sc_pc8[0], sc->sc_pc8[1]); 144 printf("PC9: %lld -> %lld\n", sc->sc_pc9[0], sc->sc_pc9[1]); 145 printf("PC10: %lld -> %lld\n", sc->sc_pc10[0], sc->sc_pc10[1]); 146 for (i = 0; i < sc->sc_num_counters; i++) { 147 printf("LPIT%d: %lld -> %lld\n", i, 148 sc->sc_lpit[i][0], sc->sc_lpit[i][1]); 149 } 150 break; 151 } 152 #endif 153 154 return 0; 155 } 156 157 void 158 intelpmc_parse_lpit(struct intelpmc_softc *sc, struct acpi_lpit *lpit) 159 { 160 caddr_t addr = (caddr_t)(lpit + 1); 161 162 while (addr < (caddr_t)lpit + lpit->hdr.length) { 163 struct acpi_lpit_entry *entry = (struct acpi_lpit_entry *)addr; 164 uint32_t length = entry->length; 165 166 if (length < 8) 167 return; 168 169 if (addr + length > (caddr_t)lpit + lpit->hdr.length) 170 return; 171 172 switch (entry->type) { 173 case 0: 174 if (length != sizeof(struct acpi_lpit_entry)) 175 return; 176 177 if (entry->flags & LPIT_DISABLED) 178 break; 179 180 #ifdef INTELPMC_DEBUG 181 printf("state %d: 0x%02x:%d:%d:0x%02x:0x%016llx\n", 182 entry->uid, entry->entry_trigger.address_space_id, 183 entry->entry_trigger.register_bit_width, 184 entry->entry_trigger.register_bit_offset, 185 entry->entry_trigger.access_size, 186 entry->entry_trigger.address); 187 #endif 188 189 if (entry->flags & LPIT_COUNTER_NOT_AVAILABLE) 190 break; 191 192 #ifdef INTELPMC_DEBUG 193 printf("counter: 0x%02x:%d:%d:0x%02x:0x%016llx\n", 194 entry->residency_counter.address_space_id, 195 entry->residency_counter.register_bit_width, 196 entry->residency_counter.register_bit_offset, 197 entry->residency_counter.access_size, 198 entry->residency_counter.address); 199 printf("frequency: %lld\n", 200 entry->residency_frequency); 201 #endif 202 203 if (sc->sc_num_counters >= nitems(sc->sc_counter)) 204 break; 205 memcpy(&sc->sc_counter[sc->sc_num_counters++], 206 &entry->residency_counter, sizeof(struct acpi_gas)); 207 break; 208 } 209 210 addr += length; 211 } 212 } 213 214 int 215 intelpmc_dsm(struct acpi_softc *sc, struct aml_node *node, int func) 216 { 217 struct aml_value cmd[4]; 218 struct aml_value res; 219 220 /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */ 221 static uint8_t lps0_dsm_guid[] = { 222 0xA0, 0x40, 0xEB, 0xC4, 0xD2, 0x6C, 0xE2, 0x11, 223 0xBC, 0xFD, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66, 224 }; 225 226 bzero(&cmd, sizeof(cmd)); 227 cmd[0].type = AML_OBJTYPE_BUFFER; 228 cmd[0].v_buffer = (uint8_t *)&lps0_dsm_guid; 229 cmd[0].length = sizeof(lps0_dsm_guid); 230 /* rev */ 231 cmd[1].type = AML_OBJTYPE_INTEGER; 232 cmd[1].v_integer = 0; 233 cmd[1].length = 1; 234 /* func */ 235 cmd[2].type = AML_OBJTYPE_INTEGER; 236 cmd[2].v_integer = func; 237 cmd[2].length = 1; 238 /* not used */ 239 cmd[3].type = AML_OBJTYPE_PACKAGE; 240 cmd[3].length = 0; 241 242 if (aml_evalname(sc, node, "_DSM", 4, cmd, &res)) { 243 printf("%s: eval of _DSM at %s failed\n", 244 sc->sc_dev.dv_xname, aml_nodename(node)); 245 return 1; 246 } 247 aml_freevalue(&res); 248 249 return 0; 250 } 251 252 void 253 intelpmc_suspend(void *cookie) 254 { 255 struct intelpmc_softc *sc = cookie; 256 #ifdef INTELPMC_DEBUG 257 int i; 258 #endif 259 260 if (sc->sc_acpi->sc_state != ACPI_STATE_S0) 261 return; 262 263 #ifdef INTELPMC_DEBUG 264 rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[0]); 265 rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[0]); 266 rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[0]); 267 rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[0]); 268 rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[0]); 269 rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[0]); 270 rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[0]); 271 rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[0]); 272 rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[0]); 273 rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[0]); 274 for (i = 0; i < sc->sc_num_counters; i++) { 275 if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED) 276 rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][0]); 277 else { 278 acpi_gasio(sc->sc_acpi, ACPI_IOREAD, 279 sc->sc_counter[i].address_space_id, 280 sc->sc_counter[i].address, 281 (1 << (sc->sc_counter[i].access_size - 1)), 282 sc->sc_counter[i].register_bit_width / 8, 283 &sc->sc_lpit[i][0]); 284 } 285 } 286 #endif 287 288 intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_OFF); 289 intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_ENTRY); 290 } 291 292 void 293 intelpmc_resume(void *cookie) 294 { 295 struct intelpmc_softc *sc = cookie; 296 #ifdef INTELPMC_DEBUG 297 int i; 298 #endif 299 300 if (sc->sc_acpi->sc_state != ACPI_STATE_S0) 301 return; 302 303 intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_EXIT); 304 intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_ON); 305 306 #ifdef INTELPMC_DEBUG 307 rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[1]); 308 rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[1]); 309 rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[1]); 310 rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[1]); 311 rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[1]); 312 rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[1]); 313 rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[1]); 314 rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[1]); 315 rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[1]); 316 rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[1]); 317 for (i = 0; i < sc->sc_num_counters; i++) { 318 if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED) 319 rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][1]); 320 else { 321 acpi_gasio(sc->sc_acpi, ACPI_IOREAD, 322 sc->sc_counter[i].address_space_id, 323 sc->sc_counter[i].address, 324 (1 << (sc->sc_counter[i].access_size - 1)), 325 sc->sc_counter[i].register_bit_width / 8, 326 &sc->sc_lpit[i][1]); 327 } 328 } 329 #endif 330 } 331