Home | History | Annotate | Line # | Download | only in acpi
aibs_acpi.c revision 1.3.2.1
      1 /* $NetBSD: aibs_acpi.c,v 1.3.2.1 2012/10/30 17:20:51 yamt Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jukka Ruohonen.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
     33 /*
     34  * Copyright (c) 2009 Constantine A. Murenin <cnst+netbsd (at) bugmail.mojo.ru>
     35  *
     36  * Permission to use, copy, modify, and distribute this software for any
     37  * purpose with or without fee is hereby granted, provided that the above
     38  * copyright notice and this permission notice appear in all copies.
     39  *
     40  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     41  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     42  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     43  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     44  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     45  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     46  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     47  */
     48 
     49 #include <sys/cdefs.h>
     50 __KERNEL_RCSID(0, "$NetBSD: aibs_acpi.c,v 1.3.2.1 2012/10/30 17:20:51 yamt Exp $");
     51 
     52 #include <sys/param.h>
     53 #include <sys/kmem.h>
     54 #include <sys/module.h>
     55 
     56 #include <dev/acpi/acpireg.h>
     57 #include <dev/acpi/acpivar.h>
     58 
     59 /*
     60  * ASUSTeK AI Booster (ACPI ASOC ATK0110).
     61  *
     62  * This code was originally written for OpenBSD after the techniques
     63  * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
     64  * were verified to be accurate on the actual hardware kindly provided by
     65  * Sam Fourman Jr.  It was subsequently ported from OpenBSD to DragonFly BSD,
     66  * and then to the NetBSD's sysmon_envsys(9) framework.
     67  *
     68  *				  -- Constantine A. Murenin <http://cnst.su/>
     69  */
     70 
     71 #define _COMPONENT		 ACPI_RESOURCE_COMPONENT
     72 ACPI_MODULE_NAME		 ("acpi_aibs")
     73 
     74 #define AIBS_MUX_HWMON		 0x00000006
     75 #define AIBS_MUX_MGMT		 0x00000011
     76 
     77 #define AIBS_TYPE(x)		 (((x) >> 16) & 0xff)
     78 #define AIBS_TYPE_VOLT		 2
     79 #define AIBS_TYPE_TEMP		 3
     80 #define AIBS_TYPE_FAN		 4
     81 
     82 struct aibs_sensor {
     83 	envsys_data_t			 as_sensor;
     84 	uint64_t			 as_type;
     85 	uint64_t			 as_liml;
     86 	uint64_t			 as_limh;
     87 
     88 	SIMPLEQ_ENTRY(aibs_sensor)	 as_list;
     89 };
     90 
     91 struct aibs_softc {
     92 	device_t			 sc_dev;
     93 	struct acpi_devnode		*sc_node;
     94 	struct sysmon_envsys		*sc_sme;
     95 	bool				 sc_model;	/* new model = true */
     96 
     97 	SIMPLEQ_HEAD(, aibs_sensor)	 as_head;
     98 };
     99 
    100 static int	aibs_match(device_t, cfdata_t, void *);
    101 static void	aibs_attach(device_t, device_t, void *);
    102 static int	aibs_detach(device_t, int);
    103 
    104 static void	aibs_init(device_t);
    105 static void	aibs_init_new(device_t);
    106 static void	aibs_init_old(device_t, int);
    107 
    108 static void	aibs_sensor_add(device_t, ACPI_OBJECT *);
    109 static bool	aibs_sensor_value(device_t, struct aibs_sensor *, uint64_t *);
    110 static void	aibs_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
    111 static void	aibs_sensor_limits(struct sysmon_envsys *, envsys_data_t *,
    112 				   sysmon_envsys_lim_t *, uint32_t *);
    113 
    114 CFATTACH_DECL_NEW(aibs, sizeof(struct aibs_softc),
    115     aibs_match, aibs_attach, aibs_detach, NULL);
    116 
    117 static const char* const aibs_hid[] = {
    118 	"ATK0110",
    119 	NULL
    120 };
    121 
    122 static int
    123 aibs_match(device_t parent, cfdata_t match, void *aux)
    124 {
    125 	struct acpi_attach_args *aa = aux;
    126 
    127 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
    128 		return 0;
    129 
    130 	return acpi_match_hid(aa->aa_node->ad_devinfo, aibs_hid);
    131 }
    132 
    133 static void
    134 aibs_attach(device_t parent, device_t self, void *aux)
    135 {
    136 	struct aibs_softc *sc = device_private(self);
    137 	struct acpi_attach_args *aa = aux;
    138 
    139 	sc->sc_dev = self;
    140 	sc->sc_node = aa->aa_node;
    141 
    142 	aprint_naive("\n");
    143 	aprint_normal(": ASUSTeK AI Booster\n");
    144 
    145 	sc->sc_sme = sysmon_envsys_create();
    146 
    147 	sc->sc_sme->sme_cookie = sc;
    148 	sc->sc_sme->sme_name = device_xname(self);
    149 	sc->sc_sme->sme_refresh = aibs_sensor_refresh;
    150 	sc->sc_sme->sme_get_limits = aibs_sensor_limits;
    151 
    152 	aibs_init(self);
    153 	SIMPLEQ_INIT(&sc->as_head);
    154 
    155 	if (sc->sc_model != false)
    156 		aibs_init_new(self);
    157 	else {
    158 		aibs_init_old(self, AIBS_TYPE_FAN);
    159 		aibs_init_old(self, AIBS_TYPE_TEMP);
    160 		aibs_init_old(self, AIBS_TYPE_VOLT);
    161 	}
    162 
    163 	(void)pmf_device_register(self, NULL, NULL);
    164 
    165 	if (sc->sc_sme->sme_nsensors == 0) {
    166 		aprint_error_dev(self, "no sensors found\n");
    167 		sysmon_envsys_destroy(sc->sc_sme);
    168 		sc->sc_sme = NULL;
    169 		return;
    170 	}
    171 
    172 	if (sysmon_envsys_register(sc->sc_sme) != 0)
    173 		aprint_error_dev(self, "failed to register with sysmon\n");
    174 }
    175 
    176 static int
    177 aibs_detach(device_t self, int flags)
    178 {
    179 	struct aibs_softc *sc = device_private(self);
    180 	struct aibs_sensor *as;
    181 
    182 	pmf_device_deregister(self);
    183 
    184 	if (sc->sc_sme != NULL)
    185 		sysmon_envsys_unregister(sc->sc_sme);
    186 
    187 	while (SIMPLEQ_FIRST(&sc->as_head) != NULL) {
    188 		as = SIMPLEQ_FIRST(&sc->as_head);
    189 		SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list);
    190 		kmem_free(as, sizeof(*as));
    191 	}
    192 
    193 	return 0;
    194 }
    195 
    196 static void
    197 aibs_init(device_t self)
    198 {
    199 	struct aibs_softc *sc = device_private(self);
    200 	ACPI_HANDLE tmp;
    201 	ACPI_STATUS rv;
    202 
    203 	/*
    204 	 * Old model uses the tuple { TSIF, VSIF, FSIF } to
    205 	 * enumerate the sensors and { RTMP, RVLT, RFAN }
    206 	 * to obtain the values. New mode uses GGRP for the
    207 	 * enumeration and { GITM, SITM } as accessors.
    208 	 */
    209 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp);
    210 
    211 	if (ACPI_FAILURE(rv)) {
    212 		sc->sc_model = false;
    213 		return;
    214 	}
    215 
    216 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp);
    217 
    218 	if (ACPI_FAILURE(rv)) {
    219 		sc->sc_model = false;
    220 		return;
    221 	}
    222 
    223 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp);
    224 
    225 	if (ACPI_FAILURE(rv)) {
    226 		sc->sc_model = false;
    227 		return;
    228 	}
    229 
    230 	sc->sc_model = true;
    231 
    232 	/*
    233 	 * If both the new and the old methods are present, prefer
    234 	 * the old one; GGRP/GITM may not be functional in this case.
    235 	 */
    236 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "FSIF", &tmp);
    237 
    238 	if (ACPI_FAILURE(rv))
    239 		return;
    240 
    241 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "TSIF", &tmp);
    242 
    243 	if (ACPI_FAILURE(rv))
    244 		return;
    245 
    246 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "VSIF", &tmp);
    247 
    248 	if (ACPI_FAILURE(rv))
    249 		return;
    250 
    251 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RFAN", &tmp);
    252 
    253 	if (ACPI_FAILURE(rv))
    254 		return;
    255 
    256 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RTMP", &tmp);
    257 
    258 	if (ACPI_FAILURE(rv))
    259 		return;
    260 
    261 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RVLT", &tmp);
    262 
    263 	if (ACPI_FAILURE(rv))
    264 		return;
    265 
    266 	sc->sc_model = false;
    267 }
    268 
    269 static void
    270 aibs_init_new(device_t self)
    271 {
    272 	struct aibs_softc *sc = device_private(self);
    273 	ACPI_OBJECT_LIST arg;
    274 	ACPI_OBJECT id, *obj;
    275 	ACPI_BUFFER buf;
    276 	ACPI_STATUS rv;
    277 	uint32_t i, n;
    278 
    279 	arg.Count = 1;
    280 	arg.Pointer = &id;
    281 
    282 	id.Type = ACPI_TYPE_INTEGER;
    283 	id.Integer.Value = AIBS_MUX_HWMON;
    284 
    285 	buf.Pointer = NULL;
    286 	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
    287 
    288 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf);
    289 
    290 	if (ACPI_FAILURE(rv))
    291 		goto out;
    292 
    293 	obj = buf.Pointer;
    294 
    295 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    296 		rv = AE_TYPE;
    297 		goto out;
    298 	}
    299 
    300 	if (obj->Package.Count > UINT32_MAX) {
    301 		rv = AE_AML_NUMERIC_OVERFLOW;
    302 		goto out;
    303 	}
    304 
    305 	n = obj->Package.Count;
    306 
    307 	if (n == 0) {
    308 		rv = AE_NOT_EXIST;
    309 		goto out;
    310 	}
    311 
    312 	for (i = 0; i < n; i++)
    313 		aibs_sensor_add(self, &obj->Package.Elements[i]);
    314 
    315 out:
    316 	if (buf.Pointer != NULL)
    317 		ACPI_FREE(buf.Pointer);
    318 
    319 	if (ACPI_FAILURE(rv)) {
    320 
    321 		aprint_error_dev(self, "failed to evaluate "
    322 		    "GGRP: %s\n", AcpiFormatException(rv));
    323 	}
    324 }
    325 
    326 static void
    327 aibs_init_old(device_t self, int type)
    328 {
    329 	struct aibs_softc *sc = device_private(self);
    330 	char path[] = "?SIF";
    331 	ACPI_OBJECT *elm, *obj;
    332 	ACPI_BUFFER buf;
    333 	ACPI_STATUS rv;
    334 	uint32_t i, n;
    335 
    336 	switch (type) {
    337 
    338 	case AIBS_TYPE_FAN:
    339 		path[0] = 'F';
    340 		break;
    341 
    342 	case AIBS_TYPE_TEMP:
    343 		path[0] = 'T';
    344 		break;
    345 
    346 	case AIBS_TYPE_VOLT:
    347 		path[0] = 'V';
    348 		break;
    349 
    350 	default:
    351 		return;
    352 	}
    353 
    354 	rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf);
    355 
    356 	if (ACPI_FAILURE(rv))
    357 		goto out;
    358 
    359 	obj = buf.Pointer;
    360 
    361 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    362 		rv = AE_TYPE;
    363 		goto out;
    364 	}
    365 
    366 	elm = obj->Package.Elements;
    367 
    368 	if (elm[0].Type != ACPI_TYPE_INTEGER) {
    369 		rv = AE_TYPE;
    370 		goto out;
    371 	}
    372 
    373 	if (elm[0].Integer.Value > UINT32_MAX) {
    374 		rv = AE_AML_NUMERIC_OVERFLOW;
    375 		goto out;
    376 	}
    377 
    378 	n = elm[0].Integer.Value;
    379 
    380 	if (n == 0) {
    381 		rv = AE_NOT_EXIST;
    382 		goto out;
    383 	}
    384 
    385 	if (obj->Package.Count - 1 != n) {
    386 		rv = AE_BAD_VALUE;
    387 		goto out;
    388 	}
    389 
    390 	for (i = 1; i < obj->Package.Count; i++) {
    391 
    392 		if (elm[i].Type != ACPI_TYPE_PACKAGE)
    393 			continue;
    394 
    395 		aibs_sensor_add(self, &elm[i]);
    396 	}
    397 
    398 out:
    399 	if (buf.Pointer != NULL)
    400 		ACPI_FREE(buf.Pointer);
    401 
    402 	if (ACPI_FAILURE(rv)) {
    403 
    404 		aprint_error_dev(self, "failed to evaluate "
    405 		    "%s: %s\n", path, AcpiFormatException(rv));
    406 	}
    407 }
    408 
    409 static void
    410 aibs_sensor_add(device_t self, ACPI_OBJECT *obj)
    411 {
    412 	struct aibs_softc *sc = device_private(self);
    413 	struct aibs_sensor *as;
    414 	int ena, len, lhi, llo;
    415 	const char *name;
    416 	ACPI_STATUS rv;
    417 
    418 	as = NULL;
    419 	rv = AE_OK;
    420 
    421 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    422 		rv = AE_TYPE;
    423 		goto out;
    424 	}
    425 
    426 	/*
    427 	 * The known formats are:
    428 	 *
    429 	 *	index		type		old		new
    430 	 *	-----		----		---		---
    431 	 *	0		integer		flags		flags
    432 	 *	1		string		name		name
    433 	 *	2		integer		limit1		unknown
    434 	 *	3		integer		limit2		unknown
    435 	 *	4		integer		enable		limit1
    436 	 *	5		integer		-		limit2
    437 	 *	6		integer		-		enable
    438 	 */
    439 	if (sc->sc_model != false) {
    440 		len = 7;
    441 		llo = 4;
    442 		lhi = 5;
    443 		ena = 6;
    444 	} else {
    445 		len = 5;
    446 		llo = 2;
    447 		lhi = 3;
    448 		ena = 4;
    449 	}
    450 
    451 	if (obj->Package.Count != (uint32_t)len) {
    452 		rv = AE_LIMIT;
    453 		goto out;
    454 	}
    455 
    456 	if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
    457 	    obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
    458 	    obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER ||
    459 	    obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER ||
    460 	    obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) {
    461 		rv = AE_TYPE;
    462 		goto out;
    463 	}
    464 
    465 	as = kmem_zalloc(sizeof(*as), KM_SLEEP);
    466 
    467 	if (as == NULL) {
    468 		rv = AE_NO_MEMORY;
    469 		goto out;
    470 	}
    471 
    472 	name = obj->Package.Elements[1].String.Pointer;
    473 
    474 	as->as_type = obj->Package.Elements[0].Integer.Value;
    475 	as->as_liml = obj->Package.Elements[llo].Integer.Value;
    476 	as->as_limh = obj->Package.Elements[lhi].Integer.Value;
    477 
    478 	if (sc->sc_model != false)
    479 		as->as_limh += as->as_liml;	/* A range in the new model. */
    480 
    481 	as->as_sensor.state = ENVSYS_SINVALID;
    482 
    483 	switch (AIBS_TYPE(as->as_type)) {
    484 
    485 	case AIBS_TYPE_FAN:
    486 		as->as_sensor.units = ENVSYS_SFANRPM;
    487 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
    488 		break;
    489 
    490 	case AIBS_TYPE_TEMP:
    491 		as->as_sensor.units = ENVSYS_STEMP;
    492 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
    493 		break;
    494 
    495 	case AIBS_TYPE_VOLT:
    496 		as->as_sensor.units = ENVSYS_SVOLTS_DC;
    497 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
    498 		break;
    499 
    500 	default:
    501 		rv = AE_TYPE;
    502 		goto out;
    503 	}
    504 
    505 	(void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
    506 
    507 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
    508 		rv = AE_AML_INTERNAL;
    509 		goto out;
    510 	}
    511 
    512 	SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
    513 
    514 out:
    515 	if (ACPI_FAILURE(rv)) {
    516 
    517 		if (as != NULL)
    518 			kmem_free(as, sizeof(*as));
    519 
    520 		aprint_error_dev(self, "failed to add "
    521 		    "sensor: %s\n",  AcpiFormatException(rv));
    522 	}
    523 }
    524 
    525 static bool
    526 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
    527 {
    528 	struct aibs_softc *sc = device_private(self);
    529 	uint32_t type, *ret, cmb[3];
    530 	ACPI_OBJECT_LIST arg;
    531 	ACPI_OBJECT cmi, tmp;
    532 	ACPI_OBJECT *obj;
    533 	ACPI_BUFFER buf;
    534 	ACPI_STATUS rv;
    535 	const char *path;
    536 
    537 	if (sc->sc_model != false) {
    538 
    539 		path = "GITM";
    540 
    541 		cmb[0] = as->as_type;
    542 		cmb[1] = 0;
    543 		cmb[2] = 0;
    544 
    545 		arg.Count = 1;
    546 		arg.Pointer = &tmp;
    547 
    548 		tmp.Buffer.Length = sizeof(cmb);
    549 		tmp.Buffer.Pointer = (uint8_t *)cmb;
    550 		tmp.Type = type = ACPI_TYPE_BUFFER;
    551 
    552 	} else {
    553 
    554 		arg.Count = 1;
    555 		arg.Pointer = &cmi;
    556 
    557 		cmi.Integer.Value = as->as_type;
    558 		cmi.Type = type = ACPI_TYPE_INTEGER;
    559 
    560 		switch (AIBS_TYPE(as->as_type)) {
    561 
    562 		case AIBS_TYPE_FAN:
    563 			path = "RFAN";
    564 			break;
    565 
    566 		case AIBS_TYPE_TEMP:
    567 			path = "RTMP";
    568 			break;
    569 
    570 		case AIBS_TYPE_VOLT:
    571 			path = "RVLT";
    572 			break;
    573 
    574 		default:
    575 			return false;
    576 		}
    577 	}
    578 
    579 	buf.Pointer = NULL;
    580 	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
    581 
    582 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
    583 
    584 	if (ACPI_FAILURE(rv))
    585 		goto out;
    586 
    587 	obj = buf.Pointer;
    588 
    589 	if (obj->Type != type) {
    590 		rv = AE_TYPE;
    591 		goto out;
    592 	}
    593 
    594 	if (sc->sc_model != true)
    595 		*val = obj->Integer.Value;
    596 	else {
    597 		/*
    598 		 * The return buffer contains at least:
    599 		 *
    600 		 *	uint32_t buf[0]	 flags
    601 		 *	uint32_t buf[1]	 return value
    602 		 *	uint8_t  buf[2-] unknown
    603 		 */
    604 		if (obj->Buffer.Length < 8) {
    605 			rv = AE_BUFFER_OVERFLOW;
    606 			goto out;
    607 		}
    608 
    609 		ret = (uint32_t *)obj->Buffer.Pointer;
    610 
    611 		if (ret[0] == 0) {
    612 			rv = AE_BAD_VALUE;
    613 			goto out;
    614 		}
    615 
    616 		*val = ret[1];
    617 	}
    618 
    619 out:
    620 	if (buf.Pointer != NULL)
    621 		ACPI_FREE(buf.Pointer);
    622 
    623 	if (ACPI_FAILURE(rv)) {
    624 
    625 		aprint_error_dev(self, "failed to evaluate "
    626 		    "%s: %s\n", path, AcpiFormatException(rv));
    627 
    628 		return false;
    629 	}
    630 
    631 	return true;
    632 }
    633 
    634 static void
    635 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    636 {
    637 	struct aibs_softc *sc = sme->sme_cookie;
    638 	struct aibs_sensor *tmp, *as = NULL;
    639 	envsys_data_t *s = edata;
    640 	uint64_t val = 0;
    641 
    642 	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
    643 
    644 		if (tmp->as_sensor.sensor == s->sensor) {
    645 			as = tmp;
    646 			break;
    647 		}
    648 	}
    649 
    650 	if (as == NULL) {
    651 		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
    652 		return;
    653 	}
    654 
    655 	as->as_sensor.state = ENVSYS_SINVALID;
    656 	as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
    657 
    658 	if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
    659 		return;
    660 
    661 	switch (as->as_sensor.units) {
    662 
    663 	case ENVSYS_SFANRPM:
    664 		as->as_sensor.value_cur = val;
    665 		break;
    666 
    667 	case ENVSYS_STEMP:
    668 
    669 		if (val == 0)
    670 			return;
    671 
    672 		as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
    673 		break;
    674 
    675 	case ENVSYS_SVOLTS_DC:
    676 		as->as_sensor.value_cur = val * 1000;
    677 		break;
    678 
    679 	default:
    680 		return;
    681 	}
    682 
    683 	as->as_sensor.state = ENVSYS_SVALID;
    684 	as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
    685 }
    686 
    687 static void
    688 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    689     sysmon_envsys_lim_t *limits, uint32_t *props)
    690 {
    691 	struct aibs_softc *sc = sme->sme_cookie;
    692 	struct aibs_sensor *tmp, *as = NULL;
    693 	sysmon_envsys_lim_t *lim = limits;
    694 	envsys_data_t *s = edata;
    695 
    696 	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
    697 
    698 		if (tmp->as_sensor.sensor == s->sensor) {
    699 			as = tmp;
    700 			break;
    701 		}
    702 	}
    703 
    704 	if (as == NULL) {
    705 		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
    706 		return;
    707 	}
    708 
    709 	switch (as->as_sensor.units) {
    710 
    711 	case ENVSYS_SFANRPM:
    712 
    713 		/*
    714 		 * Some boards have strange limits for fans.
    715 		 */
    716 		if (as->as_liml == 0) {
    717 			lim->sel_warnmin = as->as_limh;
    718 			*props = PROP_WARNMIN;
    719 
    720 		} else {
    721 			lim->sel_warnmin = as->as_liml;
    722 			lim->sel_warnmax = as->as_limh;
    723 			*props = PROP_WARNMIN | PROP_WARNMAX;
    724 		}
    725 
    726 		break;
    727 
    728 	case ENVSYS_STEMP:
    729 		lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
    730 		lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
    731 
    732 		*props = PROP_CRITMAX | PROP_WARNMAX;
    733 		break;
    734 
    735 	case ENVSYS_SVOLTS_DC:
    736 		lim->sel_critmin = as->as_liml * 1000;
    737 		lim->sel_critmax = as->as_limh * 1000;
    738 		*props = PROP_CRITMIN | PROP_CRITMAX;
    739 		break;
    740 
    741 	default:
    742 		return;
    743 	}
    744 }
    745 
    746 MODULE(MODULE_CLASS_DRIVER, aibs, NULL);
    747 
    748 #ifdef _MODULE
    749 #include "ioconf.c"
    750 #endif
    751 
    752 static int
    753 aibs_modcmd(modcmd_t cmd, void *aux)
    754 {
    755 	int rv = 0;
    756 
    757 	switch (cmd) {
    758 
    759 	case MODULE_CMD_INIT:
    760 
    761 #ifdef _MODULE
    762 		rv = config_init_component(cfdriver_ioconf_aibs,
    763 		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
    764 #endif
    765 		break;
    766 
    767 	case MODULE_CMD_FINI:
    768 
    769 #ifdef _MODULE
    770 		rv = config_fini_component(cfdriver_ioconf_aibs,
    771 		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
    772 #endif
    773 		break;
    774 
    775 	default:
    776 		rv = ENOTTY;
    777 	}
    778 
    779 	return rv;
    780 }
    781