ACPI P-State support step 1.5/2: Add CPU driver for AMD 10h family processors
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 3 Jun 2009 12:54:48 +0000 (20:54 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 3 Jun 2009 13:01:43 +0000 (21:01 +0800)
sys/dev/acpica5/Makefile
sys/dev/acpica5/acpi_cpu_pstate.c
sys/dev/acpica5/acpi_cpu_pstate.h [new file with mode: 0644]
sys/platform/pc32/acpica5/acpi_pstate_machdep.c [new file with mode: 0644]

index 8335337..33e8a5a 100644 (file)
@@ -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"
index f6065ef..e3c2e0e 100644 (file)
 #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;
@@ -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 (file)
index 0000000..8df0dcf
--- /dev/null
@@ -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 <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__ */
diff --git a/sys/platform/pc32/acpica5/acpi_pstate_machdep.c b/sys/platform/pc32/acpica5/acpi_pstate_machdep.c
new file mode 100644 (file)
index 0000000..f1b0a7a
--- /dev/null
@@ -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 <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;
+}