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