From 7b46b972ec5ff5a033819820fd9459a69b6ef8a1 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Wed, 3 Jun 2009 20:54:48 +0800 Subject: [PATCH] ACPI P-State support step 1.5/2: Add CPU driver for AMD 10h family processors --- sys/dev/acpica5/Makefile | 2 + sys/dev/acpica5/acpi_cpu_pstate.c | 342 ++++++++++++++++++++++-- sys/dev/acpica5/acpi_cpu_pstate.h | 66 +++++ sys/platform/pc32/acpica5/acpi_pstate_machdep.c | 214 +++++++++++++++ 4 files changed, 604 insertions(+), 20 deletions(-) create mode 100644 sys/dev/acpica5/acpi_cpu_pstate.h create mode 100644 sys/platform/pc32/acpica5/acpi_pstate_machdep.c diff --git a/sys/dev/acpica5/Makefile b/sys/dev/acpica5/Makefile index 8335337a3a..33e8a5a4b2 100644 --- a/sys/dev/acpica5/Makefile +++ b/sys/dev/acpica5/Makefile @@ -104,6 +104,8 @@ SRCS+= utcache.c SRCS+= OsdCache.c .endif +# Machine-specific code for P-State +SRCS+= acpi_pstate_machdep.c # Machine-specific code such as sleep/wakeup SRCS+= acpi_machdep.c acpi_wakecode.h acpi_wakeup.c .if ${MACHINE_ARCH} == "i386" diff --git a/sys/dev/acpica5/acpi_cpu_pstate.c b/sys/dev/acpica5/acpi_cpu_pstate.c index f6065efebb..e3c2e0ea3b 100644 --- a/sys/dev/acpica5/acpi_cpu_pstate.c +++ b/sys/dev/acpica5/acpi_cpu_pstate.c @@ -40,10 +40,16 @@ #include #include #include +#include + +#include +#include +#include #include "acpi.h" #include "acpivar.h" #include "acpi_cpu.h" +#include "acpi_cpu_pstate.h" #define ACPI_NPSTATE_MAX 16 @@ -60,6 +66,13 @@ struct acpi_pst_softc; LIST_HEAD(acpi_pst_list, acpi_pst_softc); +struct netmsg_acpi_pst { + struct netmsg nmsg; + + const ACPI_RESOURCE_GENERIC_REGISTER *ctrl; + const ACPI_RESOURCE_GENERIC_REGISTER *status; +}; + struct acpi_pst_domain { uint32_t pd_dom; uint32_t pd_coord; @@ -78,8 +91,10 @@ struct acpi_pst_domain { LIST_HEAD(acpi_pst_domlist, acpi_pst_domain); #define ACPI_PSTDOM_FLAG_STUB 0x1 /* stub domain, no _PSD */ +#define ACPI_PSTDOM_FLAG_DEAD 0x2 /* domain can't be started */ struct acpi_pst_softc { + device_t pst_dev; struct acpi_cpux_softc *pst_parent; struct acpi_pst_domain *pst_domain; ACPI_RESOURCE_GENERIC_REGISTER pst_creg; @@ -94,15 +109,6 @@ struct acpi_pst_softc { LIST_ENTRY(acpi_pst_softc) pst_link; }; -struct acpi_pstate { - uint32_t st_freq; - uint32_t st_power; - uint32_t st_xsit_lat; - uint32_t st_bm_lat; - uint32_t st_cval; - uint32_t st_sval; -}; - static int acpi_pst_probe(device_t dev); static int acpi_pst_attach(device_t dev); @@ -113,16 +119,35 @@ static struct acpi_pst_domain * acpi_pst_domain_find(uint32_t); static struct acpi_pst_domain * acpi_pst_domain_alloc(uint32_t, uint32_t, uint32_t); +static int acpi_pst_domain_set_pstate(struct acpi_pst_domain *, int); + +static int acpi_pst_check_csr(struct acpi_pst_softc *); +static int acpi_pst_check_pstates(struct acpi_pst_softc *); +static int acpi_pst_set_pstate(struct acpi_pst_softc *, + const struct acpi_pstate *); +static const struct acpi_pstate * + acpi_pst_get_pstate(struct acpi_pst_softc *); + +static void acpi_pst_check_csr_handler(struct netmsg *); +static void acpi_pst_check_pstates_handler(struct netmsg *); +static void acpi_pst_set_pstate_handler(struct netmsg *); +static void acpi_pst_get_pstate_handler(struct netmsg *); static int acpi_pst_sysctl_freqs(SYSCTL_HANDLER_ARGS); static int acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS); +static int acpi_pst_sysctl_select(SYSCTL_HANDLER_ARGS); +static int acpi_pst_sysctl_global(SYSCTL_HANDLER_ARGS); static struct acpi_pst_domlist acpi_pst_domains = LIST_HEAD_INITIALIZER(acpi_pst_domains); +static int acpi_pst_global_state; + static int acpi_npstates; static struct acpi_pstate *acpi_pstates; +static const struct acpi_pst_md *acpi_pst_md; + static device_method_t acpi_pst_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_pst_probe), @@ -161,6 +186,18 @@ static devclass_t acpi_pst_devclass; DRIVER_MODULE(cpu_pst, cpu, acpi_pst_driver, acpi_pst_devclass, 0, 0); MODULE_DEPEND(cpu_pst, acpi, 1, 1, 1); +static __inline int +acpi_pst_freq2index(int freq) +{ + int i; + + for (i = 0; i < acpi_npstates; ++i) { + if (acpi_pstates[i].st_freq == freq) + return i; + } + return -1; +} + static int acpi_pst_probe(device_t dev) { @@ -173,6 +210,9 @@ acpi_pst_probe(device_t dev) acpi_get_type(dev) != ACPI_TYPE_PROCESSOR) return ENXIO; + if (acpi_pst_md == NULL) + acpi_pst_md = acpi_pst_md_probe(); + handle = acpi_get_handle(dev); /* @@ -230,6 +270,7 @@ acpi_pst_attach(device_t dev) struct acpi_pstate *pstate, *p; int i, npstate; + sc->pst_dev = dev; sc->pst_parent = device_get_softc(device_get_parent(dev)); sc->pst_handle = acpi_get_handle(dev); sc->pst_cpuid = acpi_get_magic(dev); @@ -537,7 +578,7 @@ acpi_pst_domain_create(device_t dev, ACPI_OBJECT *obj) dom = acpi_pst_domain_alloc(domain, coord, nproc); if (bootverbose) - device_printf(dev, "create domain %u\n", dom->pd_dom); + device_printf(dev, "create domain%u\n", dom->pd_dom); return dom; } @@ -574,17 +615,49 @@ acpi_pst_domain_alloc(uint32_t domain, uint32_t coord, uint32_t nproc) return dom; } +static int +acpi_pst_domain_set_pstate(struct acpi_pst_domain *dom, int i) +{ + const struct acpi_pstate *pstate; + struct acpi_pst_softc *sc; + int done, error; + + KKASSERT(i >= 0 && i < acpi_npstates); + pstate = &acpi_pstates[i]; + + done = 0; + LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) { + if (!done) { + error = acpi_pst_set_pstate(sc, pstate); + if (error) { + device_printf(sc->pst_dev, "can't set " + "freq %d\n", pstate->st_freq); + /* XXX error cleanup? */ + } + if (dom->pd_coord == ACPI_PSD_COORD_SWANY) + done = 1; + } + sc->pst_state = i; + } + dom->pd_state = i; + + return 0; +} + static void acpi_pst_postattach(void *arg __unused) { struct acpi_pst_domain *dom; struct acpi_cpux_softc *cpux; device_t *devices; - int i, ndevices; + int i, ndevices, error, has_domain; devices = NULL; ndevices = 0; - devclass_get_devices(acpi_pst_devclass, &devices, &ndevices); + error = devclass_get_devices(acpi_pst_devclass, &devices, &ndevices); + if (error) + return; + if (ndevices == 0) return; @@ -597,20 +670,50 @@ acpi_pst_postattach(void *arg __unused) kfree(devices, M_TEMP); KKASSERT(cpux != NULL); + if (acpi_pst_md == NULL) + kprintf("ACPI: no P-State CPU driver\n"); + + has_domain = 0; LIST_FOREACH(dom, &acpi_pst_domains, pd_link) { struct acpi_pst_softc *sc; char buf[32]; + /* + * Make sure that all processors belonging to this + * domain are located. + */ i = 0; LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) ++i; if (i != dom->pd_nproc) { - /* - * Can't activate this domain, - * certain processors are missing. - */ + kprintf("ACPI: domain%u misses processors, " + "should be %d, got %d\n", dom->pd_dom, + dom->pd_nproc, i); + dom->pd_flags |= ACPI_PSTDOM_FLAG_DEAD; + continue; + } + + /* + * Validate P-State configurations for this domain + */ + LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) { + error = acpi_pst_check_csr(sc); + if (error) + break; + + error = acpi_pst_check_pstates(sc); + if (error) + break; + } + if (sc != NULL) { + kprintf("ACPI: domain%u P-State configuration " + "check failed\n", dom->pd_dom); + dom->pd_flags |= ACPI_PSTDOM_FLAG_DEAD; continue; } + + has_domain = 1; + ksnprintf(buf, sizeof(buf), "px_dom%u", dom->pd_dom); sysctl_ctx_init(&dom->pd_sysctl_ctx); @@ -619,8 +722,11 @@ acpi_pst_postattach(void *arg __unused) SYSCTL_CHILDREN(cpux->glob_sysctl_tree), OID_AUTO, buf, CTLFLAG_RD, 0, "P-State domain"); - if (dom->pd_sysctl_tree == NULL) + if (dom->pd_sysctl_tree == NULL) { + kprintf("ACPI: Can't create sysctl tree for domain%u", + dom->pd_dom); continue; + } SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx, SYSCTL_CHILDREN(dom->pd_sysctl_tree), @@ -635,6 +741,26 @@ acpi_pst_postattach(void *arg __unused) CTLTYPE_STRING | CTLFLAG_RD, dom, 0, acpi_pst_sysctl_members, "A", "member cpus"); + + if (acpi_pst_md != NULL && + acpi_pst_md->pmd_set_pstate != NULL) { + SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx, + SYSCTL_CHILDREN(dom->pd_sysctl_tree), + OID_AUTO, "select", + CTLTYPE_UINT | CTLFLAG_RW, + dom, 0, acpi_pst_sysctl_select, + "IU", "select freq"); + } + } + + if (has_domain && acpi_pst_md != NULL && + acpi_pst_md->pmd_set_pstate != NULL) { + SYSCTL_ADD_PROC(&cpux->glob_sysctl_ctx, + SYSCTL_CHILDREN(cpux->glob_sysctl_tree), + OID_AUTO, "px_global", + CTLTYPE_UINT | CTLFLAG_RW, + NULL, 0, acpi_pst_sysctl_global, + "IU", "select freq for all domains"); } } @@ -669,20 +795,196 @@ static int acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS) { struct acpi_pst_domain *dom = arg1; - const struct acpi_pst_softc *sc; + struct acpi_pst_softc *sc; int loop, error; loop = error = 0; LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) { + char buf[32]; + if (error == 0 && loop) error = SYSCTL_OUT(req, " ", 1); if (error == 0) { - char buf[32]; - ksnprintf(buf, sizeof(buf), "cpu%d", sc->pst_cpuid); error = SYSCTL_OUT(req, buf, strlen(buf)); } + + if (error == 0 && acpi_pst_md && acpi_pst_md->pmd_get_pstate) { + const struct acpi_pstate *pstate; + const char *str; + + pstate = acpi_pst_get_pstate(sc); + if (pstate == NULL) { + str = "(*)"; + } else { + ksnprintf(buf, sizeof(buf), "(%d)", + pstate->st_freq); + str = buf; + } + error = SYSCTL_OUT(req, str, strlen(str)); + } ++loop; } return error; } + +static int +acpi_pst_sysctl_select(SYSCTL_HANDLER_ARGS) +{ + struct acpi_pst_domain *dom = arg1; + int error, i, freq; + + KKASSERT(dom->pd_state >= 0 && dom->pd_state < acpi_npstates); + + freq = acpi_pstates[dom->pd_state].st_freq; + + error = sysctl_handle_int(oidp, &freq, 0, req); + if (error || req->newptr == NULL) + return error; + + i = acpi_pst_freq2index(freq); + if (i < 0) + return EINVAL; + + acpi_pst_domain_set_pstate(dom, i); + return 0; +} + +static int +acpi_pst_sysctl_global(SYSCTL_HANDLER_ARGS) +{ + struct acpi_pst_domain *dom; + int error, i, freq; + + KKASSERT(acpi_pst_global_state >= 0 && + acpi_pst_global_state < acpi_npstates); + + freq = acpi_pstates[acpi_pst_global_state].st_freq; + + error = sysctl_handle_int(oidp, &freq, 0, req); + if (error || req->newptr == NULL) + return error; + + i = acpi_pst_freq2index(freq); + if (i < 0) + return EINVAL; + + LIST_FOREACH(dom, &acpi_pst_domains, pd_link) { + /* Skip dead domain */ + if (dom->pd_flags & ACPI_PSTDOM_FLAG_DEAD) + continue; + acpi_pst_domain_set_pstate(dom, i); + } + acpi_pst_global_state = i; + + return 0; +} + +static void +acpi_pst_check_csr_handler(struct netmsg *nmsg) +{ + struct netmsg_acpi_pst *msg = (struct netmsg_acpi_pst *)nmsg; + int error; + + error = acpi_pst_md->pmd_check_csr(msg->ctrl, msg->status); + lwkt_replymsg(&nmsg->nm_lmsg, error); +} + +static int +acpi_pst_check_csr(struct acpi_pst_softc *sc) +{ + struct netmsg_acpi_pst msg; + + if (acpi_pst_md == NULL) + return 0; + + netmsg_init(&msg.nmsg, &curthread->td_msgport, MSGF_MPSAFE, + acpi_pst_check_csr_handler); + msg.ctrl = &sc->pst_creg; + msg.status = &sc->pst_sreg; + + return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.nmsg.nm_lmsg, 0); +} + +static void +acpi_pst_check_pstates_handler(struct netmsg *nmsg) +{ + int error; + + error = acpi_pst_md->pmd_check_pstates(acpi_pstates, acpi_npstates); + lwkt_replymsg(&nmsg->nm_lmsg, error); +} + +static int +acpi_pst_check_pstates(struct acpi_pst_softc *sc) +{ + struct netmsg nmsg; + + if (acpi_pst_md == NULL) + return 0; + + netmsg_init(&nmsg, &curthread->td_msgport, MSGF_MPSAFE, + acpi_pst_check_pstates_handler); + + return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &nmsg.nm_lmsg, 0); +} + +static void +acpi_pst_set_pstate_handler(struct netmsg *nmsg) +{ + struct netmsg_acpi_pst *msg = (struct netmsg_acpi_pst *)nmsg; + int error; + + error = acpi_pst_md->pmd_set_pstate(msg->ctrl, msg->status, + nmsg->nm_lmsg.u.ms_resultp); + lwkt_replymsg(&nmsg->nm_lmsg, error); +} + +static int +acpi_pst_set_pstate(struct acpi_pst_softc *sc, const struct acpi_pstate *pstate) +{ + struct netmsg_acpi_pst msg; + + KKASSERT(acpi_pst_md != NULL); + + if (bootverbose) { + device_printf(sc->pst_dev, "set pstate, freq %d\n", + pstate->st_freq); + } + + netmsg_init(&msg.nmsg, &curthread->td_msgport, MSGF_MPSAFE, + acpi_pst_set_pstate_handler); + msg.nmsg.nm_lmsg.u.ms_resultp = __DECONST(void *, pstate); + msg.ctrl = &sc->pst_creg; + msg.status = &sc->pst_sreg; + + return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.nmsg.nm_lmsg, 0); +} + +static void +acpi_pst_get_pstate_handler(struct netmsg *nmsg) +{ + struct netmsg_acpi_pst *msg = (struct netmsg_acpi_pst *)nmsg; + const struct acpi_pstate *pstate; + + pstate = acpi_pst_md->pmd_get_pstate(msg->status, acpi_pstates, + acpi_npstates); + nmsg->nm_lmsg.u.ms_resultp = __DECONST(void *, pstate); + lwkt_replymsg(&nmsg->nm_lmsg, 0); +} + +static const struct acpi_pstate * +acpi_pst_get_pstate(struct acpi_pst_softc *sc) +{ + struct netmsg_acpi_pst msg; + + if (acpi_pst_md == NULL) + return 0; + + netmsg_init(&msg.nmsg, &curthread->td_msgport, MSGF_MPSAFE, + acpi_pst_get_pstate_handler); + msg.status = &sc->pst_sreg; + + lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.nmsg.nm_lmsg, 0); + return msg.nmsg.nm_lmsg.u.ms_resultp; +} diff --git a/sys/dev/acpica5/acpi_cpu_pstate.h b/sys/dev/acpica5/acpi_cpu_pstate.h new file mode 100644 index 0000000000..8df0dcf9ed --- /dev/null +++ b/sys/dev/acpica5/acpi_cpu_pstate.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __ACPI_CPU_PSTATE_H__ +#define __ACPI_CPU_PSTATE_H__ + +struct acpi_pstate { + uint32_t st_freq; + uint32_t st_power; + uint32_t st_xsit_lat; + uint32_t st_bm_lat; + uint32_t st_cval; + uint32_t st_sval; +}; + +struct acpi_pst_md { + int (*pmd_check_csr) + (const ACPI_RESOURCE_GENERIC_REGISTER *, + const ACPI_RESOURCE_GENERIC_REGISTER *); + int (*pmd_check_pstates) + (const struct acpi_pstate *, int); + + int (*pmd_set_pstate) + (const ACPI_RESOURCE_GENERIC_REGISTER *, + const ACPI_RESOURCE_GENERIC_REGISTER *, + const struct acpi_pstate *); + + const struct acpi_pstate *(*pmd_get_pstate) + (const ACPI_RESOURCE_GENERIC_REGISTER *, + const struct acpi_pstate *, int); +}; + +const struct acpi_pst_md *acpi_pst_md_probe(void); + +#endif /* !__ACPI_CPU_PSTATE_H__ */ diff --git a/sys/platform/pc32/acpica5/acpi_pstate_machdep.c b/sys/platform/pc32/acpica5/acpi_pstate_machdep.c new file mode 100644 index 0000000000..f1b0a7ad6f --- /dev/null +++ b/sys/platform/pc32/acpica5/acpi_pstate_machdep.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "acpi.h" +#include "acpi_cpu_pstate.h" + +#define AMD_APMI_HWPSTATE 0x80 + +#define AMD_MSR_PSTATE_CSR_MASK 0x7ULL +#define AMD1XH_MSR_PSTATE_CTL 0xc0010062 +#define AMD1XH_MSR_PSTATE_ST 0xc0010063 + +#define AMD_MSR_PSTATE_EN 0x8000000000000000ULL + +#define AMD10H_MSR_PSTATE_START 0xc0010064 +#define AMD10H_MSR_PSTATE_COUNT 5 + +static const struct acpi_pst_md * + acpi_pst_amd_probe(void); +static int acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *, + const ACPI_RESOURCE_GENERIC_REGISTER *); +static int acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *, int, + uint32_t, uint32_t); +static int acpi_pst_amd10h_check_pstates(const struct acpi_pstate *, int); +static int acpi_pst_amd1xh_set_pstate( + const ACPI_RESOURCE_GENERIC_REGISTER *, + const ACPI_RESOURCE_GENERIC_REGISTER *, + const struct acpi_pstate *); +static const struct acpi_pstate * + acpi_pst_amd1xh_get_pstate( + const ACPI_RESOURCE_GENERIC_REGISTER *, + const struct acpi_pstate *, int); + +static const struct acpi_pst_md acpi_pst_amd10h = { + .pmd_check_csr = acpi_pst_amd_check_csr, + .pmd_check_pstates = acpi_pst_amd10h_check_pstates, + .pmd_set_pstate = acpi_pst_amd1xh_set_pstate, + .pmd_get_pstate = acpi_pst_amd1xh_get_pstate +}; + +const struct acpi_pst_md * +acpi_pst_md_probe(void) +{ + if (strcmp(cpu_vendor, "AuthenticAMD") == 0) + return acpi_pst_amd_probe(); + return NULL; +} + +static const struct acpi_pst_md * +acpi_pst_amd_probe(void) +{ + uint32_t regs[4], ext_family; + + if ((cpu_id & 0x00000f00) != 0x00000f00) + return NULL; + + /* Check whether APMI exists */ + do_cpuid(0x80000000, regs); + if (regs[0] < 0x80000007) + return NULL; + + /* Fetch APMI */ + do_cpuid(0x80000007, regs); + + ext_family = cpu_id & 0x0ff00000; + switch (ext_family) { + case 0x00100000: /* Family 10h */ + if (regs[3] & 0x80) + return &acpi_pst_amd10h; + break; + + default: + break; + } + return NULL; +} + +static int +acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl, + const ACPI_RESOURCE_GENERIC_REGISTER *status) +{ + if (ctrl->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) { + kprintf("cpu%d: Invalid P-State control register\n", mycpuid); + return EINVAL; + } + if (status->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) { + kprintf("cpu%d: Invalid P-State status register\n", mycpuid); + return EINVAL; + } + return 0; +} + +static int +acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *pstates, int npstates, + uint32_t msr_start, uint32_t msr_end) +{ + int i; + + /* + * Make sure that related MSR P-State registers are enabled. + * + * NOTE: + * We don't check status register value here; + * it will not be used. + */ + for (i = 0; i < npstates; ++i) { + uint64_t pstate; + uint32_t msr; + + msr = msr_start + + (pstates[i].st_cval & AMD_MSR_PSTATE_CSR_MASK); + if (msr >= msr_end) { + kprintf("cpu%d: MSR P-State register %#08x " + "does not exist\n", mycpuid, msr); + return EINVAL; + } + + pstate = rdmsr(msr); + if ((pstate & AMD_MSR_PSTATE_EN) == 0) { + kprintf("cpu%d: MSR P-State register %#08x " + "is not enabled\n", mycpuid, msr); + return EINVAL; + } + } + return 0; +} + +static int +acpi_pst_amd10h_check_pstates(const struct acpi_pstate *pstates, int npstates) +{ + /* Only P0-P4 are supported */ + if (npstates > AMD10H_MSR_PSTATE_COUNT) { + kprintf("cpu%d: only P0-P4 is allowed\n", mycpuid); + return EINVAL; + } + + return acpi_pst_amd1xh_check_pstates(pstates, npstates, + AMD10H_MSR_PSTATE_START, + AMD10H_MSR_PSTATE_START + AMD10H_MSR_PSTATE_COUNT); +} + +static int +acpi_pst_amd1xh_set_pstate(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl __unused, + const ACPI_RESOURCE_GENERIC_REGISTER *status __unused, + const struct acpi_pstate *pstate) +{ + uint64_t cval; + + cval = pstate->st_cval & AMD_MSR_PSTATE_CSR_MASK; + wrmsr(AMD1XH_MSR_PSTATE_CTL, cval); + + /* + * Don't check AMD1XH_MSR_PSTATE_ST here, since it is + * affected by various P-State limits. + * + * For details: + * AMD Family 10h Processor BKDG Rev 3.20 (#31116) + * 2.4.2.4 P-state Transition Behavior + */ + + return 0; +} + +static const struct acpi_pstate * +acpi_pst_amd1xh_get_pstate( + const ACPI_RESOURCE_GENERIC_REGISTER *status __unused, + const struct acpi_pstate *pstates, int npstates) +{ + uint64_t sval; + int i; + + sval = rdmsr(AMD1XH_MSR_PSTATE_ST) & AMD_MSR_PSTATE_CSR_MASK; + for (i = 0; i < npstates; ++i) { + if ((pstates[i].st_sval & AMD_MSR_PSTATE_CSR_MASK) == sval) + return &pstates[i]; + } + return NULL; +} -- 2.11.4.GIT