xref: /netbsd-src/sys/dev/acpi/aibs_acpi.c (revision 3772555307d774424243e6d1cdc66aa160602c26)
1 /* $NetBSD: aibs_acpi.c,v 1.7 2021/01/29 15:49:55 thorpej 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@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.7 2021/01/29 15:49:55 thorpej 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 struct device_compatible_entry compat_data[] = {
118 	{ .compat = "ATK0110" },
119 	DEVICE_COMPAT_EOL
120 };
121 
122 static int
aibs_match(device_t parent,cfdata_t match,void * aux)123 aibs_match(device_t parent, cfdata_t match, void *aux)
124 {
125 	struct acpi_attach_args *aa = aux;
126 
127 	return acpi_compatible_match(aa, compat_data);
128 }
129 
130 static void
aibs_attach(device_t parent,device_t self,void * aux)131 aibs_attach(device_t parent, device_t self, void *aux)
132 {
133 	struct aibs_softc *sc = device_private(self);
134 	struct acpi_attach_args *aa = aux;
135 
136 	sc->sc_dev = self;
137 	sc->sc_node = aa->aa_node;
138 
139 	aprint_naive("\n");
140 	aprint_normal(": ASUSTeK AI Booster\n");
141 
142 	sc->sc_sme = sysmon_envsys_create();
143 
144 	sc->sc_sme->sme_cookie = sc;
145 	sc->sc_sme->sme_name = device_xname(self);
146 	sc->sc_sme->sme_refresh = aibs_sensor_refresh;
147 	sc->sc_sme->sme_get_limits = aibs_sensor_limits;
148 
149 	aibs_init(self);
150 	SIMPLEQ_INIT(&sc->as_head);
151 
152 	if (sc->sc_model != false)
153 		aibs_init_new(self);
154 	else {
155 		aibs_init_old(self, AIBS_TYPE_FAN);
156 		aibs_init_old(self, AIBS_TYPE_TEMP);
157 		aibs_init_old(self, AIBS_TYPE_VOLT);
158 	}
159 
160 	(void)pmf_device_register(self, NULL, NULL);
161 
162 	if (sc->sc_sme->sme_nsensors == 0) {
163 		aprint_error_dev(self, "no sensors found\n");
164 		sysmon_envsys_destroy(sc->sc_sme);
165 		sc->sc_sme = NULL;
166 		return;
167 	}
168 
169 	if (sysmon_envsys_register(sc->sc_sme) != 0)
170 		aprint_error_dev(self, "failed to register with sysmon\n");
171 }
172 
173 static int
aibs_detach(device_t self,int flags)174 aibs_detach(device_t self, int flags)
175 {
176 	struct aibs_softc *sc = device_private(self);
177 	struct aibs_sensor *as;
178 
179 	pmf_device_deregister(self);
180 
181 	if (sc->sc_sme != NULL)
182 		sysmon_envsys_unregister(sc->sc_sme);
183 
184 	while (SIMPLEQ_FIRST(&sc->as_head) != NULL) {
185 		as = SIMPLEQ_FIRST(&sc->as_head);
186 		SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list);
187 		kmem_free(as, sizeof(*as));
188 	}
189 
190 	return 0;
191 }
192 
193 static void
aibs_init(device_t self)194 aibs_init(device_t self)
195 {
196 	struct aibs_softc *sc = device_private(self);
197 	ACPI_HANDLE tmp;
198 	ACPI_STATUS rv;
199 
200 	/*
201 	 * Old model uses the tuple { TSIF, VSIF, FSIF } to
202 	 * enumerate the sensors and { RTMP, RVLT, RFAN }
203 	 * to obtain the values. New mode uses GGRP for the
204 	 * enumeration and { GITM, SITM } as accessors.
205 	 */
206 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp);
207 
208 	if (ACPI_FAILURE(rv)) {
209 		sc->sc_model = false;
210 		return;
211 	}
212 
213 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp);
214 
215 	if (ACPI_FAILURE(rv)) {
216 		sc->sc_model = false;
217 		return;
218 	}
219 
220 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp);
221 
222 	if (ACPI_FAILURE(rv)) {
223 		sc->sc_model = false;
224 		return;
225 	}
226 
227 	sc->sc_model = true;
228 
229 	/*
230 	 * If both the new and the old methods are present, prefer
231 	 * the old one; GGRP/GITM may not be functional in this case.
232 	 */
233 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "FSIF", &tmp);
234 
235 	if (ACPI_FAILURE(rv))
236 		return;
237 
238 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "TSIF", &tmp);
239 
240 	if (ACPI_FAILURE(rv))
241 		return;
242 
243 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "VSIF", &tmp);
244 
245 	if (ACPI_FAILURE(rv))
246 		return;
247 
248 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RFAN", &tmp);
249 
250 	if (ACPI_FAILURE(rv))
251 		return;
252 
253 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RTMP", &tmp);
254 
255 	if (ACPI_FAILURE(rv))
256 		return;
257 
258 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "RVLT", &tmp);
259 
260 	if (ACPI_FAILURE(rv))
261 		return;
262 
263 	sc->sc_model = false;
264 }
265 
266 static void
aibs_init_new(device_t self)267 aibs_init_new(device_t self)
268 {
269 	struct aibs_softc *sc = device_private(self);
270 	ACPI_OBJECT_LIST arg;
271 	ACPI_OBJECT id, *obj;
272 	ACPI_BUFFER buf;
273 	ACPI_STATUS rv;
274 	uint32_t i, n;
275 
276 	arg.Count = 1;
277 	arg.Pointer = &id;
278 
279 	id.Type = ACPI_TYPE_INTEGER;
280 	id.Integer.Value = AIBS_MUX_HWMON;
281 
282 	buf.Pointer = NULL;
283 	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
284 
285 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf);
286 
287 	if (ACPI_FAILURE(rv))
288 		goto out;
289 
290 	obj = buf.Pointer;
291 
292 	if (obj->Type != ACPI_TYPE_PACKAGE) {
293 		rv = AE_TYPE;
294 		goto out;
295 	}
296 
297 	if (obj->Package.Count > UINT32_MAX) {
298 		rv = AE_AML_NUMERIC_OVERFLOW;
299 		goto out;
300 	}
301 
302 	n = obj->Package.Count;
303 
304 	if (n == 0) {
305 		rv = AE_NOT_EXIST;
306 		goto out;
307 	}
308 
309 	for (i = 0; i < n; i++)
310 		aibs_sensor_add(self, &obj->Package.Elements[i]);
311 
312 out:
313 	if (buf.Pointer != NULL)
314 		ACPI_FREE(buf.Pointer);
315 
316 	if (ACPI_FAILURE(rv)) {
317 
318 		aprint_error_dev(self, "failed to evaluate "
319 		    "GGRP: %s\n", AcpiFormatException(rv));
320 	}
321 }
322 
323 static void
aibs_init_old(device_t self,int type)324 aibs_init_old(device_t self, int type)
325 {
326 	struct aibs_softc *sc = device_private(self);
327 	char path[] = "?SIF";
328 	ACPI_OBJECT *elm, *obj;
329 	ACPI_BUFFER buf;
330 	ACPI_STATUS rv;
331 	uint32_t i, n;
332 
333 	switch (type) {
334 
335 	case AIBS_TYPE_FAN:
336 		path[0] = 'F';
337 		break;
338 
339 	case AIBS_TYPE_TEMP:
340 		path[0] = 'T';
341 		break;
342 
343 	case AIBS_TYPE_VOLT:
344 		path[0] = 'V';
345 		break;
346 
347 	default:
348 		return;
349 	}
350 
351 	rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf);
352 
353 	if (ACPI_FAILURE(rv))
354 		goto out;
355 
356 	obj = buf.Pointer;
357 
358 	if (obj->Type != ACPI_TYPE_PACKAGE) {
359 		rv = AE_TYPE;
360 		goto out;
361 	}
362 
363 	elm = obj->Package.Elements;
364 
365 	if (elm[0].Type != ACPI_TYPE_INTEGER) {
366 		rv = AE_TYPE;
367 		goto out;
368 	}
369 
370 	if (elm[0].Integer.Value > UINT32_MAX) {
371 		rv = AE_AML_NUMERIC_OVERFLOW;
372 		goto out;
373 	}
374 
375 	n = elm[0].Integer.Value;
376 
377 	if (n == 0) {
378 		rv = AE_NOT_EXIST;
379 		goto out;
380 	}
381 
382 	if (obj->Package.Count - 1 != n) {
383 		rv = AE_BAD_VALUE;
384 		goto out;
385 	}
386 
387 	for (i = 1; i < obj->Package.Count; i++) {
388 
389 		if (elm[i].Type != ACPI_TYPE_PACKAGE)
390 			continue;
391 
392 		aibs_sensor_add(self, &elm[i]);
393 	}
394 
395 out:
396 	if (buf.Pointer != NULL)
397 		ACPI_FREE(buf.Pointer);
398 
399 	if (ACPI_FAILURE(rv)) {
400 
401 		aprint_error_dev(self, "failed to evaluate "
402 		    "%s: %s\n", path, AcpiFormatException(rv));
403 	}
404 }
405 
406 static void
aibs_sensor_add(device_t self,ACPI_OBJECT * obj)407 aibs_sensor_add(device_t self, ACPI_OBJECT *obj)
408 {
409 	struct aibs_softc *sc = device_private(self);
410 	struct aibs_sensor *as;
411 	int ena, len, lhi, llo;
412 	const char *name;
413 	ACPI_STATUS rv;
414 
415 	as = NULL;
416 	rv = AE_OK;
417 
418 	if (obj->Type != ACPI_TYPE_PACKAGE) {
419 		rv = AE_TYPE;
420 		goto out;
421 	}
422 
423 	/*
424 	 * The known formats are:
425 	 *
426 	 *	index		type		old		new
427 	 *	-----		----		---		---
428 	 *	0		integer		flags		flags
429 	 *	1		string		name		name
430 	 *	2		integer		limit1		unknown
431 	 *	3		integer		limit2		unknown
432 	 *	4		integer		enable		limit1
433 	 *	5		integer		-		limit2
434 	 *	6		integer		-		enable
435 	 */
436 	if (sc->sc_model != false) {
437 		len = 7;
438 		llo = 4;
439 		lhi = 5;
440 		ena = 6;
441 	} else {
442 		len = 5;
443 		llo = 2;
444 		lhi = 3;
445 		ena = 4;
446 	}
447 
448 	if (obj->Package.Count != (uint32_t)len) {
449 		rv = AE_LIMIT;
450 		goto out;
451 	}
452 
453 	if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
454 	    obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
455 	    obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER ||
456 	    obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER ||
457 	    obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) {
458 		rv = AE_TYPE;
459 		goto out;
460 	}
461 
462 	as = kmem_zalloc(sizeof(*as), KM_SLEEP);
463 
464 	name = obj->Package.Elements[1].String.Pointer;
465 
466 	as->as_type = obj->Package.Elements[0].Integer.Value;
467 	as->as_liml = obj->Package.Elements[llo].Integer.Value;
468 	as->as_limh = obj->Package.Elements[lhi].Integer.Value;
469 
470 	if (sc->sc_model != false)
471 		as->as_limh += as->as_liml;	/* A range in the new model. */
472 
473 	as->as_sensor.state = ENVSYS_SINVALID;
474 
475 	switch (AIBS_TYPE(as->as_type)) {
476 
477 	case AIBS_TYPE_FAN:
478 		as->as_sensor.units = ENVSYS_SFANRPM;
479 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
480 		break;
481 
482 	case AIBS_TYPE_TEMP:
483 		as->as_sensor.units = ENVSYS_STEMP;
484 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
485 		break;
486 
487 	case AIBS_TYPE_VOLT:
488 		as->as_sensor.units = ENVSYS_SVOLTS_DC;
489 		as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
490 		break;
491 
492 	default:
493 		rv = AE_TYPE;
494 		goto out;
495 	}
496 
497 	(void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
498 
499 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
500 		rv = AE_AML_INTERNAL;
501 		goto out;
502 	}
503 
504 	SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
505 
506 out:
507 	if (ACPI_FAILURE(rv)) {
508 
509 		if (as != NULL)
510 			kmem_free(as, sizeof(*as));
511 
512 		aprint_error_dev(self, "failed to add "
513 		    "sensor: %s\n",  AcpiFormatException(rv));
514 	}
515 }
516 
517 static bool
aibs_sensor_value(device_t self,struct aibs_sensor * as,uint64_t * val)518 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
519 {
520 	struct aibs_softc *sc = device_private(self);
521 	uint32_t type, *ret, cmb[3];
522 	ACPI_OBJECT_LIST arg;
523 	ACPI_OBJECT cmi, tmp;
524 	ACPI_OBJECT *obj;
525 	ACPI_BUFFER buf;
526 	ACPI_STATUS rv;
527 	const char *path;
528 
529 	if (sc->sc_model != false) {
530 
531 		path = "GITM";
532 
533 		cmb[0] = as->as_type;
534 		cmb[1] = 0;
535 		cmb[2] = 0;
536 
537 		arg.Count = 1;
538 		arg.Pointer = &tmp;
539 
540 		tmp.Buffer.Length = sizeof(cmb);
541 		tmp.Buffer.Pointer = (uint8_t *)cmb;
542 		tmp.Type = type = ACPI_TYPE_BUFFER;
543 
544 	} else {
545 
546 		arg.Count = 1;
547 		arg.Pointer = &cmi;
548 
549 		cmi.Integer.Value = as->as_type;
550 		cmi.Type = type = ACPI_TYPE_INTEGER;
551 
552 		switch (AIBS_TYPE(as->as_type)) {
553 
554 		case AIBS_TYPE_FAN:
555 			path = "RFAN";
556 			break;
557 
558 		case AIBS_TYPE_TEMP:
559 			path = "RTMP";
560 			break;
561 
562 		case AIBS_TYPE_VOLT:
563 			path = "RVLT";
564 			break;
565 
566 		default:
567 			return false;
568 		}
569 	}
570 
571 	buf.Pointer = NULL;
572 	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
573 
574 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
575 
576 	if (ACPI_FAILURE(rv))
577 		goto out;
578 
579 	obj = buf.Pointer;
580 
581 	if (obj->Type != type) {
582 		rv = AE_TYPE;
583 		goto out;
584 	}
585 
586 	if (sc->sc_model != true)
587 		*val = obj->Integer.Value;
588 	else {
589 		/*
590 		 * The return buffer contains at least:
591 		 *
592 		 *	uint32_t buf[0]	 flags
593 		 *	uint32_t buf[1]	 return value
594 		 *	uint8_t  buf[2-] unknown
595 		 */
596 		if (obj->Buffer.Length < 8) {
597 			rv = AE_BUFFER_OVERFLOW;
598 			goto out;
599 		}
600 
601 		ret = (uint32_t *)obj->Buffer.Pointer;
602 
603 		if (ret[0] == 0) {
604 			rv = AE_BAD_VALUE;
605 			goto out;
606 		}
607 
608 		*val = ret[1];
609 	}
610 
611 out:
612 	if (buf.Pointer != NULL)
613 		ACPI_FREE(buf.Pointer);
614 
615 	if (ACPI_FAILURE(rv)) {
616 
617 		aprint_error_dev(self, "failed to evaluate "
618 		    "%s: %s\n", path, AcpiFormatException(rv));
619 
620 		return false;
621 	}
622 
623 	return true;
624 }
625 
626 static void
aibs_sensor_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)627 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
628 {
629 	struct aibs_softc *sc = sme->sme_cookie;
630 	struct aibs_sensor *tmp, *as = NULL;
631 	envsys_data_t *s = edata;
632 	uint64_t val = 0;
633 
634 	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
635 
636 		if (tmp->as_sensor.sensor == s->sensor) {
637 			as = tmp;
638 			break;
639 		}
640 	}
641 
642 	if (as == NULL) {
643 		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
644 		return;
645 	}
646 
647 	as->as_sensor.state = ENVSYS_SINVALID;
648 	as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
649 
650 	if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
651 		return;
652 
653 	switch (as->as_sensor.units) {
654 
655 	case ENVSYS_SFANRPM:
656 		as->as_sensor.value_cur = val;
657 		break;
658 
659 	case ENVSYS_STEMP:
660 
661 		if (val == 0)
662 			return;
663 
664 		as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
665 		break;
666 
667 	case ENVSYS_SVOLTS_DC:
668 		as->as_sensor.value_cur = val * 1000;
669 		break;
670 
671 	default:
672 		return;
673 	}
674 
675 	as->as_sensor.state = ENVSYS_SVALID;
676 	as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
677 }
678 
679 static void
aibs_sensor_limits(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)680 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
681     sysmon_envsys_lim_t *limits, uint32_t *props)
682 {
683 	struct aibs_softc *sc = sme->sme_cookie;
684 	struct aibs_sensor *tmp, *as = NULL;
685 	sysmon_envsys_lim_t *lim = limits;
686 	envsys_data_t *s = edata;
687 
688 	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
689 
690 		if (tmp->as_sensor.sensor == s->sensor) {
691 			as = tmp;
692 			break;
693 		}
694 	}
695 
696 	if (as == NULL) {
697 		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
698 		return;
699 	}
700 
701 	switch (as->as_sensor.units) {
702 
703 	case ENVSYS_SFANRPM:
704 
705 		/*
706 		 * Some boards have strange limits for fans.
707 		 */
708 		if (as->as_liml == 0) {
709 			lim->sel_warnmin = as->as_limh;
710 			*props = PROP_WARNMIN;
711 
712 		} else {
713 			lim->sel_warnmin = as->as_liml;
714 			lim->sel_warnmax = as->as_limh;
715 			*props = PROP_WARNMIN | PROP_WARNMAX;
716 		}
717 
718 		break;
719 
720 	case ENVSYS_STEMP:
721 		lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
722 		lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
723 
724 		*props = PROP_CRITMAX | PROP_WARNMAX;
725 		break;
726 
727 	case ENVSYS_SVOLTS_DC:
728 		lim->sel_critmin = as->as_liml * 1000;
729 		lim->sel_critmax = as->as_limh * 1000;
730 		*props = PROP_CRITMIN | PROP_CRITMAX;
731 		break;
732 
733 	default:
734 		return;
735 	}
736 }
737 
738 MODULE(MODULE_CLASS_DRIVER, aibs, "sysmon_envsys");
739 
740 #ifdef _MODULE
741 #include "ioconf.c"
742 #endif
743 
744 static int
aibs_modcmd(modcmd_t cmd,void * aux)745 aibs_modcmd(modcmd_t cmd, void *aux)
746 {
747 	int rv = 0;
748 
749 	switch (cmd) {
750 
751 	case MODULE_CMD_INIT:
752 
753 #ifdef _MODULE
754 		rv = config_init_component(cfdriver_ioconf_aibs,
755 		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
756 #endif
757 		break;
758 
759 	case MODULE_CMD_FINI:
760 
761 #ifdef _MODULE
762 		rv = config_fini_component(cfdriver_ioconf_aibs,
763 		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
764 #endif
765 		break;
766 
767 	default:
768 		rv = ENOTTY;
769 	}
770 
771 	return rv;
772 }
773