ACPI P-State support step 1/2: Extract information from ACPI objects
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 30 May 2009 11:31:00 +0000 (19:31 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 30 May 2009 11:38:14 +0000 (19:38 +0800)
share/man/man4/acpi.4
sys/dev/acpica5/Makefile
sys/dev/acpica5/acpi_cpu.c
sys/dev/acpica5/acpi_cpu_pstate.c [new file with mode: 0644]
sys/dev/acpica5/acpivar.h

index b3a9f12..f7cee7b 100644 (file)
@@ -219,6 +219,12 @@ Supports CPU power-saving.
 Diabling
 .Dq Li cpu ,
 will also disable this device.
+.It Li cpu_pst
+.Pq Vt device
+Supports CPU speed-setting.
+Diabling
+.Dq Li cpu ,
+will also disable this device.
 .It Li ec
 .Pq Vt device
 Supports the ACPI Embedded Controller interface, used to communicate
index 6ca4077..8335337 100644 (file)
@@ -63,7 +63,7 @@ SRCS+=        utresrc.c utstate.c utxface.c
 # OSD layer
 #
 SRCS+=  acpi.c acpi_acad.c acpi_battery.c acpi_button.c acpi_cmbat.c
-SRCS+= acpi_cpu.c acpi_cpu_cstate.c
+SRCS+= acpi_cpu.c acpi_cpu_cstate.c acpi_cpu_pstate.c
 SRCS+=  acpi_ec.c acpi_isab.c acpi_lid.c
 SRCS+=  acpi_package.c
 # The PCI part of ACPI5 doesn't work with the current infrastructure
index 939fb4b..fbbc00f 100644 (file)
@@ -185,6 +185,12 @@ acpi_cpu_attach(device_t dev)
     acpi_set_magic(child, cpu_id);
     sc->cpux_cst = child;
 
+    child = BUS_ADD_CHILD(dev, dev, 0, "cpu_pst", -1);
+    if (child == NULL)
+       return ENXIO;
+    acpi_set_handle(child, handle);
+    acpi_set_magic(child, cpu_id);
+
     bus_generic_attach(dev);
 
     AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, acpi_cpu_notify, sc);
