xref: /netbsd-src/sys/dev/acpi/acpi_bat.c (revision 08c81a9c2dc8c7300e893321eb65c0925d60871c)
1 /*	$NetBSD: acpi_bat.c,v 1.4 2002/08/20 14:07:51 christos 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.4 2002/08/20 14:07:51 christos 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 #define	BAT_WORDS	13
64 
65 struct acpibat_softc {
66 	struct device sc_dev;		/* base device glue */
67 	struct acpi_devnode *sc_node;	/* our ACPI devnode */
68 	int sc_flags;			/* see below */
69 	struct callout sc_callout; 	/* XXX temporary polling */
70 	int sc_status;			/* power status */
71 	int sc_rate;			/* current drain rate */
72 	int sc_capacity;		/* current capacity */
73 	int sc_mv;			/* current potential in mV */
74 	int sc_design_capacity;		/* design capacity */
75 	int sc_pred_capacity;		/* estimated current max */
76 	int sc_warn_capacity;		/* warning level */
77 	int sc_low_capacity;		/* low level */
78 
79 	ACPI_OBJECT sc_Ret[BAT_WORDS];	/* Return Buffer */
80 };
81 
82 #define	ABAT_F_VERBOSE		0x01	/* verbose events */
83 #define ABAT_F_PWRUNIT_MA	0x02 	/* mA instead of mW */
84 
85 #define ACM_RATEUNIT(sc) (((sc)->sc_flags & ABAT_F_PWRUNIT_MA)?"A":"W")
86 #define ACM_CAPUNIT(sc) (((sc)->sc_flags & ABAT_F_PWRUNIT_MA)?"Ah":"Wh")
87 #define ACM_SCALE(x)	((x) / 1000), ((x) % 1000)
88 
89 int	acpibat_match(struct device *, struct cfdata *, void *);
90 void	acpibat_attach(struct device *, struct device *, void *);
91 
92 struct cfattach acpibat_ca = {
93 	sizeof(struct acpibat_softc), acpibat_match, acpibat_attach,
94 };
95 
96 static void acpibat_get_status(void *);
97 static void acpibat_get_info(void *);
98 void	acpibat_notify_handler(ACPI_HANDLE, UINT32, void *context);
99 static void acpibat_tick(void *);
100 
101 /*
102  * acpibat_match:
103  *
104  *	Autoconfiguration `match' routine.
105  */
106 int
107 acpibat_match(struct device *parent, struct cfdata *match, void *aux)
108 {
109 	struct acpi_attach_args *aa = aux;
110 
111 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
112 		return (0);
113 
114 	if (strcmp(aa->aa_node->ad_devinfo.HardwareId, "PNP0C0A") == 0)
115 		return (1);
116 
117 	return (0);
118 }
119 
120 /*
121  * acpibat_attach:
122  *
123  *	Autoconfiguration `attach' routine.
124  */
125 void
126 acpibat_attach(struct device *parent, struct device *self, void *aux)
127 {
128 	struct acpibat_softc *sc = (void *) self;
129 	struct acpi_attach_args *aa = aux;
130 	ACPI_STATUS rv;
131 
132 	printf(": ACPI Battery\n");
133 
134 	sc->sc_node = aa->aa_node;
135 
136 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
137 	    ACPI_DEVICE_NOTIFY, acpibat_notify_handler, sc);
138 	if (rv != AE_OK) {
139 		printf("%s: unable to register DEVICE NOTIFY handler: %d\n",
140 		    sc->sc_dev.dv_xname, rv);
141 		return;
142 	}
143 
144 	/* XXX See acpibat_notify_handler() */
145 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
146 	    ACPI_SYSTEM_NOTIFY, acpibat_notify_handler, sc);
147 	if (rv != AE_OK) {
148 		printf("%s: unable to register SYSTEM NOTIFY handler: %d\n",
149 		    sc->sc_dev.dv_xname, rv);
150 		return;
151 	}
152 	/*
153 	 * XXX poll battery in the driver for now.
154 	 * in the future, when we have an API, let userland do this polling
155 	 */
156 	callout_init(&sc->sc_callout);
157 	callout_reset(&sc->sc_callout, 60*hz, acpibat_tick, sc);
158 
159 	/* Display the current state. */
160 	sc->sc_flags = ABAT_F_VERBOSE;
161 	acpibat_get_info(sc);
162 	acpibat_get_status(sc);
163 
164 	/*
165 	 * XXX Hook into sysmon here.
166 	 */
167 }
168 
169 static void
170 acpibat_tick(void *arg)
171 {
172 	struct acpibat_softc *sc = arg;
173 	callout_reset(&sc->sc_callout, 60*hz, acpibat_tick, arg);
174 	AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpibat_get_status, sc);
175 }
176 
177 
178 /*
179  * acpibat_get_info
180  *
181  * 	Get, and possibly display, the battery info.
182  */
183 
184 static void
185 acpibat_get_info(void *arg)
186 {
187 	struct acpibat_softc *sc = arg;
188 	ACPI_OBJECT *p1, *p2;
189 	ACPI_STATUS rv;
190 	ACPI_BUFFER buf;
191 
192 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
193 	if (rv != AE_OK) {
194 		printf("%s: failed to evaluate _BIF: %x\n",
195 		    sc->sc_dev.dv_xname, rv);
196 		return;
197 	}
198 	p1 = (ACPI_OBJECT *)buf.Pointer;
199 	if (p1->Type != ACPI_TYPE_PACKAGE) {
200 		printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname,
201 		    p1->Type);
202 		goto out;
203 	}
204 	if (p1->Package.Count < 13)
205 		printf("%s: expected 13 elts, got %d\n",
206 		    sc->sc_dev.dv_xname, p1->Package.Count);
207 
208 	p2 = p1->Package.Elements;
209 	if (p2[0].Integer.Value == 1)
210 		sc->sc_flags |= ABAT_F_PWRUNIT_MA;
211 
212 	sc->sc_design_capacity = p2[1].Integer.Value;
213 	sc->sc_pred_capacity = p2[2].Integer.Value;
214 	sc->sc_warn_capacity = p2[6].Integer.Value;
215 	sc->sc_low_capacity = p2[7].Integer.Value;
216 
217 	printf("%s: %s %s %s %s\n",
218 	    sc->sc_dev.dv_xname,
219 	    p2[12].String.Pointer, p2[11].String.Pointer,
220 	    p2[9].String.Pointer, p2[10].String.Pointer);
221 
222 	printf("%s: Design %d.%03d%s, Predicted %d.%03d%s Warn %d.%03d%s Low %d.%03d%s\n",
223 	       sc->sc_dev.dv_xname,
224 	       ACM_SCALE(sc->sc_design_capacity), ACM_CAPUNIT(sc),
225 	       ACM_SCALE(sc->sc_pred_capacity), ACM_CAPUNIT(sc),
226 	       ACM_SCALE(sc->sc_warn_capacity), ACM_CAPUNIT(sc),
227 	       ACM_SCALE(sc->sc_low_capacity), ACM_CAPUNIT(sc));
228 out:
229 	AcpiOsFree(buf.Pointer);
230 }
231 
232 /*
233  * acpibat_get_status:
234  *
235  *	Get, and possibly display, the current battery line status.
236  */
237 static void
238 acpibat_get_status(void *arg)
239 {
240 	struct acpibat_softc *sc = arg;
241 	ACPI_OBJECT *p1, *p2;
242 	ACPI_STATUS rv;
243 	ACPI_BUFFER buf;
244 
245 	buf.Pointer = sc->sc_Ret;
246 	buf.Length = sizeof(sc->sc_Ret);
247 
248 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "_BST", NULL, &buf);
249 	if (rv != AE_OK) {
250 		printf("bat: failed to evaluate _BST: %x\n", rv);
251 		return;
252 	}
253 	p1 = (ACPI_OBJECT *)buf.Pointer;
254 
255 	if (p1->Type != ACPI_TYPE_PACKAGE) {
256 		printf("bat: expected PACKAGE, got %d\n", p1->Type);
257 		return;
258 	}
259 	if (p1->Package.Count < 4)
260 		printf("bat: expected 4 elts, got %d\n", p1->Package.Count);
261 	p2 = p1->Package.Elements;
262 
263 	sc->sc_status = p2[0].Integer.Value;
264 	sc->sc_rate = p2[1].Integer.Value;
265 	sc->sc_capacity = p2[2].Integer.Value;
266 	sc->sc_mv = p2[3].Integer.Value;
267 
268 	if (sc->sc_flags & ABAT_F_VERBOSE) {
269 		printf("%s: %s%s: %d.%03dV cap %d.%03d%s (%d%%) rate %d.%03d%s\n",
270 		       sc->sc_dev.dv_xname,
271 		       (sc->sc_status&4) ? "CRITICAL ":"",
272 		       (sc->sc_status&1) ? "discharging" : (
273 		               (sc->sc_status & 2) ? "charging" : "idle"),
274 		       ACM_SCALE(sc->sc_mv),
275 		       ACM_SCALE(sc->sc_capacity), ACM_CAPUNIT(sc),
276 		       (sc->sc_design_capacity == 0) ? 0 :
277 			    (sc->sc_capacity * 100) / sc->sc_design_capacity,
278 		       ACM_SCALE(sc->sc_rate), ACM_RATEUNIT(sc));
279 	}
280 }
281 
282 /*
283  * acpibat_notify_handler:
284  *
285  *	Callback from ACPI interrupt handler to notify us of an event.
286  */
287 void
288 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
289 {
290 	struct acpibat_softc *sc = context;
291 	int rv;
292 
293 	switch (notify) {
294 	case ACPI_NOTIFY_BusCheck:
295 	case ACPI_NOTIFY_BatteryStatusChanged:
296 	case ACPI_NOTIFY_BatteryInformationChanged:
297 #ifdef ACPI_BAT_DEBUG
298 		printf("%s: received notify message: 0x%x\n",
299 		    sc->sc_dev.dv_xname, notify);
300 #endif
301 		rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
302 		    acpibat_get_status, sc);
303 		if (rv != AE_OK)
304 			printf("%s: unable to queue status check: %d\n",
305 			    sc->sc_dev.dv_xname, rv);
306 		break;
307 	default:
308 		printf("%s: received unknown notify message: 0x%x\n",
309 		    sc->sc_dev.dv_xname, notify);
310 	}
311 }
312