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