aibs_acpi.c revision 1.4.2.1 1 /* $NetBSD: aibs_acpi.c,v 1.4.2.1 2017/12/03 11:36:58 jdolecek 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.4.2.1 2017/12/03 11:36:58 jdolecek 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 name = obj->Package.Elements[1].String.Pointer;
468
469 as->as_type = obj->Package.Elements[0].Integer.Value;
470 as->as_liml = obj->Package.Elements[llo].Integer.Value;
471 as->as_limh = obj->Package.Elements[lhi].Integer.Value;
472
473 if (sc->sc_model != false)
474 as->as_limh += as->as_liml; /* A range in the new model. */
475
476 as->as_sensor.state = ENVSYS_SINVALID;
477
478 switch (AIBS_TYPE(as->as_type)) {
479
480 case AIBS_TYPE_FAN:
481 as->as_sensor.units = ENVSYS_SFANRPM;
482 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
483 break;
484
485 case AIBS_TYPE_TEMP:
486 as->as_sensor.units = ENVSYS_STEMP;
487 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
488 break;
489
490 case AIBS_TYPE_VOLT:
491 as->as_sensor.units = ENVSYS_SVOLTS_DC;
492 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
493 break;
494
495 default:
496 rv = AE_TYPE;
497 goto out;
498 }
499
500 (void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
501
502 if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
503 rv = AE_AML_INTERNAL;
504 goto out;
505 }
506
507 SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
508
509 out:
510 if (ACPI_FAILURE(rv)) {
511
512 if (as != NULL)
513 kmem_free(as, sizeof(*as));
514
515 aprint_error_dev(self, "failed to add "
516 "sensor: %s\n", AcpiFormatException(rv));
517 }
518 }
519
520 static bool
521 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
522 {
523 struct aibs_softc *sc = device_private(self);
524 uint32_t type, *ret, cmb[3];
525 ACPI_OBJECT_LIST arg;
526 ACPI_OBJECT cmi, tmp;
527 ACPI_OBJECT *obj;
528 ACPI_BUFFER buf;
529 ACPI_STATUS rv;
530 const char *path;
531
532 if (sc->sc_model != false) {
533
534 path = "GITM";
535
536 cmb[0] = as->as_type;
537 cmb[1] = 0;
538 cmb[2] = 0;
539
540 arg.Count = 1;
541 arg.Pointer = &tmp;
542
543 tmp.Buffer.Length = sizeof(cmb);
544 tmp.Buffer.Pointer = (uint8_t *)cmb;
545 tmp.Type = type = ACPI_TYPE_BUFFER;
546
547 } else {
548
549 arg.Count = 1;
550 arg.Pointer = &cmi;
551
552 cmi.Integer.Value = as->as_type;
553 cmi.Type = type = ACPI_TYPE_INTEGER;
554
555 switch (AIBS_TYPE(as->as_type)) {
556
557 case AIBS_TYPE_FAN:
558 path = "RFAN";
559 break;
560
561 case AIBS_TYPE_TEMP:
562 path = "RTMP";
563 break;
564
565 case AIBS_TYPE_VOLT:
566 path = "RVLT";
567 break;
568
569 default:
570 return false;
571 }
572 }
573
574 buf.Pointer = NULL;
575 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
576
577 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
578
579 if (ACPI_FAILURE(rv))
580 goto out;
581
582 obj = buf.Pointer;
583
584 if (obj->Type != type) {
585 rv = AE_TYPE;
586 goto out;
587 }
588
589 if (sc->sc_model != true)
590 *val = obj->Integer.Value;
591 else {
592 /*
593 * The return buffer contains at least:
594 *
595 * uint32_t buf[0] flags
596 * uint32_t buf[1] return value
597 * uint8_t buf[2-] unknown
598 */
599 if (obj->Buffer.Length < 8) {
600 rv = AE_BUFFER_OVERFLOW;
601 goto out;
602 }
603
604 ret = (uint32_t *)obj->Buffer.Pointer;
605
606 if (ret[0] == 0) {
607 rv = AE_BAD_VALUE;
608 goto out;
609 }
610
611 *val = ret[1];
612 }
613
614 out:
615 if (buf.Pointer != NULL)
616 ACPI_FREE(buf.Pointer);
617
618 if (ACPI_FAILURE(rv)) {
619
620 aprint_error_dev(self, "failed to evaluate "
621 "%s: %s\n", path, AcpiFormatException(rv));
622
623 return false;
624 }
625
626 return true;
627 }
628
629 static void
630 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
631 {
632 struct aibs_softc *sc = sme->sme_cookie;
633 struct aibs_sensor *tmp, *as = NULL;
634 envsys_data_t *s = edata;
635 uint64_t val = 0;
636
637 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
638
639 if (tmp->as_sensor.sensor == s->sensor) {
640 as = tmp;
641 break;
642 }
643 }
644
645 if (as == NULL) {
646 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
647 return;
648 }
649
650 as->as_sensor.state = ENVSYS_SINVALID;
651 as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
652
653 if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
654 return;
655
656 switch (as->as_sensor.units) {
657
658 case ENVSYS_SFANRPM:
659 as->as_sensor.value_cur = val;
660 break;
661
662 case ENVSYS_STEMP:
663
664 if (val == 0)
665 return;
666
667 as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
668 break;
669
670 case ENVSYS_SVOLTS_DC:
671 as->as_sensor.value_cur = val * 1000;
672 break;
673
674 default:
675 return;
676 }
677
678 as->as_sensor.state = ENVSYS_SVALID;
679 as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
680 }
681
682 static void
683 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
684 sysmon_envsys_lim_t *limits, uint32_t *props)
685 {
686 struct aibs_softc *sc = sme->sme_cookie;
687 struct aibs_sensor *tmp, *as = NULL;
688 sysmon_envsys_lim_t *lim = limits;
689 envsys_data_t *s = edata;
690
691 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
692
693 if (tmp->as_sensor.sensor == s->sensor) {
694 as = tmp;
695 break;
696 }
697 }
698
699 if (as == NULL) {
700 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
701 return;
702 }
703
704 switch (as->as_sensor.units) {
705
706 case ENVSYS_SFANRPM:
707
708 /*
709 * Some boards have strange limits for fans.
710 */
711 if (as->as_liml == 0) {
712 lim->sel_warnmin = as->as_limh;
713 *props = PROP_WARNMIN;
714
715 } else {
716 lim->sel_warnmin = as->as_liml;
717 lim->sel_warnmax = as->as_limh;
718 *props = PROP_WARNMIN | PROP_WARNMAX;
719 }
720
721 break;
722
723 case ENVSYS_STEMP:
724 lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
725 lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
726
727 *props = PROP_CRITMAX | PROP_WARNMAX;
728 break;
729
730 case ENVSYS_SVOLTS_DC:
731 lim->sel_critmin = as->as_liml * 1000;
732 lim->sel_critmax = as->as_limh * 1000;
733 *props = PROP_CRITMIN | PROP_CRITMAX;
734 break;
735
736 default:
737 return;
738 }
739 }
740
741 MODULE(MODULE_CLASS_DRIVER, aibs, "sysmon_envsys");
742
743 #ifdef _MODULE
744 #include "ioconf.c"
745 #endif
746
747 static int
748 aibs_modcmd(modcmd_t cmd, void *aux)
749 {
750 int rv = 0;
751
752 switch (cmd) {
753
754 case MODULE_CMD_INIT:
755
756 #ifdef _MODULE
757 rv = config_init_component(cfdriver_ioconf_aibs,
758 cfattach_ioconf_aibs, cfdata_ioconf_aibs);
759 #endif
760 break;
761
762 case MODULE_CMD_FINI:
763
764 #ifdef _MODULE
765 rv = config_fini_component(cfdriver_ioconf_aibs,
766 cfattach_ioconf_aibs, cfdata_ioconf_aibs);
767 #endif
768 break;
769
770 default:
771 rv = ENOTTY;
772 }
773
774 return rv;
775 }
776