xref: /netbsd/sys/dev/acpi/acpi_bat.c (revision bf9ec67e)
1 /*	$NetBSD: acpi_bat.c,v 1.1 2002/03/24 03:46:10 sommerfeld Exp $	*/
2 
3 /*
4  * Copyright 2001 Bill Sommerfeld.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed for the NetBSD Project by
18  *	Wasabi Systems, Inc.
19  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
20  *    or promote products derived from this software without specific prior
21  *    written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #define ACPI_BAT_DEBUG
37 
38 /*
39  * ACPI Battery Driver.
40  *
41  * ACPI defines two different battery device interfaces: "Control
42  * Method" batteries, in which AML methods are defined in order to get
43  * battery status and set battery alarm thresholds, and a "Smart
44  * Battery" device, which is an SMbus device accessed through the ACPI
45  * Embedded Controller device.
46  *
47  * This driver is for the "Control Method"-style battery only.
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.1 2002/03/24 03:46:10 sommerfeld Exp $");
52 
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/kernel.h>		/* for hz */
56 #include <sys/device.h>
57 #include <sys/callout.h>
58 
59 #include <dev/acpi/acpica.h>
60 #include <dev/acpi/acpireg.h>
61 #include <dev/acpi/acpivar.h>
62 
63 struct acpibat_softc {
64 	struct device sc_dev;		/* base device glue */
65 	struct acpi_devnode *sc_node;	/* our ACPI devnode */
66 	int sc_flags;			/* see below */
67 	struct callout sc_callout; 	/* XXX temporary polling */
68 	int sc_status;			/* power status */
69 	int sc_rate;			/* current drain rate */
70 	int sc_capacity;		/* current capacity */
71 	int sc_mv;			/* current potential in mV */
72 	int sc_design_capacity;		/* design capacity */
73 	int sc_pred_capacity;		/* estimated current max */
74 	int sc_warn_capacity;		/* warning level */
75 	int sc_low_capacity;		/* low level */
76 };
77 
78 #define	ABAT_F_VERBOSE		0x01	/* verbose events */
79 #define ABAT_F_PWRUNIT_MA	0x02 	/* mA instead of mW */
80 
81 #define ACM_RATEUNIT(sc) (((sc)->sc_flags & ABAT_F_PWRUNIT_MA)?"mA":"mW")
82 #define ACM_CAPUNIT(sc) (((sc)->sc_flags & ABAT_F_PWRUNIT_MA)?"mAh":"mWh")
83 
84 int	acpibat_match(struct device *, struct cfdata *, void *);
85 void	acpibat_attach(struct device *, struct device *, void *);
86 
87 struct cfattach acpibat_ca = {
88 	sizeof(struct acpibat_softc), acpibat_match, acpibat_attach,
89 };
90 
91 static void acpibat_get_status(void *);
92 static void acpibat_get_info(void *);
93 void	acpibat_notify_handler(ACPI_HANDLE, UINT32, void *context);
94 static void acpibat_tick(void *);
95 
96 /*
97  * acpibat_match:
98  *
99  *	Autoconfiguration `match' routine.
100  */
101 int
102 acpibat_match(struct device *parent, struct cfdata *match, void *aux)
103 {
104 	struct acpi_attach_args *aa = aux;
105 
106 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
107 		return (0);
108 
109 	if (strcmp(aa->aa_node->ad_devinfo.HardwareId, "PNP0C0A") == 0)
110 		return (1);
111 
112 	return (0);
113 }
114 
115 /*
116  * acpibat_attach:
117  *
118  *	Autoconfiguration `attach' routine.
119  */
120 void
121 acpibat_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct acpibat_softc *sc = (void *) self;
124 	struct acpi_attach_args *aa = aux;
125 	ACPI_STATUS rv;
126 
127 	printf(": ACPI Battery\n");
128 
129 	sc->sc_node = aa->aa_node;
130 
131 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
132 	    ACPI_DEVICE_NOTIFY, acpibat_notify_handler, sc);
133 	if (rv != AE_OK) {
134 		printf("%s: unable to register DEVICE NOTIFY handler: %d\n",
135 		    sc->sc_dev.dv_xname, rv);
136 		return;
137 	}
138 
139 	/* XXX See acpibat_notify_handler() */
140 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
141 	    ACPI_SYSTEM_NOTIFY, acpibat_notify_handler, sc);
142 	if (rv != AE_OK) {
143 		printf("%s: unable to register SYSTEM NOTIFY handler: %d\n",
144 		    sc->sc_dev.dv_xname, rv);
145 		return;
146 	}
147 	/*
148 	 * XXX poll battery in the driver for now.
149 	 * in the future, when we have an API, let userland do this polling
150 	 */
151 	callout_init(&sc->sc_callout);
152 	callout_reset(&sc->sc_callout, 60*hz, acpibat_tick, sc);
153 
154 	/* Display the current state. */
155 	sc->sc_flags = ABAT_F_VERBOSE;
156 	acpibat_get_info(sc);
157 	acpibat_get_status(sc);
158 
159 	/*
160 	 * XXX Hook into sysmon here.
161 	 */
162 }
163 
164 static void
165 acpibat_tick(void *arg)
166 {
167 	struct acpibat_softc *sc = arg;
168 	callout_reset(&sc->sc_callout, 60*hz, acpibat_tick, arg);
169 	AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpibat_get_status, sc);
170 }
171 
172 
173 /*
174  * acpibat_get_info
175  *
176  * 	Get, and possibly display, the battery info.
177  */
178 
179 static void
180 acpibat_get_info(void *arg)
181 {
182 	struct acpibat_softc *sc = arg;
183 	ACPI_OBJECT *p1, *p2;
184 	ACPI_STATUS rv;
185 	ACPI_BUFFER buf;
186 
187 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
188 	if (rv != AE_OK) {
189 		printf("%s: failed to evaluate _BIF: %x\n",
190 		    sc->sc_dev.dv_xname, rv);
191 		return;
192 	}
193 	p1 = (ACPI_OBJECT *)buf.Pointer;
194 	if (p1->Type != ACPI_TYPE_PACKAGE) {
195 		printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname,
196 		    p1->Type);
197 		goto out;
198 	}
199 	if (p1->Package.Count < 13)
200 		printf("%s: expected 13 elts, got %d\n",
201 		    sc->sc_dev.dv_xname, p1->Package.Count);
202 
203 	p2 = p1->Package.Elements;
204 	if (p2[0].Integer.Value == 1)
205 		sc->sc_flags |= ABAT_F_PWRUNIT_MA;
206 
207 	sc->sc_design_capacity = p2[1].Integer.Value;
208 	sc->sc_pred_capacity = p2[2].Integer.Value;
209 	sc->sc_warn_capacity = p2[6].Integer.Value;
210 	sc->sc_low_capacity = p2[7].Integer.Value;
211 
212 	printf("%s: %s %s %s %s\n",
213 	    sc->sc_dev.dv_xname,
214 	    p2[12].String.Pointer, p2[11].String.Pointer,
215 	    p2[9].String.Pointer, p2[10].String.Pointer);
216 
217 	printf("%s: Design %d%s, Predicted %d%s Warn %d%s Low %d%s\n",
218 	    sc->sc_dev.dv_xname,
219 	    sc->sc_design_capacity, ACM_CAPUNIT(sc),
220 	    sc->sc_pred_capacity, ACM_CAPUNIT(sc),
221 	    sc->sc_warn_capacity, ACM_CAPUNIT(sc),
222 	    sc->sc_low_capacity, ACM_CAPUNIT(sc));
223 out:
224 	AcpiOsFree(buf.Pointer);
225 }
226 
227 /*
228  * acpibat_get_status:
229  *
230  *	Get, and possibly display, the current battery line status.
231  */
232 static void
233 acpibat_get_status(void *arg)
234 {
235 	struct acpibat_softc *sc = arg;
236 	ACPI_OBJECT *p1, *p2;
237 	ACPI_STATUS rv;
238 	ACPI_BUFFER buf;
239 
240 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf);
241 	if (rv != AE_OK) {
242 		printf("bat: failed to evaluate _BIF: %x\n", rv);
243 		return;
244 	}
245 	p1 = (ACPI_OBJECT *)buf.Pointer;
246 
247 	if (p1->Type != ACPI_TYPE_PACKAGE) {
248 		printf("bat: expected PACKAGE, got %d\n", p1->Type);
249 		goto out;
250 	}
251 	if (p1->Package.Count < 4)
252 		printf("bat: expected 4 elts, got %d\n", p1->Package.Count);
253 	p2 = p1->Package.Elements;
254 
255 	sc->sc_status = p2[0].Integer.Value;
256 	sc->sc_rate = p2[1].Integer.Value;
257 	sc->sc_capacity = p2[2].Integer.Value;
258 	sc->sc_mv = p2[3].Integer.Value;
259 
260 	if (sc->sc_flags & ABAT_F_VERBOSE) {
261 		printf("%s: %s%s: %dmV cap %d%s (%d%%) rate %d%s\n",
262 		    sc->sc_dev.dv_xname,
263 		    (sc->sc_status&4) ? "CRITICAL ":"",
264 		    (sc->sc_status&1) ? "discharging" : (
265 			    (sc->sc_status & 2) ? "charging" : "idle"),
266 		    sc->sc_mv,
267 		    sc->sc_capacity,
268 		    ACM_CAPUNIT(sc),
269 		    (sc->sc_capacity * 100) / sc->sc_design_capacity,
270 		    sc->sc_rate,
271 		    ACM_RATEUNIT(sc));
272 	}
273 out:
274 	AcpiOsFree(buf.Pointer);
275 }
276 
277 /*
278  * acpibat_notify_handler:
279  *
280  *	Callback from ACPI interrupt handler to notify us of an event.
281  */
282 void
283 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
284 {
285 	struct acpibat_softc *sc = context;
286 	int rv;
287 
288 	switch (notify) {
289 	case ACPI_NOTIFY_BusCheck:
290 	case ACPI_NOTIFY_BatteryStatusChanged:
291 	case ACPI_NOTIFY_BatteryInformationChanged:
292 #ifdef ACPI_BAT_DEBUG
293 		printf("%s: received notify message: 0x%x\n",
294 		    sc->sc_dev.dv_xname, notify);
295 #endif
296 		rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
297 		    acpibat_get_status, sc);
298 		if (rv != AE_OK)
299 			printf("%s: unable to queue status check: %d\n",
300 			    sc->sc_dev.dv_xname, rv);
301 		break;
302 	default:
303 		printf("%s: received unknown notify message: 0x%x\n",
304 		    sc->sc_dev.dv_xname, notify);
305 	}
306 }
307