diff --git a/sys/dev/acpica5/acpi_cpu_pstate.c b/sys/dev/acpica5/acpi_cpu_pstate.c
new file mode 100644 (file)
index 0000000..f6065ef
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+ * 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 "opt_acpi.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include "acpi.h"
+#include "acpivar.h"
+#include "acpi_cpu.h"
+
+#define ACPI_NPSTATE_MAX       16
+
+#define ACPI_PSS_PX_NENTRY     6
+
+#define ACPI_PSD_COORD_SWALL   0xfc
+#define ACPI_PSD_COORD_SWANY   0xfd
+#define ACPI_PSD_COORD_HWALL   0xfe
+#define ACPI_PSD_COORD_VALID(coord) \
+       ((coord) == ACPI_PSD_COORD_SWALL || \
+        (coord) == ACPI_PSD_COORD_SWANY || \
+        (coord) == ACPI_PSD_COORD_HWALL)
+
+struct acpi_pst_softc;
+LIST_HEAD(acpi_pst_list, acpi_pst_softc);
+
+struct acpi_pst_domain {
+       uint32_t                pd_dom;
+       uint32_t                pd_coord;
+       uint32_t                pd_nproc;
+       LIST_ENTRY(acpi_pst_domain) pd_link;
+
+       uint32_t                pd_flags;
+
+       int                     pd_state;
+       int                     pd_sstart;
+       struct acpi_pst_list    pd_pstlist;
+
+       struct sysctl_ctx_list  pd_sysctl_ctx;
+       struct sysctl_oid       *pd_sysctl_tree;
+};
+LIST_HEAD(acpi_pst_domlist, acpi_pst_domain);
+
+#define ACPI_PSTDOM_FLAG_STUB  0x1     /* stub domain, no _PSD */
+
+struct acpi_pst_softc {
+       struct acpi_cpux_softc  *pst_parent;
+       struct acpi_pst_domain  *pst_domain;
+       ACPI_RESOURCE_GENERIC_REGISTER pst_creg;
+       ACPI_RESOURCE_GENERIC_REGISTER pst_sreg;
+
+       int                     pst_state;
+       int                     pst_sstart;
+       int                     pst_cpuid;
+
+       ACPI_HANDLE             pst_handle;
+
+       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);
+
+static void    acpi_pst_postattach(void *);
+static struct acpi_pst_domain *
+               acpi_pst_domain_create(device_t, ACPI_OBJECT *);
+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_sysctl_freqs(SYSCTL_HANDLER_ARGS);
+static int     acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS);
+
+static struct acpi_pst_domlist acpi_pst_domains =
+       LIST_HEAD_INITIALIZER(acpi_pst_domains);
+
+static int                     acpi_npstates;
+static struct acpi_pstate      *acpi_pstates;
+
+static device_method_t acpi_pst_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,                 acpi_pst_probe),
+       DEVMETHOD(device_attach,                acpi_pst_attach),
+       DEVMETHOD(device_detach,                bus_generic_detach),
+       DEVMETHOD(device_shutdown,              bus_generic_shutdown),
+       DEVMETHOD(device_suspend,               bus_generic_suspend),
+       DEVMETHOD(device_resume,                bus_generic_resume),
+
+       /* Bus interface */
+       DEVMETHOD(bus_add_child,                bus_generic_add_child),
+       DEVMETHOD(bus_print_child,              bus_generic_print_child),
+       DEVMETHOD(bus_read_ivar,                bus_generic_read_ivar),
+       DEVMETHOD(bus_write_ivar,               bus_generic_write_ivar),
+       DEVMETHOD(bus_get_resource_list,        bus_generic_get_resource_list),
+       DEVMETHOD(bus_set_resource,             bus_generic_rl_set_resource),
+       DEVMETHOD(bus_get_resource,             bus_generic_rl_get_resource),
+       DEVMETHOD(bus_alloc_resource,           bus_generic_alloc_resource),
+       DEVMETHOD(bus_release_resource,         bus_generic_release_resource),
+       DEVMETHOD(bus_driver_added,             bus_generic_driver_added),
+       DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
+       DEVMETHOD(bus_deactivate_resource,      bus_generic_deactivate_resource),
+       DEVMETHOD(bus_setup_intr,               bus_generic_setup_intr),
+       DEVMETHOD(bus_teardown_intr,            bus_generic_teardown_intr),
+
+       { 0, 0 }
+};
+
+static driver_t acpi_pst_driver = {
+       "cpu_pst",
+       acpi_pst_methods,
+       sizeof(struct acpi_pst_softc)
+};
+
+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 int
+acpi_pst_probe(device_t dev)
+{
+       ACPI_BUFFER buf;
+       ACPI_HANDLE handle;
+       ACPI_STATUS status;
+       ACPI_OBJECT *obj;
+
+       if (acpi_disabled("cpu_pst") ||
+           acpi_get_type(dev) != ACPI_TYPE_PROCESSOR)
+               return ENXIO;
+
+       handle = acpi_get_handle(dev);
+
+       /*
+        * Check _PCT package
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(handle, "_PCT", NULL, &buf);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "Can't get _PCT package - %s\n",
+                             AcpiFormatException(status));
+               return ENXIO;
+       }
+
+       obj = (ACPI_OBJECT *)buf.Pointer;
+       if (!ACPI_PKG_VALID_EQ(obj, 2)) {
+               device_printf(dev, "Invalid _PCT package\n");
+               AcpiOsFree(obj);
+               return ENXIO;
+       }
+       AcpiOsFree(obj);
+
+       /*
+        * Check _PSS package
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(handle, "_PSS", NULL, &buf);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "Can't get _PSS package - %s\n",
+                             AcpiFormatException(status));
+               return ENXIO;
+       }
+
+       obj = (ACPI_OBJECT *)buf.Pointer;
+       if (!ACPI_PKG_VALID(obj, 1)) {
+               device_printf(dev, "Invalid _PSS package\n");
+               AcpiOsFree(obj);
+               return ENXIO;
+       }
+       AcpiOsFree(obj);
+
+       device_set_desc(dev, "ACPI CPU P-State");
+       return 0;
+}
+
+static int
+acpi_pst_attach(device_t dev)
+{
+       struct acpi_pst_softc *sc = device_get_softc(dev), *pst;
+       struct acpi_pst_domain *dom = NULL;
+       ACPI_BUFFER buf;
+       ACPI_STATUS status;
+       ACPI_OBJECT *obj, *reg;
+       struct acpi_pstate *pstate, *p;
+       int i, npstate;
+
+       sc->pst_parent = device_get_softc(device_get_parent(dev));
+       sc->pst_handle = acpi_get_handle(dev);
+       sc->pst_cpuid = acpi_get_magic(dev);
+
+       /*
+        * If there is a _PSD, then we create procossor domain
+        * accordingly.  If there is no _PSD, we just fake a
+        * default processor domain0.
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(sc->pst_handle, "_PSD", NULL, &buf);
+       if (!ACPI_FAILURE(status)) {
+               obj = (ACPI_OBJECT *)buf.Pointer;
+               if (ACPI_PKG_VALID_EQ(obj, 1)) {
+                       dom = acpi_pst_domain_create(dev,
+                               &obj->Package.Elements[0]);
+                       if (dom == NULL) {
+                               AcpiOsFree(obj);
+                               return ENXIO;
+                       }
+               } else {
+                       device_printf(dev, "Invalid _PSD package\n");
+                       AcpiOsFree(obj);
+                       return ENXIO;
+               }
+
+               /* Free _PSD */
+               AcpiOsFree(buf.Pointer);
+       } else {
+               /* Create a stub one processor domain */
+               dom = acpi_pst_domain_alloc(0, ACPI_PSD_COORD_SWANY, 1);
+               dom->pd_flags |= ACPI_PSTDOM_FLAG_STUB;
+       }
+
+       /* Make sure that adding us will not overflow our domain */
+       i = 0;
+       LIST_FOREACH(pst, &dom->pd_pstlist, pst_link)
+               ++i;
+       if (i == dom->pd_nproc) {
+               device_printf(dev, "Domain%u already contains %d P-States, "
+                             "invalid _PSD package\n",
+                             dom->pd_dom, dom->pd_nproc);
+               return ENXIO;
+       }
+
+       /*
+        * Get control/status registers from _PCT
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(sc->pst_handle, "_PCT", NULL, &buf);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "Can't get _PCT package - %s\n",
+                             AcpiFormatException(status));
+               return ENXIO;
+       }
+
+       obj = (ACPI_OBJECT *)buf.Pointer;
+       if (!ACPI_PKG_VALID_EQ(obj, 2)) {
+               device_printf(dev, "Invalid _PCT package\n");
+               AcpiOsFree(obj);
+               return ENXIO;
+       }
+
+       /* Save control register */
+       reg = &obj->Package.Elements[0];
+       if (reg->Type != ACPI_TYPE_BUFFER || reg->Buffer.Pointer == NULL ||
+           reg->Buffer.Length < sizeof(sc->pst_creg) + 3)
+               return ENXIO;
+       memcpy(&sc->pst_creg, reg->Buffer.Pointer + 3, sizeof(sc->pst_creg));
+       if (bootverbose) {
+               device_printf(dev, "control reg %d %llx\n",
+                             sc->pst_creg.SpaceId, sc->pst_creg.Address);
+       }
+
+       /* Save status register */
+       reg = &obj->Package.Elements[1];
+       if (reg->Type != ACPI_TYPE_BUFFER || reg->Buffer.Pointer == NULL ||
+           reg->Buffer.Length < sizeof(sc->pst_sreg) + 3)
+               return ENXIO;
+       memcpy(&sc->pst_sreg, reg->Buffer.Pointer + 3, sizeof(sc->pst_sreg));
+       if (bootverbose) {
+               device_printf(dev, "status reg %d %llx\n",
+                             sc->pst_sreg.SpaceId, sc->pst_sreg.Address);
+       }
+
+       /* Free _PCT */
+       AcpiOsFree(obj);
+
+       /*
+        * Create P-State table according to _PSS
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(sc->pst_handle, "_PSS", NULL, &buf);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "Can't get _PSS package - %s\n",
+                             AcpiFormatException(status));
+               return ENXIO;
+       }
+
+       obj = (ACPI_OBJECT *)buf.Pointer;
+       if (!ACPI_PKG_VALID(obj, 1)) {
+               device_printf(dev, "Invalid _PSS package\n");
+               AcpiOsFree(obj);
+               return ENXIO;
+       }
+
+       /* Don't create too many P-States */
+       npstate = obj->Package.Count;
+       if (npstate > ACPI_NPSTATE_MAX) {
+               device_printf(dev, "Too many P-States, %d->%d\n",
+                             npstate, ACPI_NPSTATE_MAX);
+               npstate = ACPI_NPSTATE_MAX;
+       }
+
+       /*
+        * If we have already created P-State table,
+        * we must make sure that number of entries
+        * is consistent.
+        */
+       if (acpi_pstates != NULL && acpi_npstates != npstate) {
+               device_printf(dev, "Inconsistent # of P-States "
+                             "cross Processor objects\n");
+               AcpiOsFree(obj);
+               return ENXIO;
+       }
+
+       /*
+        * Create a temporary P-State table
+        */
+       pstate = kmalloc(sizeof(*pstate) * npstate, M_TEMP, M_WAITOK);
+       for (i = 0, p = pstate; i < npstate; ++i, ++p) {
+               ACPI_OBJECT *pkg;
+               uint32_t *ptr[ACPI_PSS_PX_NENTRY] = {
+                       &p->st_freq, &p->st_power, &p->st_xsit_lat,
+                       &p->st_bm_lat, &p->st_cval, &p->st_sval
+               };
+               int j;
+
+               pkg = &obj->Package.Elements[i];
+               if (!ACPI_PKG_VALID(pkg, ACPI_PSS_PX_NENTRY)) {
+                       device_printf(dev, "Invalud _PSS P%d\n", i);
+                       AcpiOsFree(obj);
+                       kfree(pstate, M_TEMP);
+                       return ENXIO;
+               }
+               for (j = 0; j < ACPI_PSS_PX_NENTRY; ++j) {
+                       if (acpi_PkgInt32(pkg, j, ptr[j]) != 0) {
+                               device_printf(dev, "Can't extract "
+                                             "_PSS P%d %dth entry\n", i, j);
+                               AcpiOsFree(obj);
+                               kfree(pstate, M_TEMP);
+                               return ENXIO;
+                       }
+               }
+       }
+
+       /* Free _PSS */
+       AcpiOsFree(obj);
+
+       if (acpi_pstates == NULL) {
+               /*
+                * If no P-State table is created yet,
+                * save the temporary one we just created.
+                */
+               acpi_pstates = pstate;
+               acpi_npstates = npstate;
+               pstate = NULL;
+
+               if (bootverbose) {
+                       for (i = 0; i < acpi_npstates; ++i) {
+                               device_printf(dev,
+                               "freq %u, pwr %u, xlat %u, blat %u, "
+                               "cv %08x, sv %08x\n",
+                               acpi_pstates[i].st_freq,
+                               acpi_pstates[i].st_power,
+                               acpi_pstates[i].st_xsit_lat,
+                               acpi_pstates[i].st_bm_lat,
+                               acpi_pstates[i].st_cval,
+                               acpi_pstates[i].st_sval);
+                       }
+               }
+       } else {
+               /*
+                * Make sure that P-State tables are same
+                * for all processors.
+                */
+               if (memcmp(pstate, acpi_pstates,
+                          sizeof(*pstate) * npstate) != 0) {
+                       device_printf(dev, "Inconsistent _PSS "
+                                     "cross Processor objects\n");
+                       kfree(pstate, M_TEMP);
+                       return ENXIO;
+               }
+               kfree(pstate, M_TEMP);
+       }
+
+       /* By default, we start from P-State table's first entry */
+       sc->pst_sstart = 0;
+
+       /*
+        * Adjust the usable first entry of P-State table,
+        * if there is _PPC object.
+        */
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiEvaluateObject(sc->pst_handle, "_PPC", NULL, &buf);
+       if (!ACPI_FAILURE(status)) {
+               obj = (ACPI_OBJECT *)buf.Pointer;
+               if (obj->Type == ACPI_TYPE_INTEGER) {
+                       if (obj->Integer.Value >= acpi_npstates) {
+                               device_printf(dev, "Invalid _PPC value\n");
+                               AcpiOsFree(obj);
+                               return ENXIO;
+                       }
+                       sc->pst_sstart = obj->Integer.Value;
+                       if (bootverbose)
+                               device_printf(dev, "_PPC %d\n", sc->pst_sstart);
+
+                       /* TODO: Install notifiy handler */
+               } else {
+                       device_printf(dev, "Invalid _PPC object\n");
+                       AcpiOsFree(obj);
+                       return ENXIO;
+               }
+
+               /* Free _PPC */
+               AcpiOsFree(obj);
+       }
+
+       sc->pst_state = sc->pst_sstart;
+
+       /* Link us with the domain */
+       sc->pst_domain = dom;
+       LIST_INSERT_HEAD(&dom->pd_pstlist, sc, pst_link);
+
+       if (device_get_unit(dev) == 0)
+               AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_pst_postattach, NULL);
+
+       return 0;
+}
+
+static struct acpi_pst_domain *
+acpi_pst_domain_create(device_t dev, ACPI_OBJECT *obj)
+{
+       struct acpi_pst_domain *dom;
+       uint32_t val, domain, coord, nproc;
+
+       if (!ACPI_PKG_VALID_EQ(obj, 5)) {
+               device_printf(dev, "Invalid _PSD package\n");
+               return NULL;
+       }
+
+       /* NumberOfEntries */
+       if (acpi_PkgInt32(obj, 0, &val) != 0 || val != 5) {
+               device_printf(dev, "Invalid _PSD NumberOfEntries\n");
+               return NULL;
+       }
+
+       /* Revision */
+       if (acpi_PkgInt32(obj, 1, &val) != 0 || val != 0) {
+               device_printf(dev, "Invalid _PSD Revision\n");
+               return NULL;
+       }
+
+       if (acpi_PkgInt32(obj, 2, &domain) != 0 ||
+           acpi_PkgInt32(obj, 3, &coord) != 0 ||
+           acpi_PkgInt32(obj, 4, &nproc) != 0) {
+               device_printf(dev, "Can't extract _PSD package\n");
+               return NULL;
+       }
+
+       /*
+        * If NumProcessors is greater than MAXCPU,
+        * then we will never start all CPUs within
+        * this domain, and power state transition
+        * will never happen, so we just bail out
+        * here.
+        */
+       if (nproc > MAXCPU) {
+               device_printf(dev, "Unsupported _PSD NumProcessors (%d)\n",
+                             nproc);
+               return NULL;
+       } else if (nproc == 0) {
+               device_printf(dev, "_PSD NumProcessors are zero\n");
+               return NULL;
+       }
+
+       if (!ACPI_PSD_COORD_VALID(coord)) {
+               device_printf(dev, "Invalid _PSD CoordType (%#x)\n", coord);
+               return NULL;
+       }
+
+       dom = acpi_pst_domain_find(domain);
+       if (dom != NULL) {
+               if (dom->pd_coord != coord || dom->pd_nproc != nproc) {
+                       device_printf(dev, "Inconsistent _PSD information "
+                                     "cross Processor objects\n");
+                       return NULL;
+               }
+               return dom;
+       }
+
+       dom = acpi_pst_domain_alloc(domain, coord, nproc);
+       if (bootverbose)
+               device_printf(dev, "create domain %u\n", dom->pd_dom);
+
+       return dom;
+}
+
+static struct acpi_pst_domain *
+acpi_pst_domain_find(uint32_t domain)
+{
+       struct acpi_pst_domain *dom;
+
+       LIST_FOREACH(dom, &acpi_pst_domains, pd_link) {
+               if (dom->pd_flags & ACPI_PSTDOM_FLAG_STUB)
+                       continue;
+               if (dom->pd_dom == domain)
+                       return dom;
+       }
+       return NULL;
+}
+
+static struct acpi_pst_domain *
+acpi_pst_domain_alloc(uint32_t domain, uint32_t coord, uint32_t nproc)
+{
+       struct acpi_pst_domain *dom;
+
+       dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
+       dom->pd_dom = domain;
+       dom->pd_coord = coord;
+       dom->pd_nproc = nproc;
+       dom->pd_state = 0; /* XXX */
+       dom->pd_sstart = 0; /* XXX */
+       LIST_INIT(&dom->pd_pstlist);
+
+       LIST_INSERT_HEAD(&acpi_pst_domains, dom, pd_link);
+
+       return dom;
+}
+
+static void
+acpi_pst_postattach(void *arg __unused)
+{
+       struct acpi_pst_domain *dom;
+       struct acpi_cpux_softc *cpux;
+       device_t *devices;
+       int i, ndevices;
+
+       devices = NULL;
+       ndevices = 0;
+       devclass_get_devices(acpi_pst_devclass, &devices, &ndevices);
+       if (ndevices == 0)
+               return;
+
+       cpux = NULL;
+       for (i = 0; i < ndevices; ++i) {
+               cpux = device_get_softc(device_get_parent(devices[i]));
+               if (cpux->glob_sysctl_tree != NULL)
+                       break;
+       }
+       kfree(devices, M_TEMP);
+       KKASSERT(cpux != NULL);
+
+       LIST_FOREACH(dom, &acpi_pst_domains, pd_link) {
+               struct acpi_pst_softc *sc;
+               char buf[32];
+
+               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.
+                        */
+                       continue;
+               }
+               ksnprintf(buf, sizeof(buf), "px_dom%u", dom->pd_dom);
+
+               sysctl_ctx_init(&dom->pd_sysctl_ctx);
+               dom->pd_sysctl_tree =
+               SYSCTL_ADD_NODE(&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)
+                       continue;
+
+               SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx,
+                               SYSCTL_CHILDREN(dom->pd_sysctl_tree),
+                               OID_AUTO, "available",
+                               CTLTYPE_STRING | CTLFLAG_RD,
+                               dom, 0, acpi_pst_sysctl_freqs, "A",
+                               "available frequencies");
+
+               SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx,
+                               SYSCTL_CHILDREN(dom->pd_sysctl_tree),
+                               OID_AUTO, "members",
+                               CTLTYPE_STRING | CTLFLAG_RD,
+                               dom, 0, acpi_pst_sysctl_members, "A",
+                               "member cpus");
+       }
+}
+
+static int
+acpi_pst_sysctl_freqs(SYSCTL_HANDLER_ARGS)
+{
+       struct acpi_pst_domain *dom = arg1;
+       int i, error;
+
+       error = 0;
+       for (i = 0; i < acpi_npstates; ++i) {
+               if (error == 0 && i)
+                       error = SYSCTL_OUT(req, " ", 1);
+               if (error == 0) {
+                       const char *pat;
+                       char buf[32];
+
+                       if (i < dom->pd_sstart)
+                               pat = "(%u)";
+                       else
+                               pat = "%u";
+
+                       ksnprintf(buf, sizeof(buf), pat,
+                                 acpi_pstates[i].st_freq);
+                       error = SYSCTL_OUT(req, buf, strlen(buf));
+               }
+       }
+       return error;
+}
+
+static int
+acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS)
+{
+       struct acpi_pst_domain *dom = arg1;
+       const struct acpi_pst_softc *sc;
+       int loop, error;
+
+       loop = error = 0;
+       LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) {
+               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));
+               }
+               ++loop;
+       }
+       return error;
+}
index 120afd1..422a55f 100644 (file)
@@ -361,9 +361,12 @@ void               acpi_ec_ecdt_probe(device_t);
 int            acpi_acad_get_acline(int *);
 
 /* Package manipulation convenience functions. */
-#define ACPI_PKG_VALID(pkg, size)                              \
-    ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE &&      \
-     (pkg)->Package.Count >= (size))
+#define _ACPI_PKG_VALID(pkg)           \
+    ((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE)
+#define ACPI_PKG_VALID(pkg, size)      \
+    (_ACPI_PKG_VALID((pkg)) && (pkg)->Package.Count >= (size))
+#define ACPI_PKG_VALID_EQ(pkg, size)   \
+    (_ACPI_PKG_VALID((pkg)) && (pkg)->Package.Count == (size))
 int            acpi_PkgInt(ACPI_OBJECT *res, int idx, ACPI_INTEGER *dst);
 int            acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst);
 int            acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size);