Edit

IABSD.fr/src/sys/dev/acpi/intelpmc.c

Branch :

  • Show log

    Commit

  • Author : kettenis
    Date : 2024-08-08 18:46:13
    Hash : 29eb6aec
    Message : Fix gasio access size.

  • sys/dev/acpi/intelpmc.c
  • /*	$OpenBSD: intelpmc.c,v 1.3 2024/08/08 18:46:13 kettenis Exp $	*/
    /*
     * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/device.h>
    
    #include <machine/intr.h>
    #include <machine/bus.h>
    
    #include <dev/acpi/acpireg.h>
    #include <dev/acpi/acpivar.h>
    #include <dev/acpi/acpidev.h>
    #include <dev/acpi/amltypes.h>
    #include <dev/acpi/dsdt.h>
    
    #define INTELPMC_DEBUG
    
    /* Low Power S0 Idle DSM methods */
    #define ACPI_LPS0_ENUM_FUNCTIONS 	0
    #define ACPI_LPS0_GET_CONSTRAINTS	1
    #define ACPI_LPS0_SCREEN_OFF		3
    #define ACPI_LPS0_SCREEN_ON		4
    #define ACPI_LPS0_ENTRY			5
    #define ACPI_LPS0_EXIT			6
    
    struct intelpmc_softc {
    	struct device		sc_dev;
    	bus_space_tag_t		sc_iot;
    	bus_space_handle_t	sc_ioh;
    
    	struct acpi_softc	*sc_acpi;
    	struct aml_node		*sc_node;
    
    	struct acpi_gas		sc_counter[4];
    	int			sc_num_counters;
    
    #ifdef INTELPMC_DEBUG
    	uint64_t		sc_c3[2];
    	uint64_t		sc_c6[2];
    	uint64_t		sc_c7[2];
    	uint64_t		sc_pc2[2];
    	uint64_t		sc_pc3[2];
    	uint64_t		sc_pc6[2];
    	uint64_t		sc_pc7[2];
    	uint64_t		sc_pc8[2];
    	uint64_t		sc_pc9[2];
    	uint64_t		sc_pc10[2];
    	uint64_t		sc_lpit[4][2];
    #endif
    };
    
    int	intelpmc_match(struct device *, void *, void *);
    void	intelpmc_attach(struct device *, struct device *, void *);
    int	intelpmc_activate(struct device *, int);
    
    const struct cfattach intelpmc_ca = {
    	sizeof (struct intelpmc_softc), intelpmc_match, intelpmc_attach,
    	NULL, intelpmc_activate
    };
    
    struct cfdriver intelpmc_cd = {
    	NULL, "intelpmc", DV_DULL
    };
    
    const char *intelpmc_hids[] = {
    	"INT33A1",
    	NULL
    };
    
    void	intelpmc_parse_lpit(struct intelpmc_softc *, struct acpi_lpit *);
    void	intelpmc_suspend(void *);
    void	intelpmc_resume(void *);
    
    int
    intelpmc_match(struct device *parent, void *match, void *aux)
    {
    	struct acpi_attach_args *aaa = aux;
    	struct cfdata *cf = match;
    
    	return acpi_matchhids(aaa, intelpmc_hids, cf->cf_driver->cd_name);
    }
    
    void
    intelpmc_attach(struct device *parent, struct device *self, void *aux)
    {
    	struct intelpmc_softc *sc = (struct intelpmc_softc *)self;
    	struct acpi_attach_args *aaa = aux;
    	struct acpi_q *entry;
    	struct acpi_lpit *lpit = NULL;
    
    	sc->sc_acpi = (struct acpi_softc *)parent;
    	sc->sc_node = aaa->aaa_node;
    
    	printf(": %s\n", aaa->aaa_node->name);
    
    	SIMPLEQ_FOREACH(entry, &sc->sc_acpi->sc_tables, q_next) {
    		if (memcmp(entry->q_table, LPIT_SIG,
    		    sizeof(LPIT_SIG) - 1) == 0) {
    			lpit = entry->q_table;
    			break;
    		}
    	}
    
    	if (lpit)
    		intelpmc_parse_lpit(sc, lpit);
    
    	sc->sc_acpi->sc_pmc_suspend = intelpmc_suspend;
    	sc->sc_acpi->sc_pmc_resume = intelpmc_resume;
    	sc->sc_acpi->sc_pmc_cookie = sc;
    }
    
    int
    intelpmc_activate(struct device *self, int act)
    {
    #ifdef INTELPMC_DEBUG
    	struct intelpmc_softc *sc = (struct intelpmc_softc *)self;
    	int i;
    
    	switch (act) {
    	case DVACT_RESUME:
    		printf("C3: %lld -> %lld\n", sc->sc_c3[0], sc->sc_c3[1]);
    		printf("C6: %lld -> %lld\n", sc->sc_c6[0], sc->sc_c6[1]);
    		printf("C7: %lld -> %lld\n", sc->sc_c7[0], sc->sc_c7[1]);
    		printf("PC2: %lld -> %lld\n", sc->sc_pc2[0], sc->sc_pc2[1]);
    		printf("PC3: %lld -> %lld\n", sc->sc_pc3[0], sc->sc_pc3[1]);
    		printf("PC6: %lld -> %lld\n", sc->sc_pc6[0], sc->sc_pc6[1]);
    		printf("PC7: %lld -> %lld\n", sc->sc_pc7[0], sc->sc_pc7[1]);
    		printf("PC8: %lld -> %lld\n", sc->sc_pc8[0], sc->sc_pc8[1]);
    		printf("PC9: %lld -> %lld\n", sc->sc_pc9[0], sc->sc_pc9[1]);
    		printf("PC10: %lld -> %lld\n", sc->sc_pc10[0], sc->sc_pc10[1]);
    		for (i = 0; i < sc->sc_num_counters; i++) {
    			printf("LPIT%d: %lld -> %lld\n", i,
    			    sc->sc_lpit[i][0], sc->sc_lpit[i][1]);
    		}
    		break;
    	}
    #endif
    
    	return 0;
    }
    
    void
    intelpmc_parse_lpit(struct intelpmc_softc *sc, struct acpi_lpit *lpit)
    {
    	caddr_t addr = (caddr_t)(lpit + 1);
    
    	while (addr < (caddr_t)lpit + lpit->hdr.length) {
    		struct acpi_lpit_entry *entry = (struct acpi_lpit_entry *)addr;
    		uint32_t length = entry->length;
    
    		if (length < 8)
    			return;
    
    		if (addr + length > (caddr_t)lpit + lpit->hdr.length)
    			return;
    
    		switch (entry->type) {
    		case 0:
    			if (length != sizeof(struct acpi_lpit_entry))
    				return;
    
    			if (entry->flags & LPIT_DISABLED)
    				break;
    
    #ifdef INTELPMC_DEBUG
    			printf("state %d: 0x%02x:%d:%d:0x%02x:0x%016llx\n",
    			    entry->uid, entry->entry_trigger.address_space_id,
    			    entry->entry_trigger.register_bit_width,
    			    entry->entry_trigger.register_bit_offset,
    			    entry->entry_trigger.access_size,
    			    entry->entry_trigger.address);
    #endif
    
    			if (entry->flags & LPIT_COUNTER_NOT_AVAILABLE)
    				break;
    
    #ifdef INTELPMC_DEBUG
    			printf("counter: 0x%02x:%d:%d:0x%02x:0x%016llx\n",
    			    entry->residency_counter.address_space_id,
    			    entry->residency_counter.register_bit_width,
    			    entry->residency_counter.register_bit_offset,
    			    entry->residency_counter.access_size,
    			    entry->residency_counter.address);
    			printf("frequency: %lld\n",
    			    entry->residency_frequency);
    #endif
    
    			if (sc->sc_num_counters >= nitems(sc->sc_counter))
    				break;
    			memcpy(&sc->sc_counter[sc->sc_num_counters++],
    			       &entry->residency_counter, sizeof(struct acpi_gas));
    			break;
    		}
    
    		addr += length;
    	}
    }
    
    int
    intelpmc_dsm(struct acpi_softc *sc, struct aml_node *node, int func)
    {
    	struct aml_value cmd[4];
    	struct aml_value res;
    
    	/* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */
    	static uint8_t lps0_dsm_guid[] = {
    		0xA0, 0x40, 0xEB, 0xC4, 0xD2, 0x6C, 0xE2, 0x11,
    		0xBC, 0xFD, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66,
    	};
    
    	bzero(&cmd, sizeof(cmd));
    	cmd[0].type = AML_OBJTYPE_BUFFER;
    	cmd[0].v_buffer = (uint8_t *)&lps0_dsm_guid;
    	cmd[0].length = sizeof(lps0_dsm_guid);
    	/* rev */
    	cmd[1].type = AML_OBJTYPE_INTEGER;
    	cmd[1].v_integer = 0;
    	cmd[1].length = 1;
    	/* func */
    	cmd[2].type = AML_OBJTYPE_INTEGER;
    	cmd[2].v_integer = func;
    	cmd[2].length = 1;
    	/* not used */
    	cmd[3].type = AML_OBJTYPE_PACKAGE;
    	cmd[3].length = 0;
    
    	if (aml_evalname(sc, node, "_DSM", 4, cmd, &res)) {
    		printf("%s: eval of _DSM at %s failed\n",
    		    sc->sc_dev.dv_xname, aml_nodename(node));
    		return 1;
    	}
    	aml_freevalue(&res);
    
    	return 0;
    }
    
    void
    intelpmc_suspend(void *cookie)
    {
    	struct intelpmc_softc *sc = cookie;
    #ifdef INTELPMC_DEBUG
    	int i;
    #endif
    
    	if (sc->sc_acpi->sc_state != ACPI_STATE_S0)
    		return;
    
    #ifdef INTELPMC_DEBUG
    	rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[0]);
    	rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[0]);
    	rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[0]);
    	rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[0]);
    	rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[0]);
    	rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[0]);
    	rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[0]);
    	rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[0]);
    	rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[0]);
    	rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[0]);
    	for (i = 0; i < sc->sc_num_counters; i++) {
    		if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED)
    			rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][0]);
    		else {
    			acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
    			    sc->sc_counter[i].address_space_id,
    			    sc->sc_counter[i].address,
    			    (1 << (sc->sc_counter[i].access_size - 1)),
    			    sc->sc_counter[i].register_bit_width / 8,
    			    &sc->sc_lpit[i][0]);
    		}
    	}
    #endif
    
    	intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_OFF);
    	intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_ENTRY);
    }
    
    void
    intelpmc_resume(void *cookie)
    {
    	struct intelpmc_softc *sc = cookie;
    #ifdef INTELPMC_DEBUG
    	int i;
    #endif
    
    	if (sc->sc_acpi->sc_state != ACPI_STATE_S0)
    		return;
    
    	intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_EXIT);
    	intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_ON);
    
    #ifdef INTELPMC_DEBUG
    	rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[1]);
    	rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[1]);
    	rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[1]);
    	rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[1]);
    	rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[1]);
    	rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[1]);
    	rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[1]);
    	rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[1]);
    	rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[1]);
    	rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[1]);
    	for (i = 0; i < sc->sc_num_counters; i++) {
    		if (sc->sc_counter[i].address_space_id == GAS_FUNCTIONAL_FIXED)
    			rdmsr_safe(sc->sc_counter[i].address, &sc->sc_lpit[i][1]);
    		else {
    			acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
    			    sc->sc_counter[i].address_space_id,
    			    sc->sc_counter[i].address,
    			    (1 << (sc->sc_counter[i].access_size - 1)),
    			    sc->sc_counter[i].register_bit_width / 8,
    			    &sc->sc_lpit[i][1]);
    		}
    	}
    #endif
    }