4f0c2c3f04
thinkpad(4), and aps(4).
781 lines
16 KiB
C
781 lines
16 KiB
C
/* $NetBSD: aibs_acpi.c,v 1.4 2012/08/14 14:36:43 jruoho Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2011 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Jukka Ruohonen.
|
|
*
|
|
* 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 NETBSD FOUNDATION, INC. 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.
|
|
*/
|
|
|
|
/* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */
|
|
/*
|
|
* Copyright (c) 2009 Constantine A. Murenin <cnst+netbsd@bugmail.mojo.ru>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: aibs_acpi.c,v 1.4 2012/08/14 14:36:43 jruoho Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kmem.h>
|
|
#include <sys/module.h>
|
|
|
|
#include <dev/acpi/acpireg.h>
|
|
#include <dev/acpi/acpivar.h>
|
|
|
|
/*
|
|
* ASUSTeK AI Booster (ACPI ASOC ATK0110).
|
|
*
|
|
* This code was originally written for OpenBSD after the techniques
|
|
* described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
|
|
* were verified to be accurate on the actual hardware kindly provided by
|
|
* Sam Fourman Jr. It was subsequently ported from OpenBSD to DragonFly BSD,
|
|
* and then to the NetBSD's sysmon_envsys(9) framework.
|
|
*
|
|
* -- Constantine A. Murenin <http://cnst.su/>
|
|
*/
|
|
|
|
#define _COMPONENT ACPI_RESOURCE_COMPONENT
|
|
ACPI_MODULE_NAME ("acpi_aibs")
|
|
|
|
#define AIBS_MUX_HWMON 0x00000006
|
|
#define AIBS_MUX_MGMT 0x00000011
|
|
|
|
#define AIBS_TYPE(x) (((x) >> 16) & 0xff)
|
|
#define AIBS_TYPE_VOLT 2
|
|
#define AIBS_TYPE_TEMP 3
|
|
#define AIBS_TYPE_FAN 4
|
|
|
|
struct aibs_sensor {
|
|
envsys_data_t as_sensor;
|
|
uint64_t as_type;
|
|
uint64_t as_liml;
|
|
uint64_t as_limh;
|
|
|
|
SIMPLEQ_ENTRY(aibs_sensor) as_list;
|
|
};
|
|
|
|
struct aibs_softc {
|
|
device_t sc_dev;
|
|
struct acpi_devnode *sc_node;
|
|
struct sysmon_envsys *sc_sme;
|
|
bool sc_model; /* new model = true */
|
|
|
|
SIMPLEQ_HEAD(, aibs_sensor) as_head;
|
|
};
|
|
|
|
static int aibs_match(device_t, cfdata_t, void *);
|
|
static void aibs_attach(device_t, device_t, void *);
|
|
static int aibs_detach(device_t, int);
|
|
|
|
static void aibs_init(device_t);
|
|
static void aibs_init_new(device_t);
|
|
static void aibs_init_old(device_t, int);
|
|
|
|
static void aibs_sensor_add(device_t, ACPI_OBJECT *);
|
|
static bool aibs_sensor_value(device_t, struct aibs_sensor *, uint64_t *);
|
|
static void aibs_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
|
|
static void aibs_sensor_limits(struct sysmon_envsys *, envsys_data_t *,
|
|
sysmon_envsys_lim_t *, uint32_t *);
|
|
|
|
CFATTACH_DECL_NEW(aibs, sizeof(struct aibs_softc),
|
|
aibs_match, aibs_attach, aibs_detach, NULL);
|
|
|
|
static const char* const aibs_hid[] = {
|
|
"ATK0110",
|
|
NULL
|
|
};
|
|
|
|
static int
|
|
aibs_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, aibs_hid);
|
|
}
|
|
|
|
static void
|
|
aibs_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
struct acpi_attach_args *aa = aux;
|
|
|
|
sc->sc_dev = self;
|
|
sc->sc_node = aa->aa_node;
|
|
|
|
aprint_naive("\n");
|
|
aprint_normal(": ASUSTeK AI Booster\n");
|
|
|
|
sc->sc_sme = sysmon_envsys_create();
|
|
|
|
sc->sc_sme->sme_cookie = sc;
|
|
sc->sc_sme->sme_name = device_xname(self);
|
|
sc->sc_sme->sme_refresh = aibs_sensor_refresh;
|
|
sc->sc_sme->sme_get_limits = aibs_sensor_limits;
|
|
|
|
aibs_init(self);
|
|
SIMPLEQ_INIT(&sc->as_head);
|
|
|
|
if (sc->sc_model != false)
|
|
aibs_init_new(self);
|
|
else {
|
|
aibs_init_old(self, AIBS_TYPE_FAN);
|
|
aibs_init_old(self, AIBS_TYPE_TEMP);
|
|
aibs_init_old(self, AIBS_TYPE_VOLT);
|
|
}
|
|
|
|
(void)pmf_device_register(self, NULL, NULL);
|
|
|
|
if (sc->sc_sme->sme_nsensors == 0) {
|
|
aprint_error_dev(self, "no sensors found\n");
|
|
sysmon_envsys_destroy(sc->sc_sme);
|
|
sc->sc_sme = NULL;
|
|
return;
|
|
}
|
|
|
|
if (sysmon_envsys_register(sc->sc_sme) != 0)
|
|
aprint_error_dev(self, "failed to register with sysmon\n");
|
|
}
|
|
|
|
static int
|
|
aibs_detach(device_t self, int flags)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
struct aibs_sensor *as;
|
|
|
|
pmf_device_deregister(self);
|
|
|
|
if (sc->sc_sme != NULL)
|
|
sysmon_envsys_unregister(sc->sc_sme);
|
|
|
|
while (SIMPLEQ_FIRST(&sc->as_head) != NULL) {
|
|
as = SIMPLEQ_FIRST(&sc->as_head);
|
|
SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list);
|
|
kmem_free(as, sizeof(*as));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
aibs_init(device_t self)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
ACPI_HANDLE tmp;
|
|
ACPI_STATUS rv;
|
|
|
|
/*
|
|
* Old model uses the tuple { TSIF, VSIF, FSIF } to
|
|
* enumerate the sensors and { RTMP, RVLT, RFAN }
|
|
* to obtain the values. New mode uses GGRP for the
|
|
* enumeration and { GITM, SITM } as accessors.
|
|
*/
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
sc->sc_model = false;
|
|
return;
|
|
}
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
sc->sc_model = false;
|
|
return;
|
|
}
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
sc->sc_model = false;
|
|
return;
|
|
}
|
|
|
|
sc->sc_model = true;
|
|
|
|
/*
|
|
* If both the new and the old methods are present, prefer
|
|
* the old one; GGRP/GITM may not be functional in this case.
|
|
*/
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "FSIF", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "TSIF", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "VSIF", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "RFAN", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "RTMP", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
rv = AcpiGetHandle(sc->sc_node->ad_handle, "RVLT", &tmp);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
return;
|
|
|
|
sc->sc_model = false;
|
|
}
|
|
|
|
static void
|
|
aibs_init_new(device_t self)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
ACPI_OBJECT_LIST arg;
|
|
ACPI_OBJECT id, *obj;
|
|
ACPI_BUFFER buf;
|
|
ACPI_STATUS rv;
|
|
uint32_t i, n;
|
|
|
|
arg.Count = 1;
|
|
arg.Pointer = &id;
|
|
|
|
id.Type = ACPI_TYPE_INTEGER;
|
|
id.Integer.Value = AIBS_MUX_HWMON;
|
|
|
|
buf.Pointer = NULL;
|
|
buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
|
|
|
|
rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
goto out;
|
|
|
|
obj = buf.Pointer;
|
|
|
|
if (obj->Type != ACPI_TYPE_PACKAGE) {
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
if (obj->Package.Count > UINT32_MAX) {
|
|
rv = AE_AML_NUMERIC_OVERFLOW;
|
|
goto out;
|
|
}
|
|
|
|
n = obj->Package.Count;
|
|
|
|
if (n == 0) {
|
|
rv = AE_NOT_EXIST;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
aibs_sensor_add(self, &obj->Package.Elements[i]);
|
|
|
|
out:
|
|
if (buf.Pointer != NULL)
|
|
ACPI_FREE(buf.Pointer);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
|
|
aprint_error_dev(self, "failed to evaluate "
|
|
"GGRP: %s\n", AcpiFormatException(rv));
|
|
}
|
|
}
|
|
|
|
static void
|
|
aibs_init_old(device_t self, int type)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
char path[] = "?SIF";
|
|
ACPI_OBJECT *elm, *obj;
|
|
ACPI_BUFFER buf;
|
|
ACPI_STATUS rv;
|
|
uint32_t i, n;
|
|
|
|
switch (type) {
|
|
|
|
case AIBS_TYPE_FAN:
|
|
path[0] = 'F';
|
|
break;
|
|
|
|
case AIBS_TYPE_TEMP:
|
|
path[0] = 'T';
|
|
break;
|
|
|
|
case AIBS_TYPE_VOLT:
|
|
path[0] = 'V';
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &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 (elm[0].Type != ACPI_TYPE_INTEGER) {
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
if (elm[0].Integer.Value > UINT32_MAX) {
|
|
rv = AE_AML_NUMERIC_OVERFLOW;
|
|
goto out;
|
|
}
|
|
|
|
n = elm[0].Integer.Value;
|
|
|
|
if (n == 0) {
|
|
rv = AE_NOT_EXIST;
|
|
goto out;
|
|
}
|
|
|
|
if (obj->Package.Count - 1 != n) {
|
|
rv = AE_BAD_VALUE;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 1; i < obj->Package.Count; i++) {
|
|
|
|
if (elm[i].Type != ACPI_TYPE_PACKAGE)
|
|
continue;
|
|
|
|
aibs_sensor_add(self, &elm[i]);
|
|
}
|
|
|
|
out:
|
|
if (buf.Pointer != NULL)
|
|
ACPI_FREE(buf.Pointer);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
|
|
aprint_error_dev(self, "failed to evaluate "
|
|
"%s: %s\n", path, AcpiFormatException(rv));
|
|
}
|
|
}
|
|
|
|
static void
|
|
aibs_sensor_add(device_t self, ACPI_OBJECT *obj)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
struct aibs_sensor *as;
|
|
int ena, len, lhi, llo;
|
|
const char *name;
|
|
ACPI_STATUS rv;
|
|
|
|
as = NULL;
|
|
rv = AE_OK;
|
|
|
|
if (obj->Type != ACPI_TYPE_PACKAGE) {
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* The known formats are:
|
|
*
|
|
* index type old new
|
|
* ----- ---- --- ---
|
|
* 0 integer flags flags
|
|
* 1 string name name
|
|
* 2 integer limit1 unknown
|
|
* 3 integer limit2 unknown
|
|
* 4 integer enable limit1
|
|
* 5 integer - limit2
|
|
* 6 integer - enable
|
|
*/
|
|
if (sc->sc_model != false) {
|
|
len = 7;
|
|
llo = 4;
|
|
lhi = 5;
|
|
ena = 6;
|
|
} else {
|
|
len = 5;
|
|
llo = 2;
|
|
lhi = 3;
|
|
ena = 4;
|
|
}
|
|
|
|
if (obj->Package.Count != (uint32_t)len) {
|
|
rv = AE_LIMIT;
|
|
goto out;
|
|
}
|
|
|
|
if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
|
|
obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) {
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
as = kmem_zalloc(sizeof(*as), KM_SLEEP);
|
|
|
|
if (as == NULL) {
|
|
rv = AE_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
name = obj->Package.Elements[1].String.Pointer;
|
|
|
|
as->as_type = obj->Package.Elements[0].Integer.Value;
|
|
as->as_liml = obj->Package.Elements[llo].Integer.Value;
|
|
as->as_limh = obj->Package.Elements[lhi].Integer.Value;
|
|
|
|
if (sc->sc_model != false)
|
|
as->as_limh += as->as_liml; /* A range in the new model. */
|
|
|
|
as->as_sensor.state = ENVSYS_SINVALID;
|
|
|
|
switch (AIBS_TYPE(as->as_type)) {
|
|
|
|
case AIBS_TYPE_FAN:
|
|
as->as_sensor.units = ENVSYS_SFANRPM;
|
|
as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
|
|
break;
|
|
|
|
case AIBS_TYPE_TEMP:
|
|
as->as_sensor.units = ENVSYS_STEMP;
|
|
as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
|
|
break;
|
|
|
|
case AIBS_TYPE_VOLT:
|
|
as->as_sensor.units = ENVSYS_SVOLTS_DC;
|
|
as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
|
|
break;
|
|
|
|
default:
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
(void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
|
|
|
|
if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
|
|
rv = AE_AML_INTERNAL;
|
|
goto out;
|
|
}
|
|
|
|
SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
|
|
|
|
out:
|
|
if (ACPI_FAILURE(rv)) {
|
|
|
|
if (as != NULL)
|
|
kmem_free(as, sizeof(*as));
|
|
|
|
aprint_error_dev(self, "failed to add "
|
|
"sensor: %s\n", AcpiFormatException(rv));
|
|
}
|
|
}
|
|
|
|
static bool
|
|
aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
|
|
{
|
|
struct aibs_softc *sc = device_private(self);
|
|
uint32_t type, *ret, cmb[3];
|
|
ACPI_OBJECT_LIST arg;
|
|
ACPI_OBJECT cmi, tmp;
|
|
ACPI_OBJECT *obj;
|
|
ACPI_BUFFER buf;
|
|
ACPI_STATUS rv;
|
|
const char *path;
|
|
|
|
if (sc->sc_model != false) {
|
|
|
|
path = "GITM";
|
|
|
|
cmb[0] = as->as_type;
|
|
cmb[1] = 0;
|
|
cmb[2] = 0;
|
|
|
|
arg.Count = 1;
|
|
arg.Pointer = &tmp;
|
|
|
|
tmp.Buffer.Length = sizeof(cmb);
|
|
tmp.Buffer.Pointer = (uint8_t *)cmb;
|
|
tmp.Type = type = ACPI_TYPE_BUFFER;
|
|
|
|
} else {
|
|
|
|
arg.Count = 1;
|
|
arg.Pointer = &cmi;
|
|
|
|
cmi.Integer.Value = as->as_type;
|
|
cmi.Type = type = ACPI_TYPE_INTEGER;
|
|
|
|
switch (AIBS_TYPE(as->as_type)) {
|
|
|
|
case AIBS_TYPE_FAN:
|
|
path = "RFAN";
|
|
break;
|
|
|
|
case AIBS_TYPE_TEMP:
|
|
path = "RTMP";
|
|
break;
|
|
|
|
case AIBS_TYPE_VOLT:
|
|
path = "RVLT";
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
buf.Pointer = NULL;
|
|
buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
|
|
|
|
rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
|
|
|
|
if (ACPI_FAILURE(rv))
|
|
goto out;
|
|
|
|
obj = buf.Pointer;
|
|
|
|
if (obj->Type != type) {
|
|
rv = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
if (sc->sc_model != true)
|
|
*val = obj->Integer.Value;
|
|
else {
|
|
/*
|
|
* The return buffer contains at least:
|
|
*
|
|
* uint32_t buf[0] flags
|
|
* uint32_t buf[1] return value
|
|
* uint8_t buf[2-] unknown
|
|
*/
|
|
if (obj->Buffer.Length < 8) {
|
|
rv = AE_BUFFER_OVERFLOW;
|
|
goto out;
|
|
}
|
|
|
|
ret = (uint32_t *)obj->Buffer.Pointer;
|
|
|
|
if (ret[0] == 0) {
|
|
rv = AE_BAD_VALUE;
|
|
goto out;
|
|
}
|
|
|
|
*val = ret[1];
|
|
}
|
|
|
|
out:
|
|
if (buf.Pointer != NULL)
|
|
ACPI_FREE(buf.Pointer);
|
|
|
|
if (ACPI_FAILURE(rv)) {
|
|
|
|
aprint_error_dev(self, "failed to evaluate "
|
|
"%s: %s\n", path, AcpiFormatException(rv));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
|
|
{
|
|
struct aibs_softc *sc = sme->sme_cookie;
|
|
struct aibs_sensor *tmp, *as = NULL;
|
|
envsys_data_t *s = edata;
|
|
uint64_t val = 0;
|
|
|
|
SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
|
|
|
|
if (tmp->as_sensor.sensor == s->sensor) {
|
|
as = tmp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (as == NULL) {
|
|
aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
|
|
return;
|
|
}
|
|
|
|
as->as_sensor.state = ENVSYS_SINVALID;
|
|
as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
|
|
|
|
if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
|
|
return;
|
|
|
|
switch (as->as_sensor.units) {
|
|
|
|
case ENVSYS_SFANRPM:
|
|
as->as_sensor.value_cur = val;
|
|
break;
|
|
|
|
case ENVSYS_STEMP:
|
|
|
|
if (val == 0)
|
|
return;
|
|
|
|
as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
|
|
break;
|
|
|
|
case ENVSYS_SVOLTS_DC:
|
|
as->as_sensor.value_cur = val * 1000;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
as->as_sensor.state = ENVSYS_SVALID;
|
|
as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
|
|
}
|
|
|
|
static void
|
|
aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
|
|
sysmon_envsys_lim_t *limits, uint32_t *props)
|
|
{
|
|
struct aibs_softc *sc = sme->sme_cookie;
|
|
struct aibs_sensor *tmp, *as = NULL;
|
|
sysmon_envsys_lim_t *lim = limits;
|
|
envsys_data_t *s = edata;
|
|
|
|
SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
|
|
|
|
if (tmp->as_sensor.sensor == s->sensor) {
|
|
as = tmp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (as == NULL) {
|
|
aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
|
|
return;
|
|
}
|
|
|
|
switch (as->as_sensor.units) {
|
|
|
|
case ENVSYS_SFANRPM:
|
|
|
|
/*
|
|
* Some boards have strange limits for fans.
|
|
*/
|
|
if (as->as_liml == 0) {
|
|
lim->sel_warnmin = as->as_limh;
|
|
*props = PROP_WARNMIN;
|
|
|
|
} else {
|
|
lim->sel_warnmin = as->as_liml;
|
|
lim->sel_warnmax = as->as_limh;
|
|
*props = PROP_WARNMIN | PROP_WARNMAX;
|
|
}
|
|
|
|
break;
|
|
|
|
case ENVSYS_STEMP:
|
|
lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
|
|
lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
|
|
|
|
*props = PROP_CRITMAX | PROP_WARNMAX;
|
|
break;
|
|
|
|
case ENVSYS_SVOLTS_DC:
|
|
lim->sel_critmin = as->as_liml * 1000;
|
|
lim->sel_critmax = as->as_limh * 1000;
|
|
*props = PROP_CRITMIN | PROP_CRITMAX;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
MODULE(MODULE_CLASS_DRIVER, aibs, NULL);
|
|
|
|
#ifdef _MODULE
|
|
#include "ioconf.c"
|
|
#endif
|
|
|
|
static int
|
|
aibs_modcmd(modcmd_t cmd, void *aux)
|
|
{
|
|
int rv = 0;
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
|
|
|
#ifdef _MODULE
|
|
rv = config_init_component(cfdriver_ioconf_aibs,
|
|
cfattach_ioconf_aibs, cfdata_ioconf_aibs);
|
|
#endif
|
|
break;
|
|
|
|
case MODULE_CMD_FINI:
|
|
|
|
#ifdef _MODULE
|
|
rv = config_fini_component(cfdriver_ioconf_aibs,
|
|
cfattach_ioconf_aibs, cfdata_ioconf_aibs);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
rv = ENOTTY;
|
|
}
|
|
|
|
return rv;
|
|
}
|