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