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