Module Name: src
Committed By: jruoho
Date: Wed Jan 5 20:08:14 UTC 2011
Modified Files:
src/distrib/sets/lists/man: mi
src/doc: CHANGES
src/share/man/man4: Makefile
src/sys/arch/amd64/conf: GENERIC
src/sys/arch/i386/conf: GENERIC
src/sys/dev/acpi: files.acpi
Added Files:
src/share/man/man4: acpipmtr.4
src/sys/dev/acpi: acpi_pmtr.c
Log Message:
Add a driver for ACPI power meters.
To generate a diff of this commit:
cvs rdiff -u -r1.1271 -r1.1272 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.1484 -r1.1485 src/doc/CHANGES
cvs rdiff -u -r1.540 -r1.541 src/share/man/man4/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man4/acpipmtr.4
cvs rdiff -u -r1.295 -r1.296 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r1.1004 -r1.1005 src/sys/arch/i386/conf/GENERIC
cvs rdiff -u -r0 -r1.1 src/sys/dev/acpi/acpi_pmtr.c
cvs rdiff -u -r1.82 -r1.83 src/sys/dev/acpi/files.acpi
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1271 src/distrib/sets/lists/man/mi:1.1272
--- src/distrib/sets/lists/man/mi:1.1271 Mon Dec 27 17:26:57 2010
+++ src/distrib/sets/lists/man/mi Wed Jan 5 20:08:12 2011
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1271 2010/12/27 17:26:57 jmcneill Exp $
+# $NetBSD: mi,v 1.1272 2011/01/05 20:08:12 jruoho Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -637,6 +637,7 @@
./usr/share/man/cat4/acpiecdt.0 man-sys-catman .cat
./usr/share/man/cat4/acpilid.0 man-sys-catman .cat
./usr/share/man/cat4/acpiout.0 man-sys-catman .cat
+./usr/share/man/cat4/acpipmtr.0 man-sys-catman .cat
./usr/share/man/cat4/acpismbus.0 man-sys-catman .cat
./usr/share/man/cat4/acpitz.0 man-sys-catman .cat
./usr/share/man/cat4/acpivga.0 man-sys-catman .cat
@@ -3401,6 +3402,7 @@
./usr/share/man/html4/acpiecdt.html man-sys-htmlman html
./usr/share/man/html4/acpilid.html man-sys-htmlman html
./usr/share/man/html4/acpiout.html man-sys-htmlman html
+./usr/share/man/html4/acpipmtr.html man-sys-htmlman html
./usr/share/man/html4/acpismbus.html man-sys-htmlman html
./usr/share/man/html4/acpitz.html man-sys-htmlman html
./usr/share/man/html4/acpivga.html man-sys-htmlman html
@@ -5865,6 +5867,7 @@
./usr/share/man/man4/acpiecdt.4 man-sys-man .man
./usr/share/man/man4/acpilid.4 man-sys-man .man
./usr/share/man/man4/acpiout.4 man-sys-man .man
+./usr/share/man/man4/acpipmtr.4 man-sys-man .man
./usr/share/man/man4/acpismbus.4 man-sys-man .man
./usr/share/man/man4/acpitz.4 man-sys-man .man
./usr/share/man/man4/acpivga.4 man-sys-man .man
Index: src/doc/CHANGES
diff -u src/doc/CHANGES:1.1484 src/doc/CHANGES:1.1485
--- src/doc/CHANGES:1.1484 Wed Jan 5 03:49:57 2011
+++ src/doc/CHANGES Wed Jan 5 20:08:13 2011
@@ -1,4 +1,4 @@
-# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.1484 $>
+# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.1485 $>
#
#
# [Note: This file does not mention every change made to the NetBSD source tree.
@@ -818,3 +818,4 @@
dhcpcd(8): Import dhcpcd-5.2.10. [roy 20110103]
resize_ffs(8): support byteswapped file systems, and support
growing ufs2 file systems. [riz 20110104]
+ acpipmtr(4): Add a driver for ACPI power meters. [jruoho 20110105]
Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.540 src/share/man/man4/Makefile:1.541
--- src/share/man/man4/Makefile:1.540 Mon Dec 27 17:26:58 2010
+++ src/share/man/man4/Makefile Wed Jan 5 20:08:13 2011
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.540 2010/12/27 17:26:58 jmcneill Exp $
+# $NetBSD: Makefile,v 1.541 2011/01/05 20:08:13 jruoho Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@@ -83,7 +83,7 @@
# ACPI devices
MAN+= acpi.4 acpiacad.4 acpibat.4 acpibut.4 acpicpu.4 \
acpidalb.4 acpiec.4 acpilid.4 \
- acpismbus.4 acpitz.4 acpivga.4 acpiwmi.4
+ acpipmtr.4 acpismbus.4 acpitz.4 acpivga.4 acpiwmi.4
# Radio devices
MAN+= radio.4 az.4 mr.4 rt.4 rtii.4 sf2r.4
Index: src/sys/arch/amd64/conf/GENERIC
diff -u src/sys/arch/amd64/conf/GENERIC:1.295 src/sys/arch/amd64/conf/GENERIC:1.296
--- src/sys/arch/amd64/conf/GENERIC:1.295 Mon Jan 3 06:07:42 2011
+++ src/sys/arch/amd64/conf/GENERIC Wed Jan 5 20:08:12 2011
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.295 2011/01/03 06:07:42 tsutsui Exp $
+# $NetBSD: GENERIC,v 1.296 2011/01/05 20:08:12 jruoho Exp $
#
# GENERIC machine description file
#
@@ -22,7 +22,7 @@
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
-#ident "GENERIC-$Revision: 1.295 $"
+#ident "GENERIC-$Revision: 1.296 $"
maxusers 64 # estimated number of users
@@ -276,12 +276,10 @@
acpibut* at acpi? # ACPI Button
#acpicpu* at acpi? # ACPI CPU
acpidalb* at acpi? # Direct Application Launch Button
-# The ACPI Embedded Controller is generally configured via the special ECDT.
-# This is required as parts of the DSDT can reference the EC before the normal
-# attach phase.
-acpiec* at acpi? # ACPI Embedded Controller (late binding)
-acpiecdt* at acpi? # ACPI Embedded Controller (early binding)
+acpiec* at acpi? # ACPI Embedded Controller (late)
+acpiecdt* at acpi? # ACPI Embedded Controller (early)
acpilid* at acpi? # ACPI Lid Switch
+#acpipmtr* at acpi? # ACPI Power Meter (experimental)
#acpismbus* at acpi? # ACPI SMBus CMI (experimental)
acpitz* at acpi? # ACPI Thermal Zone
acpivga* at acpi? # ACPI Display Adapter
Index: src/sys/arch/i386/conf/GENERIC
diff -u src/sys/arch/i386/conf/GENERIC:1.1004 src/sys/arch/i386/conf/GENERIC:1.1005
--- src/sys/arch/i386/conf/GENERIC:1.1004 Mon Jan 3 06:07:43 2011
+++ src/sys/arch/i386/conf/GENERIC Wed Jan 5 20:08:12 2011
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.1004 2011/01/03 06:07:43 tsutsui Exp $
+# $NetBSD: GENERIC,v 1.1005 2011/01/05 20:08:12 jruoho Exp $
#
# GENERIC machine description file
#
@@ -22,7 +22,7 @@
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
-#ident "GENERIC-$Revision: 1.1004 $"
+#ident "GENERIC-$Revision: 1.1005 $"
maxusers 64 # estimated number of users
@@ -356,12 +356,10 @@
acpibut* at acpi? # ACPI Button
#acpicpu* at acpi? # ACPI CPU
acpidalb* at acpi? # ACPI Direct Application Launch Button
-# The ACPI Embedded Controller is generally configured via the special ECDT.
-# This is required as parts of the DSDT can reference the EC before the normal
-# attach phase.
-acpiec* at acpi? # ACPI Embedded Controller (late binding)
-acpiecdt* at acpi? # ACPI Embedded Controller (early binding)
+acpiec* at acpi? # ACPI Embedded Controller (late)
+acpiecdt* at acpi? # ACPI Embedded Controller (early)
acpilid* at acpi? # ACPI Lid Switch
+#acpipmtr* at acpi? # ACPI Power Meter (experimental)
#acpismbus* at acpi? # ACPI SMBus CMI (experimental)
acpitz* at acpi? # ACPI Thermal Zone
acpivga* at acpi? # ACPI Display Adapter
Index: src/sys/dev/acpi/files.acpi
diff -u src/sys/dev/acpi/files.acpi:1.82 src/sys/dev/acpi/files.acpi:1.83
--- src/sys/dev/acpi/files.acpi:1.82 Fri Nov 5 10:28:20 2010
+++ src/sys/dev/acpi/files.acpi Wed Jan 5 20:08:12 2011
@@ -1,4 +1,4 @@
-# $NetBSD: files.acpi,v 1.82 2010/11/05 10:28:20 gsutre Exp $
+# $NetBSD: files.acpi,v 1.83 2011/01/05 20:08:12 jruoho Exp $
include "dev/acpi/acpica/files.acpica"
@@ -73,6 +73,11 @@
attach acpiout at acpivga
file dev/acpi/acpi_display.c acpivga|acpiout
+# ACPI Power Meter
+device acpipmtr: sysmon_envsys
+attach acpipmtr at acpinodebus
+file dev/acpi/acpi_pmtr.c acpipmtr
+
# ACPI Thermal Zone
device acpitz: sysmon_envsys
attach acpitz at acpinodebus
Added files:
Index: src/share/man/man4/acpipmtr.4
diff -u /dev/null src/share/man/man4/acpipmtr.4:1.1
--- /dev/null Wed Jan 5 20:08:14 2011
+++ src/share/man/man4/acpipmtr.4 Wed Jan 5 20:08:13 2011
@@ -0,0 +1,66 @@
+.\" $NetBSD: acpipmtr.4,v 1.1 2011/01/05 20:08:13 jruoho Exp $
+.\"
+.\" Copyright (c) 2010 Jukka Ruohonen <[email protected]>
+.\" All rights reserved.
+.\"
+.\" 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. Neither the name of the author nor the names of any
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 FOUNDATION 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.
+.\"
+.Dd December 22, 2010
+.Dt ACPIPMTR 4
+.Os
+.Sh NAME
+.Nm acpipmtr
+.Nd ACPI Power Meter
+.Sh SYNOPSIS
+.Cd "acpipmtr* at acpi?"
+.Sh DESCRIPTION
+The
+.Nm
+device driver provides support for
+.Dq power meters .
+These are sensors that approximate the current
+power consumption of the system.
+Alternatively, a power meter may measure also the power gain
+when the system is connected to an external power supply.
+.Pp
+The available information (namely, the power consumption)
+is made available through the
+.Xr envsys 4
+.Tn API
+and the
+.Xr envstat 8
+command.
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpibat 4
+.Sh HISTORY
+The
+.Nm
+device driver appeared in
+.Nx 6.0 .
+.Sh AUTHORS
+.An Jukka Ruohonen
+.Aq [email protected]
+.Sh CAVEATS
+The
+.Nm
+driver is experimental.
Index: src/sys/dev/acpi/acpi_pmtr.c
diff -u /dev/null src/sys/dev/acpi/acpi_pmtr.c:1.1
--- /dev/null Wed Jan 5 20:08:14 2011
+++ src/sys/dev/acpi/acpi_pmtr.c Wed Jan 5 20:08:12 2011
@@ -0,0 +1,518 @@
+/* $NetBSD: acpi_pmtr.c,v 1.1 2011/01/05 20:08:12 jruoho Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jukka Ruohonen <[email protected]>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: acpi_pmtr.c,v 1.1 2011/01/05 20:08:12 jruoho Exp $");
+
+#include <sys/param.h>
+#include <sys/module.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+
+#include <dev/sysmon/sysmonvar.h>
+
+#define _COMPONENT ACPI_RESOURCE_COMPONENT
+ACPI_MODULE_NAME ("acpi_pmtr")
+
+#define ACPIPMTR_CAP_FLAGS 0
+#define ACPIPMTR_CAP_UNIT 1
+#define ACPIPMTR_CAP_TYPE 2
+#define ACPIPMTR_CAP_ACCURACY 3
+#define ACPIPMTR_CAP_SAMPLING 4
+#define ACPIPMTR_CAP_IVAL_MIN 5
+#define ACPIPMTR_CAP_IVAL_MAX 6
+#define ACPIPMTR_CAP_HYSTERESIS 7
+#define ACPIPMTR_CAP_HWLIMIT 8
+#define ACPIPMTR_CAP_HWLIMIT_MIN 9
+#define ACPIPMTR_CAP_HWLIMIT_MAX 10
+#define ACPIPMTR_CAP_COUNT 11
+/* ACPIPMTR_CAP_MODEL 11 */
+/* ACPIPMTR_CAP_SERIAL 12 */
+/* ACPIPMTR_CAP_OEM 13 */
+
+#define ACPIPMTR_FLAGS_MEASURE __BIT(0)
+#define ACPIPMTR_FLAGS_TRIP __BIT(1)
+#define ACPIPMTR_FLAGS_HWLIMIT __BIT(2)
+#define ACPIPMTR_FLAGS_NOTIFY __BIT(3)
+#define ACPIPMTR_FLAGS_DISCHARGE __BIT(8)
+
+#define ACPIPMTR_POWER_INPUT 0x00
+#define ACPIPMTR_POWER_OUTPUT 0x01
+
+#define ACPIPMTR_NOTIFY_CAP 0x80
+#define ACPIPMTR_NOTIFY_TRIP 0x81
+#define ACPIPMTR_NOTIFY_HWLIMIT1 0x82
+#define ACPIPMTR_NOTIFY_HWLIMIT2 0x83
+#define ACPIPMTR_NOTIFY_INTERVAL 0x84
+
+struct acpipmtr_softc {
+ device_t sc_dev;
+ struct acpi_devnode *sc_node;
+ struct sysmon_envsys *sc_sme;
+ envsys_data_t sc_sensor_i;
+ envsys_data_t sc_sensor_o;
+ uint32_t sc_cap[ACPIPMTR_CAP_COUNT];
+ int32_t sc_interval;
+};
+
+const char * const acpi_pmtr_ids[] = {
+ "ACPI000D",
+ NULL
+};
+
+static int acpipmtr_match(device_t, cfdata_t, void *);
+static void acpipmtr_attach(device_t, device_t, void *);
+static int acpipmtr_detach(device_t, int);
+static bool acpipmtr_cap_get(device_t, bool);
+static bool acpipmtr_dev_print(device_t);
+static bool acpipmtr_sensor_init(device_t);
+static void acpipmtr_sensor_type(device_t);
+static int32_t acpipmtr_sensor_get(device_t, const char *);
+static int32_t acpipmtr_sensor_get_reading(device_t);
+static int32_t acpipmtr_sensor_get_interval(device_t);
+static void acpipmtr_sensor_refresh(struct sysmon_envsys*,envsys_data_t *);
+static void acpipmtr_notify(ACPI_HANDLE, uint32_t, void *);
+
+CFATTACH_DECL_NEW(acpipmtr, sizeof(struct acpipmtr_softc),
+ acpipmtr_match, acpipmtr_attach, acpipmtr_detach, NULL);
+
+static int
+acpipmtr_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct acpi_attach_args *aa = aux;
+
+ if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
+ return 0;
+
+ return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_pmtr_ids);
+}
+
+static void
+acpipmtr_attach(device_t parent, device_t self, void *aux)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+ struct acpi_attach_args *aa = aux;
+ uint32_t acc;
+
+ sc->sc_sme = NULL;
+ sc->sc_dev = self;
+ sc->sc_node = aa->aa_node;
+
+ aprint_naive("\n");
+ aprint_normal(": ACPI Power Meter\n");
+
+ (void)pmf_device_register(self, NULL, NULL);
+
+ if (acpipmtr_cap_get(self, true) != true)
+ return;
+
+ if (acpipmtr_sensor_init(self) != true)
+ return;
+
+ (void)acpipmtr_dev_print(self);
+ (void)acpi_register_notify(sc->sc_node, acpipmtr_notify);
+
+ if ((acc = sc->sc_cap[ACPIPMTR_CAP_ACCURACY]) == 0)
+ acc = 100000;
+
+ aprint_verbose_dev(self,
+ "measuring %s power at %u.%u %% accuracy, %u ms sampling\n",
+ (sc->sc_cap[ACPIPMTR_CAP_TYPE] != 0) ? "output" : "input",
+ acc / 1000, acc % 1000, sc->sc_cap[ACPIPMTR_CAP_SAMPLING]);
+
+ aprint_debug_dev(self, "%s hw-limits, capabilities 0x%02x\n",
+ (sc->sc_cap[ACPIPMTR_CAP_HWLIMIT] != 0) ? "rw" : "ro",
+ sc->sc_cap[ACPIPMTR_CAP_FLAGS]);
+}
+
+static int
+acpipmtr_detach(device_t self, int flags)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+
+ pmf_device_deregister(self);
+ acpi_deregister_notify(sc->sc_node);
+
+ if (sc->sc_sme != NULL)
+ sysmon_envsys_unregister(sc->sc_sme);
+
+ return 0;
+}
+
+static bool
+acpipmtr_cap_get(device_t self, bool print)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+ ACPI_OBJECT *elm, *obj;
+ ACPI_BUFFER buf;
+ ACPI_STATUS rv;
+ uint32_t i;
+
+ for (i = 0; i < __arraycount(sc->sc_cap); i++)
+ sc->sc_cap[i] = 0;
+
+ rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMC", &buf);
+
+ if (ACPI_FAILURE(rv))
+ goto out;
+
+ obj = buf.Pointer;
+
+ if (obj->Type != ACPI_TYPE_PACKAGE) {
+ rv = AE_TYPE;
+ goto out;
+ }
+
+ elm = obj->Package.Elements;
+
+ if (obj->Package.Count != 14) {
+ rv = AE_LIMIT;
+ goto out;
+ }
+
+ CTASSERT(__arraycount(sc->sc_cap) == 11);
+
+ for (i = 0; i < __arraycount(sc->sc_cap); i++) {
+
+ if (elm[i].Type != ACPI_TYPE_INTEGER) {
+ rv = AE_TYPE;
+ goto out;
+ }
+
+ if (elm[i].Integer.Value > UINT32_MAX) {
+ rv = AE_AML_NUMERIC_OVERFLOW;
+ goto out;
+ }
+
+ sc->sc_cap[i] = elm[i].Integer.Value;
+ }
+
+ if (print != true)
+ goto out;
+
+ for (; i < 14; i++) {
+
+ if (elm[i].Type != ACPI_TYPE_STRING)
+ goto out;
+
+ if (elm[i].String.Pointer == NULL)
+ goto out;
+
+ if (elm[i].String.Pointer[0] == '\0')
+ goto out;
+ }
+
+ aprint_debug_dev(self, "%s, serial %s, "
+ "model %s\n", elm[13].String.Pointer,
+ elm[12].String.Pointer, elm[11].String.Pointer);
+
+out:
+ if (ACPI_FAILURE(rv))
+ aprint_error_dev(self, "failed to evaluate _PMC: %s\n",
+ AcpiFormatException(rv));
+
+ if (buf.Pointer != NULL)
+ ACPI_FREE(buf.Pointer);
+
+ return (rv != AE_OK) ? false : true;
+}
+
+static bool
+acpipmtr_dev_print(device_t self)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+ struct acpi_devnode *ad;
+ ACPI_OBJECT *elm, *obj;
+ ACPI_BUFFER buf;
+ ACPI_HANDLE hdl;
+ ACPI_STATUS rv;
+ uint32_t i, n;
+
+ /*
+ * The _PMD method returns a package of devices whose total power
+ * drawn should roughly correspond with the readings from the meter.
+ */
+ rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMD", &buf);
+
+ if (ACPI_FAILURE(rv))
+ goto out;
+
+ obj = buf.Pointer;
+
+ if (obj->Type != ACPI_TYPE_PACKAGE) {
+ rv = AE_TYPE;
+ goto out;
+ }
+
+ n = obj->Package.Count;
+
+ if (n == 0) {
+ rv = AE_LIMIT;
+ goto out;
+ }
+
+ aprint_debug_dev(self, "measured devices: ");
+
+ for (i = 0; i < n; i++) {
+
+ elm = &obj->Package.Elements[i];
+ rv = acpi_eval_reference_handle(elm, &hdl);
+
+ if (ACPI_FAILURE(rv))
+ continue;
+
+ ad = acpi_get_node(hdl);
+
+ if (ACPI_FAILURE(rv))
+ continue;
+
+ aprint_debug("%s ", ad->ad_name);
+ }
+
+ aprint_debug("\n");
+
+out:
+ if (ACPI_FAILURE(rv))
+ aprint_debug_dev(self, "failed to evaluate _PMD: %s\n",
+ AcpiFormatException(rv));
+
+ if (buf.Pointer != NULL)
+ ACPI_FREE(buf.Pointer);
+
+ return (rv != AE_OK) ? false : true;
+}
+
+static bool
+acpipmtr_sensor_init(device_t self)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+ const size_t siz = sizeof(sc->sc_sensor_i.desc);
+ int32_t val;
+
+ val = acpipmtr_sensor_get_reading(self);
+ sc->sc_interval = acpipmtr_sensor_get_interval(self);
+
+ if (val < 0) {
+ aprint_error_dev(self, "failed to get sensor reading\n");
+ return false;
+ }
+
+ /* Always mW in ACPI 4.0. */
+ if (sc->sc_cap[ACPIPMTR_CAP_UNIT] != 0)
+ aprint_error_dev(self, "invalid measurement unit\n");
+
+ sc->sc_sme = sysmon_envsys_create();
+
+ sc->sc_sensor_i.units = ENVSYS_SWATTS;
+ sc->sc_sensor_o.units = ENVSYS_SWATTS;
+ sc->sc_sensor_i.value_cur = val * 1000;
+ sc->sc_sensor_o.value_cur = val * 1000;
+
+ acpipmtr_sensor_type(self);
+
+ (void)strlcpy(sc->sc_sensor_i.desc, "input power", siz);
+ (void)strlcpy(sc->sc_sensor_o.desc, "output power", siz);
+
+ sc->sc_sme->sme_cookie = self;
+ sc->sc_sme->sme_flags = SME_POLL_ONLY;
+ sc->sc_sme->sme_name = device_xname(self);
+ sc->sc_sme->sme_refresh = acpipmtr_sensor_refresh;
+
+ if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_i) != 0)
+ goto fail;
+
+ if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_o) != 0)
+ goto fail;
+
+ if (sysmon_envsys_register(sc->sc_sme) != 0)
+ goto fail;
+
+ return true;
+
+fail:
+ aprint_error_dev(self, "failed to initialize sysmon\n");
+
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+
+ return false;
+}
+
+static void
+acpipmtr_sensor_type(device_t self)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+
+ switch (sc->sc_cap[ACPIPMTR_CAP_TYPE]) {
+
+ case ACPIPMTR_POWER_INPUT:
+ sc->sc_sensor_i.state = ENVSYS_SVALID;
+ sc->sc_sensor_o.state = ENVSYS_SINVALID;
+ break;
+
+ case ACPIPMTR_POWER_OUTPUT:
+ sc->sc_sensor_i.state = ENVSYS_SINVALID;
+ sc->sc_sensor_o.state = ENVSYS_SVALID;
+ break;
+
+ default:
+ sc->sc_sensor_i.state = ENVSYS_SINVALID;
+ sc->sc_sensor_o.state = ENVSYS_SINVALID;
+ break;
+ }
+}
+
+static int32_t
+acpipmtr_sensor_get(device_t self, const char *path)
+{
+ struct acpipmtr_softc *sc = device_private(self);
+ ACPI_INTEGER val = 0;
+ ACPI_STATUS rv;
+
+ rv = acpi_eval_integer(sc->sc_node->ad_handle, path, &val);
+
+ if (ACPI_FAILURE(rv))
+ goto fail;
+
+ if (val == 0 || val > INT32_MAX) {
+ rv = AE_LIMIT;
+ goto fail;
+ }
+
+ return val;
+
+fail:
+ aprint_debug_dev(self, "failed to evaluate "
+ "%s: %s\n", path, AcpiFormatException(rv));
+
+ return -1;
+}
+
+static int32_t
+acpipmtr_sensor_get_reading(device_t self)
+{
+ return acpipmtr_sensor_get(self, "_PMM");
+}
+
+static int32_t
+acpipmtr_sensor_get_interval(device_t self)
+{
+ return acpipmtr_sensor_get(self, "_GAI");
+}
+
+static void
+acpipmtr_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+ device_t self = sme->sme_cookie;
+ struct acpipmtr_softc *sc;
+ int32_t val;
+
+ sc = device_private(self);
+
+ sc->sc_sensor_i.state = ENVSYS_SINVALID;
+ sc->sc_sensor_o.state = ENVSYS_SINVALID;
+
+ val = acpipmtr_sensor_get_reading(self) * 1000;
+
+ if (val < 0)
+ return;
+
+ sc->sc_sensor_i.value_cur = val;
+ sc->sc_sensor_o.value_cur = val;
+
+ acpipmtr_sensor_type(self);
+}
+
+static void
+acpipmtr_notify(ACPI_HANDLE hdl, uint32_t evt, void *aux)
+{
+ struct acpipmtr_softc *sc;
+ device_t self = aux;
+ int32_t val;
+
+ sc = device_private(self);
+
+ switch (evt) {
+
+ case ACPIPMTR_NOTIFY_CAP:
+
+ if (acpipmtr_cap_get(self, false) != true)
+ break;
+
+ acpipmtr_sensor_type(self);
+ break;
+
+ case ACPIPMTR_NOTIFY_INTERVAL:
+ val = acpipmtr_sensor_get_interval(self);
+
+ if (val < 0 || val == sc->sc_interval)
+ break;
+
+ aprint_debug_dev(self, "averaging interval changed "
+ "from %u ms to %u ms\n", sc->sc_interval, val);
+
+ sc->sc_interval = val;
+ break;
+
+ case ACPIPMTR_NOTIFY_TRIP: /* AE_SUPPORT */
+ case ACPIPMTR_NOTIFY_HWLIMIT1: /* AE_SUPPORT */
+ case ACPIPMTR_NOTIFY_HWLIMIT2: /* AE_SUPPORT */
+ break;
+
+ default:
+ aprint_debug_dev(self, "unknown notify 0x%02x\n", evt);
+ }
+}
+
+#ifdef _MODULE
+
+MODULE(MODULE_CLASS_DRIVER, acpipmtr, NULL);
+
+#include "ioconf.c"
+
+static int
+acpipmtr_modcmd(modcmd_t cmd, void *context)
+{
+
+ switch (cmd) {
+
+ case MODULE_CMD_INIT:
+ return config_init_component(cfdriver_ioconf_acpipmtr,
+ cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr);
+
+ case MODULE_CMD_FINI:
+ return config_fini_component(cfdriver_ioconf_acpipmtr,
+ cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr);
+
+ default:
+ return ENOTTY;
+ }
+}
+
+#endif /* _MODULE */