xref: /openbsd/sys/dev/acpi/intelpmc.c (revision 29eb6aec)
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