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
intelpmc_match(struct device * parent,void * match,void * aux)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
intelpmc_attach(struct device * parent,struct device * self,void * aux)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
intelpmc_activate(struct device * self,int act)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
intelpmc_parse_lpit(struct intelpmc_softc * sc,struct acpi_lpit * lpit)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
intelpmc_dsm(struct acpi_softc * sc,struct aml_node * node,int func)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
intelpmc_suspend(void * cookie)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
intelpmc_resume(void * cookie)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