1 /* $NetBSD: aibs_acpi.c,v 1.4 2012/08/14 14:36:43 jruoho 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.4 2012/08/14 14:36:43 jruoho 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