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 <jruoho...@iki.fi> +.\" 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 jruoho...@iki.fi +.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 <jruoho...@iki.fi> + * 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 */