#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
+#include <sys/msgport2.h>
+
+#include <net/netisr.h>
+#include <net/netmsg2.h>
+#include <net/if_var.h>
#include "acpi.h"
#include "acpivar.h"
#include "acpi_cpu.h"
+#include "acpi_cpu_pstate.h"
#define ACPI_NPSTATE_MAX 16
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;
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;
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);
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),
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)
{
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);
/*
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);
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;
}
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;
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);
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),
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");
}
}
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;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * 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__ */
--- /dev/null
+/*
+ * Copyright (c) 2009 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/globaldata.h>
+#include <machine/md_var.h>
+
+#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;
+}