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