From e2127336b1625beadea18365d59cee26bb805bae Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sun, 28 Jun 2015 21:12:34 +0200
Subject: [PATCH] WIP ACPI Power Meter 20150628

---
 sys/dev/acpica/Makefile      |   2 +-
 sys/dev/acpica/acpi_pmeter.c | 221 +++++++++++++++++++++++++++++++++++++++++++
 sys/dev/acpica/acpivar.h     |   1 +
 3 files changed, 223 insertions(+), 1 deletion(-)
 create mode 100644 sys/dev/acpica/acpi_pmeter.c

diff --git a/sys/dev/acpica/Makefile b/sys/dev/acpica/Makefile
index 434192a..956600b 100644
--- a/sys/dev/acpica/Makefile
+++ b/sys/dev/acpica/Makefile
@@ -72,7 +72,7 @@ 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
 SRCS+=	acpi_pci.c acpi_pcib.c acpi_pcib_acpi.c acpi_pcib_pci.c
-SRCS+=	acpi_pci_link.c
+SRCS+=	acpi_pci_link.c acpi_pmeter.c
 SRCS+=  acpi_powerres.c acpi_quirk.c acpi_resource.c acpi_thermal.c
 SRCS+=  acpi_timer.c acpi_hpet.c acpi_if.c acpi_wmi_if.c
 SRCS+=  OsdCache.c OsdDebug.c
diff --git a/sys/dev/acpica/acpi_pmeter.c b/sys/dev/acpica/acpi_pmeter.c
new file mode 100644
index 0000000..2f8ed5a
--- /dev/null
+++ b/sys/dev/acpica/acpi_pmeter.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2015 The DragonFly Project.  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.
+ * 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/bus.h>
+#include <sys/module.h>
+
+#include "acpi.h"
+
+#include <dev/acpica/acpivar.h>
+
+/* Hooks for the ACPICA debugging infrastructure */
+#define _COMPONENT	ACPI_PMETER
+ACPI_MODULE_NAME("PMETER")
+
+struct	acpi_pmeter_capabilities {
+	uint32_t	pmc_caps;
+	uint32_t	pmc_units;
+	uint32_t	pmc_type;
+	uint32_t	pmc_accuracy;
+	uint32_t	pmc_sampling_time;
+	uint32_t	pmc_min_avg_intvl;
+	uint32_t	pmc_max_avg_intvl;
+	uint32_t	pmc_hysteresis;
+	uint32_t	pmc_hwlim_cfg;
+	uint32_t	pmc_min_hwlim;
+	uint32_t	pmc_max_hwlim;
+};
+
+struct	acpi_pmeter_softc {
+	device_t	pm_dev;
+	ACPI_HANDLE	pm_handle;
+
+	/* The following fields are according to table 10-258 (5.1 spec.) */
+	uint32_t	pm_caps;
+#define	PM_CAP_MEASUREMENT	(1 << 0);	/* support measurement */
+#define	PM_CAP_TRIP_POINTS	(1 << 1);	/* trip points */
+#define	PM_CAP_HARDWARE_LIMIT	(1 << 2);	/* h/w enforced limit */
+#define	PM_CAP_NOTIFY_LIMIT	(1 << 3);	/* h/w limit notify */
+#define	PM_CAP_DISCHARGE_ONLY	(1 << 8);	/* data only if discharging */
+	uint32_t	pm_meas_unit;
+#define	PM_UNIT_MW		0x00000000;
+	uint32_t	pm_meas_type;
+#define	PM_TYPE_INPUT_POWER	0x00000000;	/* measures input power */
+#define	PM_TYPE_OUTPUT_POWER	0x00000001;	/* measures output power */
+	uint32_t	pm_meas_accuracy;
+	uint32_t	pm_meas_sampling_time;
+#define	PM_SMPLTIME_UNAVAILABLE	0xFFFFFFFF;
+	uint32_t	pm_avgtime_min;
+	uint32_t	pm_avgtime_max;
+	uint32_t	pm_hys_margin;
+#define	PM_HYSMARG_UNAVAILABLE	0xFFFFFFFF;
+};
+
+static int	acpi_pmeter_probe(device_t);
+static int	acpi_pmeter_attach(device_t);
+
+static device_method_t acpi_pmeter_methods[] = {
+    /* Device interface */
+    DEVMETHOD(device_probe,	acpi_pmeter_probe),
+    DEVMETHOD(device_attach,	acpi_pmeter_attach),
+
+    DEVMETHOD_END
+};
+
+static driver_t acpi_pmeter_driver = {
+    "acpi_pmeter",
+    acpi_pmeter_methods,
+    sizeof(struct acpi_pmeter_softc),
+};
+
+static devclass_t acpi_pmeter_devclass;
+DRIVER_MODULE(acpi_pmeter, acpi, acpi_pmeter_driver, acpi_pmeter_devclass,
+    NULL, NULL);
+MODULE_DEPEND(acpi_pmeter, acpi, 1, 1, 1);
+
+static int
+acpi_pmeter_probe(device_t dev)
+{
+    static char *pmeter_ids[] = { "ACPI000D", NULL };
+
+    if (acpi_disabled("powermeter") ||
+	ACPI_ID_PROBE(device_get_parent(dev), dev, pmeter_ids) == NULL)
+	return (ENXIO);
+
+    device_set_desc(dev, "Power Meter");
+    return (0);
+}
+
+static int
+acpi_pmeter_attach(device_t dev)
+{
+	struct acpi_pmeter_softc *sc;
+	ACPI_BUFFER buf;
+	ACPI_STATUS status;
+	ACPI_OBJECT *obj;
+
+	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+	sc = device_get_softc(dev);
+	sc->pm_dev = dev;
+	sc->pm_handle = acpi_get_handle(dev);
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_PMC", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _PMC package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_PTP", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _PTP package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_PMM", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _PMM package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_PAI", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _PAI package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_GAI", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _GAI package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_SHL", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _SHL package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_GHL", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _GHL package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	status = AcpiEvaluateObject(sc->pm_handle, "_PMD", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		device_printf(dev, "can't get _PMD package - %s\n",
+		    AcpiFormatException(status));
+	} else {
+		obj = (ACPI_OBJECT *)buf.Pointer;
+		AcpiOsFree(obj);
+	}
+
+	return (0);
+}
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 6b4702b..a74a28c 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -184,6 +184,7 @@ extern struct lock acpi_lock;
 #define	ACPI_THERMAL		0x01000000
 #define	ACPI_TIMER		0x02000000
 #define	ACPI_OEM		0x04000000
+#define	ACPI_PMETER		0x08000000
 
 /*
  * Constants for different interrupt models used with acpi_SetIntrModel().
-- 
2.3.7

