acpi_bat.c revision 1.20 1 1.20 kochi /* $NetBSD: acpi_bat.c,v 1.20 2003/05/22 15:35:51 kochi 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.13 explorer #if 0
37 1.1 sommerfe #define ACPI_BAT_DEBUG
38 1.13 explorer #endif
39 1.1 sommerfe
40 1.1 sommerfe /*
41 1.1 sommerfe * ACPI Battery Driver.
42 1.1 sommerfe *
43 1.1 sommerfe * ACPI defines two different battery device interfaces: "Control
44 1.1 sommerfe * Method" batteries, in which AML methods are defined in order to get
45 1.1 sommerfe * battery status and set battery alarm thresholds, and a "Smart
46 1.1 sommerfe * Battery" device, which is an SMbus device accessed through the ACPI
47 1.1 sommerfe * Embedded Controller device.
48 1.1 sommerfe *
49 1.1 sommerfe * This driver is for the "Control Method"-style battery only.
50 1.1 sommerfe */
51 1.1 sommerfe
52 1.1 sommerfe #include <sys/cdefs.h>
53 1.20 kochi __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.20 2003/05/22 15:35:51 kochi Exp $");
54 1.1 sommerfe
55 1.1 sommerfe #include <sys/param.h>
56 1.1 sommerfe #include <sys/systm.h>
57 1.1 sommerfe #include <sys/kernel.h> /* for hz */
58 1.1 sommerfe #include <sys/device.h>
59 1.14 explorer #include <dev/sysmon/sysmonvar.h>
60 1.1 sommerfe
61 1.1 sommerfe #include <dev/acpi/acpica.h>
62 1.1 sommerfe #include <dev/acpi/acpireg.h>
63 1.1 sommerfe #include <dev/acpi/acpivar.h>
64 1.1 sommerfe
65 1.14 explorer /* sensor indexes */
66 1.15 tshiozak #define ACPIBAT_PRESENT 0
67 1.15 tshiozak #define ACPIBAT_DCAPACITY 1
68 1.15 tshiozak #define ACPIBAT_LFCCAPACITY 2
69 1.15 tshiozak #define ACPIBAT_TECHNOLOGY 3
70 1.15 tshiozak #define ACPIBAT_DVOLTAGE 4
71 1.15 tshiozak #define ACPIBAT_WCAPACITY 5
72 1.15 tshiozak #define ACPIBAT_LCAPACITY 6
73 1.15 tshiozak #define ACPIBAT_VOLTAGE 7
74 1.15 tshiozak #define ACPIBAT_LOAD 8
75 1.15 tshiozak #define ACPIBAT_CAPACITY 9
76 1.15 tshiozak #define ACPIBAT_CHARGING 10
77 1.15 tshiozak #define ACPIBAT_DISCHARGING 11
78 1.15 tshiozak #define ACPIBAT_NSENSORS 12 /* number of sensors */
79 1.14 explorer
80 1.14 explorer const struct envsys_range acpibat_range_amp[] = {
81 1.14 explorer { 0, 1, ENVSYS_SVOLTS_DC },
82 1.14 explorer { 1, 2, ENVSYS_SAMPS },
83 1.14 explorer { 2, 3, ENVSYS_SAMPHOUR },
84 1.14 explorer { 1, 0, -1 },
85 1.14 explorer };
86 1.14 explorer
87 1.14 explorer const struct envsys_range acpibat_range_watt[] = {
88 1.14 explorer { 0, 1, ENVSYS_SVOLTS_DC },
89 1.14 explorer { 1, 2, ENVSYS_SWATTS },
90 1.14 explorer { 2, 3, ENVSYS_SWATTHOUR },
91 1.14 explorer { 1, 0, -1 },
92 1.14 explorer };
93 1.11 explorer
94 1.1 sommerfe struct acpibat_softc {
95 1.1 sommerfe struct device sc_dev; /* base device glue */
96 1.1 sommerfe struct acpi_devnode *sc_node; /* our ACPI devnode */
97 1.1 sommerfe int sc_flags; /* see below */
98 1.15 tshiozak int sc_available; /* available information level */
99 1.14 explorer
100 1.14 explorer struct sysmon_envsys sc_sysmon;
101 1.14 explorer struct envsys_basic_info sc_info[ACPIBAT_NSENSORS];
102 1.14 explorer struct envsys_tre_data sc_data[ACPIBAT_NSENSORS];
103 1.14 explorer
104 1.15 tshiozak struct simplelock sc_lock;
105 1.1 sommerfe };
106 1.1 sommerfe
107 1.11 explorer /*
108 1.11 explorer * These flags are used to examine the battery device data returned from
109 1.11 explorer * the ACPI interface, specifically the "battery status"
110 1.11 explorer */
111 1.11 explorer #define ACPIBAT_PWRUNIT_MA 0x00000001 /* mA not mW */
112 1.11 explorer
113 1.11 explorer /*
114 1.11 explorer * These flags are used to examine the battery charge/discharge/critical
115 1.11 explorer * state returned from a get-status command.
116 1.11 explorer */
117 1.14 explorer #define ACPIBAT_ST_DISCHARGING 0x00000001 /* battery is discharging */
118 1.14 explorer #define ACPIBAT_ST_CHARGING 0x00000002 /* battery is charging */
119 1.14 explorer #define ACPIBAT_ST_CRITICAL 0x00000004 /* battery is critical */
120 1.11 explorer
121 1.11 explorer /*
122 1.13 explorer * Flags for battery status from _STA return
123 1.13 explorer */
124 1.15 tshiozak #define ACPIBAT_STA_PRESENT 0x00000010 /* battery present */
125 1.13 explorer
126 1.13 explorer /*
127 1.11 explorer * These flags are used to set internal state in our softc.
128 1.11 explorer */
129 1.1 sommerfe #define ABAT_F_VERBOSE 0x01 /* verbose events */
130 1.1 sommerfe #define ABAT_F_PWRUNIT_MA 0x02 /* mA instead of mW */
131 1.15 tshiozak #define ABAT_F_PRESENT 0x04 /* is the battery present? */
132 1.15 tshiozak #define ABAT_F_LOCKED 0x08 /* is locked? */
133 1.15 tshiozak #define ABAT_F_DISCHARGING 0x10 /* discharging */
134 1.15 tshiozak #define ABAT_F_CHARGING 0x20 /* charging */
135 1.15 tshiozak #define ABAT_F_CRITICAL 0x40 /* charging */
136 1.15 tshiozak
137 1.15 tshiozak #define ABAT_SET(sc, f) (void)((sc)->sc_flags |= (f))
138 1.15 tshiozak #define ABAT_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f))
139 1.15 tshiozak #define ABAT_ISSET(sc, f) ((sc)->sc_flags & (f))
140 1.15 tshiozak
141 1.15 tshiozak /*
142 1.15 tshiozak * Available info level
143 1.15 tshiozak */
144 1.15 tshiozak
145 1.15 tshiozak #define ABAT_ALV_NONE 0 /* none is available */
146 1.15 tshiozak #define ABAT_ALV_PRESENCE 1 /* presence info is available */
147 1.15 tshiozak #define ABAT_ALV_INFO 2 /* battery info is available */
148 1.15 tshiozak #define ABAT_ALV_STAT 3 /* battery status is available */
149 1.15 tshiozak
150 1.15 tshiozak #define ABAT_ASSERT_LOCKED(sc) \
151 1.15 tshiozak do { \
152 1.15 tshiozak if (!((sc)->sc_flags & ABAT_F_LOCKED)) \
153 1.15 tshiozak panic("acpi_bat (expected to be locked)"); \
154 1.15 tshiozak } while(/*CONSTCOND*/0)
155 1.15 tshiozak #define ABAT_ASSERT_UNLOCKED(sc) \
156 1.15 tshiozak do { \
157 1.15 tshiozak if (((sc)->sc_flags & ABAT_F_LOCKED)) \
158 1.15 tshiozak panic("acpi_bat (expected to be unlocked)"); \
159 1.15 tshiozak } while(/*CONSTCOND*/0)
160 1.15 tshiozak #define ABAT_LOCK(sc, s) \
161 1.15 tshiozak do { \
162 1.15 tshiozak ABAT_ASSERT_UNLOCKED(sc); \
163 1.15 tshiozak (s) = splhigh(); \
164 1.15 tshiozak simple_lock(&(sc)->sc_lock); \
165 1.15 tshiozak ABAT_SET((sc), ABAT_F_LOCKED); \
166 1.15 tshiozak } while(/*CONSTCOND*/0)
167 1.15 tshiozak #define ABAT_UNLOCK(sc, s) \
168 1.15 tshiozak do { \
169 1.15 tshiozak ABAT_ASSERT_LOCKED(sc); \
170 1.15 tshiozak ABAT_CLEAR((sc), ABAT_F_LOCKED); \
171 1.15 tshiozak simple_unlock(&(sc)->sc_lock); \
172 1.15 tshiozak splx((s)); \
173 1.15 tshiozak } while(/*CONSTCOND*/0)
174 1.1 sommerfe
175 1.1 sommerfe int acpibat_match(struct device *, struct cfdata *, void *);
176 1.1 sommerfe void acpibat_attach(struct device *, struct device *, void *);
177 1.1 sommerfe
178 1.6 thorpej CFATTACH_DECL(acpibat, sizeof(struct acpibat_softc),
179 1.7 thorpej acpibat_match, acpibat_attach, NULL, NULL);
180 1.1 sommerfe
181 1.15 tshiozak static void acpibat_clear_presence(struct acpibat_softc *);
182 1.15 tshiozak static void acpibat_clear_info(struct acpibat_softc *);
183 1.15 tshiozak static void acpibat_clear_stat(struct acpibat_softc *);
184 1.15 tshiozak static int acpibat_battery_present(struct acpibat_softc *);
185 1.15 tshiozak static ACPI_STATUS acpibat_get_status(struct acpibat_softc *);
186 1.15 tshiozak static ACPI_STATUS acpibat_get_info(struct acpibat_softc *);
187 1.15 tshiozak static void acpibat_print_info(struct acpibat_softc *);
188 1.15 tshiozak static void acpibat_print_stat(struct acpibat_softc *);
189 1.15 tshiozak static void acpibat_update(void *);
190 1.15 tshiozak
191 1.14 explorer static void acpibat_init_envsys(struct acpibat_softc *);
192 1.15 tshiozak static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *context);
193 1.14 explorer static int acpibat_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
194 1.14 explorer static int acpibat_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
195 1.1 sommerfe
196 1.1 sommerfe /*
197 1.1 sommerfe * acpibat_match:
198 1.1 sommerfe *
199 1.1 sommerfe * Autoconfiguration `match' routine.
200 1.1 sommerfe */
201 1.1 sommerfe int
202 1.1 sommerfe acpibat_match(struct device *parent, struct cfdata *match, void *aux)
203 1.1 sommerfe {
204 1.1 sommerfe struct acpi_attach_args *aa = aux;
205 1.1 sommerfe
206 1.1 sommerfe if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
207 1.1 sommerfe return (0);
208 1.1 sommerfe
209 1.1 sommerfe if (strcmp(aa->aa_node->ad_devinfo.HardwareId, "PNP0C0A") == 0)
210 1.1 sommerfe return (1);
211 1.1 sommerfe
212 1.1 sommerfe return (0);
213 1.1 sommerfe }
214 1.1 sommerfe
215 1.1 sommerfe /*
216 1.1 sommerfe * acpibat_attach:
217 1.1 sommerfe *
218 1.1 sommerfe * Autoconfiguration `attach' routine.
219 1.1 sommerfe */
220 1.1 sommerfe void
221 1.1 sommerfe acpibat_attach(struct device *parent, struct device *self, void *aux)
222 1.1 sommerfe {
223 1.1 sommerfe struct acpibat_softc *sc = (void *) self;
224 1.1 sommerfe struct acpi_attach_args *aa = aux;
225 1.1 sommerfe ACPI_STATUS rv;
226 1.1 sommerfe
227 1.11 explorer printf(": ACPI Battery (Control Method)\n");
228 1.1 sommerfe
229 1.1 sommerfe sc->sc_node = aa->aa_node;
230 1.15 tshiozak simple_lock_init(&sc->sc_lock);
231 1.1 sommerfe
232 1.1 sommerfe rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
233 1.13 explorer ACPI_DEVICE_NOTIFY,
234 1.13 explorer acpibat_notify_handler, sc);
235 1.1 sommerfe if (rv != AE_OK) {
236 1.1 sommerfe printf("%s: unable to register DEVICE NOTIFY handler: %d\n",
237 1.13 explorer sc->sc_dev.dv_xname, rv);
238 1.1 sommerfe return;
239 1.1 sommerfe }
240 1.1 sommerfe
241 1.1 sommerfe /* XXX See acpibat_notify_handler() */
242 1.1 sommerfe rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
243 1.13 explorer ACPI_SYSTEM_NOTIFY,
244 1.13 explorer acpibat_notify_handler, sc);
245 1.1 sommerfe if (rv != AE_OK) {
246 1.1 sommerfe printf("%s: unable to register SYSTEM NOTIFY handler: %d\n",
247 1.13 explorer sc->sc_dev.dv_xname, rv);
248 1.1 sommerfe return;
249 1.1 sommerfe }
250 1.13 explorer
251 1.15 tshiozak #ifdef ACPI_BAT_DEBUG
252 1.15 tshiozak ABAT_SET(sc, ABAT_F_VERBOSE);
253 1.15 tshiozak #endif
254 1.1 sommerfe
255 1.14 explorer acpibat_init_envsys(sc);
256 1.1 sommerfe }
257 1.1 sommerfe
258 1.15 tshiozak /*
259 1.15 tshiozak * clear informations
260 1.15 tshiozak */
261 1.15 tshiozak
262 1.15 tshiozak void
263 1.15 tshiozak acpibat_clear_presence(struct acpibat_softc *sc)
264 1.15 tshiozak {
265 1.15 tshiozak
266 1.15 tshiozak ABAT_ASSERT_LOCKED(sc);
267 1.15 tshiozak
268 1.15 tshiozak acpibat_clear_info(sc);
269 1.15 tshiozak sc->sc_available = ABAT_ALV_NONE;
270 1.15 tshiozak ABAT_CLEAR(sc, ABAT_F_PRESENT);
271 1.15 tshiozak }
272 1.15 tshiozak
273 1.15 tshiozak void
274 1.15 tshiozak acpibat_clear_info(struct acpibat_softc *sc)
275 1.15 tshiozak {
276 1.15 tshiozak
277 1.15 tshiozak ABAT_ASSERT_LOCKED(sc);
278 1.15 tshiozak
279 1.15 tshiozak acpibat_clear_stat(sc);
280 1.16 tshiozak if (sc->sc_available>ABAT_ALV_PRESENCE)
281 1.16 tshiozak sc->sc_available = ABAT_ALV_PRESENCE;
282 1.15 tshiozak sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s = 0;
283 1.15 tshiozak sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s = 0;
284 1.15 tshiozak sc->sc_data[ACPIBAT_LFCCAPACITY].max.data_s = 0;
285 1.15 tshiozak sc->sc_data[ACPIBAT_CAPACITY].max.data_s = 0;
286 1.15 tshiozak sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s = 0;
287 1.15 tshiozak sc->sc_data[ACPIBAT_DVOLTAGE].cur.data_s = 0;
288 1.15 tshiozak sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s = 0;
289 1.15 tshiozak sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s = 0;
290 1.15 tshiozak }
291 1.15 tshiozak
292 1.15 tshiozak void
293 1.15 tshiozak acpibat_clear_stat(struct acpibat_softc *sc)
294 1.1 sommerfe {
295 1.15 tshiozak
296 1.15 tshiozak ABAT_ASSERT_LOCKED(sc);
297 1.15 tshiozak
298 1.16 tshiozak if (sc->sc_available>ABAT_ALV_INFO)
299 1.16 tshiozak sc->sc_available = ABAT_ALV_INFO;
300 1.15 tshiozak sc->sc_data[ACPIBAT_LOAD].cur.data_s = 0;
301 1.15 tshiozak sc->sc_data[ACPIBAT_CAPACITY].cur.data_s = 0;
302 1.15 tshiozak sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s = 0;
303 1.15 tshiozak sc->sc_data[ACPIBAT_CAPACITY].warnflags = 0;
304 1.15 tshiozak sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s = 0;
305 1.15 tshiozak sc->sc_data[ACPIBAT_CHARGING].cur.data_s = 0;
306 1.1 sommerfe }
307 1.1 sommerfe
308 1.15 tshiozak
309 1.13 explorer /*
310 1.13 explorer * returns 0 for no battery, 1 for present, and -1 on error
311 1.13 explorer */
312 1.15 tshiozak int
313 1.15 tshiozak acpibat_battery_present(struct acpibat_softc *sc)
314 1.13 explorer {
315 1.13 explorer u_int32_t sta;
316 1.20 kochi int s, val;
317 1.13 explorer ACPI_STATUS rv;
318 1.13 explorer
319 1.20 kochi rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val);
320 1.13 explorer if (rv != AE_OK) {
321 1.13 explorer printf("%s: failed to evaluate _STA: %x\n",
322 1.13 explorer sc->sc_dev.dv_xname, rv);
323 1.13 explorer return (-1);
324 1.13 explorer }
325 1.13 explorer
326 1.20 kochi sta = (u_int32_t)val;
327 1.13 explorer
328 1.15 tshiozak ABAT_LOCK(sc, s);
329 1.15 tshiozak sc->sc_available = ABAT_ALV_PRESENCE;
330 1.15 tshiozak if (sta & ACPIBAT_STA_PRESENT) {
331 1.15 tshiozak ABAT_SET(sc, ABAT_F_PRESENT);
332 1.15 tshiozak sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 1;
333 1.15 tshiozak } else
334 1.15 tshiozak sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 0;
335 1.15 tshiozak ABAT_UNLOCK(sc, s);
336 1.13 explorer
337 1.15 tshiozak return ((sta & ACPIBAT_STA_PRESENT)?1:0);
338 1.13 explorer }
339 1.1 sommerfe
340 1.1 sommerfe /*
341 1.1 sommerfe * acpibat_get_info
342 1.1 sommerfe *
343 1.1 sommerfe * Get, and possibly display, the battery info.
344 1.1 sommerfe */
345 1.1 sommerfe
346 1.15 tshiozak ACPI_STATUS
347 1.15 tshiozak acpibat_get_info(struct acpibat_softc *sc)
348 1.1 sommerfe {
349 1.1 sommerfe ACPI_OBJECT *p1, *p2;
350 1.1 sommerfe ACPI_STATUS rv;
351 1.1 sommerfe ACPI_BUFFER buf;
352 1.15 tshiozak int capunit, rateunit, s;
353 1.15 tshiozak const char *capstring, *ratestring;
354 1.13 explorer
355 1.1 sommerfe rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
356 1.1 sommerfe if (rv != AE_OK) {
357 1.15 tshiozak printf("%s: failed to evaluate _BIF: 0x%x\n",
358 1.1 sommerfe sc->sc_dev.dv_xname, rv);
359 1.15 tshiozak return (rv);
360 1.1 sommerfe }
361 1.1 sommerfe p1 = (ACPI_OBJECT *)buf.Pointer;
362 1.1 sommerfe if (p1->Type != ACPI_TYPE_PACKAGE) {
363 1.1 sommerfe printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname,
364 1.1 sommerfe p1->Type);
365 1.1 sommerfe goto out;
366 1.1 sommerfe }
367 1.8 jmcneill if (p1->Package.Count < 13) {
368 1.1 sommerfe printf("%s: expected 13 elts, got %d\n",
369 1.1 sommerfe sc->sc_dev.dv_xname, p1->Package.Count);
370 1.8 jmcneill goto out;
371 1.8 jmcneill }
372 1.1 sommerfe
373 1.15 tshiozak #define INITDATA(index, unit, string) \
374 1.15 tshiozak sc->sc_data[index].units = unit; \
375 1.15 tshiozak sc->sc_info[index].units = unit; \
376 1.15 tshiozak snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \
377 1.15 tshiozak "%s %s", sc->sc_dev.dv_xname, string); \
378 1.15 tshiozak
379 1.15 tshiozak ABAT_LOCK(sc, s);
380 1.1 sommerfe p2 = p1->Package.Elements;
381 1.15 tshiozak /*
382 1.15 tshiozak * XXX: It seems that the below attributes should not be overriden
383 1.15 tshiozak * in such manner... we should unregister them for a while, maybe.
384 1.15 tshiozak */
385 1.15 tshiozak if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) {
386 1.15 tshiozak ABAT_SET(sc, ABAT_F_PWRUNIT_MA);
387 1.15 tshiozak sc->sc_sysmon.sme_ranges = acpibat_range_amp;
388 1.15 tshiozak capunit = ENVSYS_SAMPHOUR;
389 1.15 tshiozak capstring = "charge";
390 1.15 tshiozak rateunit = ENVSYS_SAMPS;
391 1.15 tshiozak ratestring = "current";
392 1.15 tshiozak } else {
393 1.15 tshiozak ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA);
394 1.15 tshiozak sc->sc_sysmon.sme_ranges = acpibat_range_watt;
395 1.15 tshiozak capunit = ENVSYS_SWATTHOUR;
396 1.15 tshiozak capstring = "energy";
397 1.15 tshiozak rateunit = ENVSYS_SWATTS;
398 1.15 tshiozak ratestring = "power";
399 1.15 tshiozak }
400 1.15 tshiozak INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap");
401 1.15 tshiozak INITDATA(ACPIBAT_LFCCAPACITY, capunit, "lfc cap");
402 1.15 tshiozak INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap");
403 1.15 tshiozak INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap");
404 1.15 tshiozak INITDATA(ACPIBAT_LOAD, rateunit, ratestring);
405 1.15 tshiozak INITDATA(ACPIBAT_CAPACITY, capunit, capstring);
406 1.1 sommerfe
407 1.14 explorer sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s = p2[1].Integer.Value * 1000;
408 1.14 explorer sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s = p2[2].Integer.Value * 1000;
409 1.14 explorer sc->sc_data[ACPIBAT_LFCCAPACITY].max.data_s = p2[1].Integer.Value * 1000;
410 1.14 explorer sc->sc_data[ACPIBAT_CAPACITY].max.data_s = p2[1].Integer.Value * 1000;
411 1.14 explorer sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s = p2[3].Integer.Value;
412 1.14 explorer sc->sc_data[ACPIBAT_DVOLTAGE].cur.data_s = p2[4].Integer.Value * 1000;
413 1.14 explorer sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s = p2[5].Integer.Value * 1000;
414 1.14 explorer sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s = p2[6].Integer.Value * 1000;
415 1.15 tshiozak sc->sc_available = ABAT_ALV_INFO;
416 1.15 tshiozak ABAT_UNLOCK(sc, s);
417 1.15 tshiozak
418 1.15 tshiozak if ((sc->sc_flags & ABAT_F_VERBOSE))
419 1.15 tshiozak printf("%s: %s %s %s %s\n",
420 1.15 tshiozak sc->sc_dev.dv_xname,
421 1.15 tshiozak p2[12].String.Pointer, p2[11].String.Pointer,
422 1.15 tshiozak p2[9].String.Pointer, p2[10].String.Pointer);
423 1.1 sommerfe
424 1.15 tshiozak rv = AE_OK;
425 1.1 sommerfe
426 1.1 sommerfe out:
427 1.1 sommerfe AcpiOsFree(buf.Pointer);
428 1.15 tshiozak return (rv);
429 1.1 sommerfe }
430 1.1 sommerfe
431 1.1 sommerfe /*
432 1.1 sommerfe * acpibat_get_status:
433 1.1 sommerfe *
434 1.1 sommerfe * Get, and possibly display, the current battery line status.
435 1.1 sommerfe */
436 1.15 tshiozak ACPI_STATUS
437 1.15 tshiozak acpibat_get_status(struct acpibat_softc *sc)
438 1.1 sommerfe {
439 1.15 tshiozak int flags, status, s;
440 1.1 sommerfe ACPI_OBJECT *p1, *p2;
441 1.1 sommerfe ACPI_STATUS rv;
442 1.1 sommerfe ACPI_BUFFER buf;
443 1.1 sommerfe
444 1.20 kochi rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf);
445 1.1 sommerfe if (rv != AE_OK) {
446 1.15 tshiozak printf("bat: failed to evaluate _BST: 0x%x\n", rv);
447 1.15 tshiozak return (rv);
448 1.1 sommerfe }
449 1.1 sommerfe p1 = (ACPI_OBJECT *)buf.Pointer;
450 1.1 sommerfe
451 1.1 sommerfe if (p1->Type != ACPI_TYPE_PACKAGE) {
452 1.1 sommerfe printf("bat: expected PACKAGE, got %d\n", p1->Type);
453 1.20 kochi rv = AE_ERROR;
454 1.20 kochi goto out;
455 1.1 sommerfe }
456 1.10 jmcneill if (p1->Package.Count < 4) {
457 1.1 sommerfe printf("bat: expected 4 elts, got %d\n", p1->Package.Count);
458 1.20 kochi rv = AE_ERROR;
459 1.20 kochi goto out;
460 1.10 jmcneill }
461 1.1 sommerfe p2 = p1->Package.Elements;
462 1.1 sommerfe
463 1.15 tshiozak ABAT_LOCK(sc, s);
464 1.15 tshiozak status = p2[0].Integer.Value;
465 1.14 explorer sc->sc_data[ACPIBAT_LOAD].cur.data_s = p2[1].Integer.Value * 1000;
466 1.14 explorer sc->sc_data[ACPIBAT_CAPACITY].cur.data_s = p2[2].Integer.Value * 1000;
467 1.14 explorer sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s = p2[3].Integer.Value * 1000;
468 1.14 explorer
469 1.14 explorer flags = 0;
470 1.15 tshiozak if (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s <
471 1.15 tshiozak sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s)
472 1.14 explorer flags |= ENVSYS_WARN_UNDER;
473 1.15 tshiozak if (status & ACPIBAT_ST_CRITICAL)
474 1.14 explorer flags |= ENVSYS_WARN_CRITUNDER;
475 1.14 explorer sc->sc_data[ACPIBAT_CAPACITY].warnflags = flags;
476 1.15 tshiozak sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s =
477 1.15 tshiozak ((status & ACPIBAT_ST_DISCHARGING) != 0);
478 1.15 tshiozak sc->sc_data[ACPIBAT_CHARGING].cur.data_s =
479 1.15 tshiozak ((status & ACPIBAT_ST_CHARGING) != 0);
480 1.15 tshiozak sc->sc_available = ABAT_ALV_STAT;
481 1.15 tshiozak ABAT_UNLOCK(sc, s);
482 1.15 tshiozak
483 1.20 kochi rv = AE_OK;
484 1.20 kochi out:
485 1.20 kochi AcpiOsFree(buf.Pointer);
486 1.20 kochi return (rv);
487 1.15 tshiozak }
488 1.15 tshiozak
489 1.15 tshiozak #define SCALE(x) ((x)/1000000), (((x)%1000000)/1000)
490 1.15 tshiozak #define CAPUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"mAh":"mWh")
491 1.15 tshiozak #define RATEUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"mA":"mW")
492 1.15 tshiozak static void
493 1.15 tshiozak acpibat_print_info(struct acpibat_softc *sc)
494 1.15 tshiozak {
495 1.15 tshiozak const char *tech;
496 1.15 tshiozak
497 1.15 tshiozak if (sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s)
498 1.15 tshiozak tech = "secondary";
499 1.15 tshiozak else
500 1.15 tshiozak tech = "primary";
501 1.15 tshiozak
502 1.15 tshiozak printf("%s: %s battery, Design %d.%03d%s, Predicted %d.%03d%s"
503 1.15 tshiozak "Warn %d.%03d%s Low %d.%03d%s\n",
504 1.15 tshiozak sc->sc_dev.dv_xname, tech,
505 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s), CAPUNITS(sc),
506 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s),CAPUNITS(sc),
507 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s), CAPUNITS(sc),
508 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s), CAPUNITS(sc));
509 1.15 tshiozak }
510 1.15 tshiozak
511 1.15 tshiozak static void
512 1.15 tshiozak acpibat_print_stat(struct acpibat_softc *sc)
513 1.15 tshiozak {
514 1.15 tshiozak const char *capstat, *chargestat;
515 1.15 tshiozak int percent;
516 1.20 kochi
517 1.20 kochi percent = 0;
518 1.15 tshiozak
519 1.15 tshiozak if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_CRITUNDER)
520 1.15 tshiozak capstat = "CRITICAL ";
521 1.15 tshiozak else if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_UNDER)
522 1.15 tshiozak capstat = "UNDER ";
523 1.15 tshiozak else
524 1.15 tshiozak capstat = "";
525 1.15 tshiozak if (sc->sc_data[ACPIBAT_CHARGING].cur.data_s)
526 1.15 tshiozak chargestat = "charging";
527 1.15 tshiozak else if (sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s)
528 1.15 tshiozak chargestat = "discharging";
529 1.15 tshiozak else
530 1.15 tshiozak chargestat = "idling";
531 1.15 tshiozak if (sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s>0)
532 1.15 tshiozak percent =
533 1.15 tshiozak (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s*100)/
534 1.15 tshiozak sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s;
535 1.15 tshiozak printf("%s: %s%s: %d.%03dV cap %d.%03d%s (%d%%) rate %d.%03d%s\n",
536 1.15 tshiozak sc->sc_dev.dv_xname,
537 1.15 tshiozak capstat, chargestat,
538 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s),
539 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_CAPACITY].cur.data_s), CAPUNITS(sc),
540 1.15 tshiozak percent,
541 1.15 tshiozak SCALE(sc->sc_data[ACPIBAT_LOAD].cur.data_s), RATEUNITS(sc));
542 1.15 tshiozak }
543 1.15 tshiozak
544 1.15 tshiozak static void
545 1.15 tshiozak acpibat_update(void *arg)
546 1.15 tshiozak {
547 1.15 tshiozak struct acpibat_softc *sc = arg;
548 1.15 tshiozak
549 1.15 tshiozak if (sc->sc_available < ABAT_ALV_INFO) {
550 1.15 tshiozak /* current information is invalid */
551 1.16 tshiozak #if 0
552 1.16 tshiozak /*
553 1.16 tshiozak * XXX: The driver sometimes unaware that the battery exist.
554 1.16 tshiozak * (i.e. just after the boot or resuming)
555 1.16 tshiozak * Thus, the driver should always check it here.
556 1.16 tshiozak */
557 1.15 tshiozak if (sc->sc_available < ABAT_ALV_PRESENCE)
558 1.16 tshiozak #endif
559 1.15 tshiozak /* presence is invalid */
560 1.16 tshiozak if (acpibat_battery_present(sc)<0) {
561 1.15 tshiozak /* error */
562 1.16 tshiozak printf("%s: cannot get battery presence.\n",
563 1.16 tshiozak sc->sc_dev.dv_xname);
564 1.15 tshiozak return;
565 1.16 tshiozak }
566 1.15 tshiozak if (ABAT_ISSET(sc, ABAT_F_PRESENT)) {
567 1.15 tshiozak /* the battery is present. */
568 1.15 tshiozak if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
569 1.15 tshiozak printf("%s: battery is present.\n",
570 1.15 tshiozak sc->sc_dev.dv_xname);
571 1.15 tshiozak if (ACPI_FAILURE(acpibat_get_info(sc)))
572 1.15 tshiozak return;
573 1.15 tshiozak if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
574 1.15 tshiozak acpibat_print_info(sc);
575 1.15 tshiozak } else {
576 1.15 tshiozak /* the battery is not present. */
577 1.15 tshiozak if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
578 1.15 tshiozak printf("%s: battery is not present.\n",
579 1.15 tshiozak sc->sc_dev.dv_xname);
580 1.15 tshiozak return;
581 1.15 tshiozak }
582 1.15 tshiozak } else {
583 1.15 tshiozak /* current information is valid */
584 1.16 tshiozak if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) {
585 1.15 tshiozak /* the battery is not present. */
586 1.15 tshiozak return;
587 1.16 tshiozak }
588 1.15 tshiozak }
589 1.15 tshiozak
590 1.15 tshiozak if (ACPI_FAILURE(acpibat_get_status(sc)))
591 1.15 tshiozak return;
592 1.15 tshiozak
593 1.15 tshiozak if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
594 1.15 tshiozak acpibat_print_stat(sc);
595 1.1 sommerfe }
596 1.1 sommerfe
597 1.1 sommerfe /*
598 1.1 sommerfe * acpibat_notify_handler:
599 1.1 sommerfe *
600 1.1 sommerfe * Callback from ACPI interrupt handler to notify us of an event.
601 1.1 sommerfe */
602 1.1 sommerfe void
603 1.1 sommerfe acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
604 1.1 sommerfe {
605 1.1 sommerfe struct acpibat_softc *sc = context;
606 1.15 tshiozak int rv, s;
607 1.1 sommerfe
608 1.11 explorer #ifdef ACPI_BAT_DEBUG
609 1.11 explorer printf("%s: received notify message: 0x%x\n",
610 1.11 explorer sc->sc_dev.dv_xname, notify);
611 1.11 explorer #endif
612 1.11 explorer
613 1.1 sommerfe switch (notify) {
614 1.1 sommerfe case ACPI_NOTIFY_BusCheck:
615 1.11 explorer break;
616 1.11 explorer
617 1.11 explorer case ACPI_NOTIFY_BatteryInformationChanged:
618 1.15 tshiozak ABAT_LOCK(sc, s);
619 1.15 tshiozak acpibat_clear_presence(sc);
620 1.15 tshiozak ABAT_UNLOCK(sc, s);
621 1.11 explorer rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
622 1.15 tshiozak acpibat_update, sc);
623 1.11 explorer if (rv != AE_OK)
624 1.11 explorer printf("%s: unable to queue status check: %d\n",
625 1.11 explorer sc->sc_dev.dv_xname, rv);
626 1.13 explorer break;
627 1.11 explorer
628 1.1 sommerfe case ACPI_NOTIFY_BatteryStatusChanged:
629 1.15 tshiozak ABAT_LOCK(sc, s);
630 1.15 tshiozak acpibat_clear_stat(sc);
631 1.15 tshiozak ABAT_UNLOCK(sc, s);
632 1.1 sommerfe rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
633 1.15 tshiozak acpibat_update, sc);
634 1.1 sommerfe if (rv != AE_OK)
635 1.1 sommerfe printf("%s: unable to queue status check: %d\n",
636 1.11 explorer sc->sc_dev.dv_xname, rv);
637 1.1 sommerfe break;
638 1.11 explorer
639 1.1 sommerfe default:
640 1.1 sommerfe printf("%s: received unknown notify message: 0x%x\n",
641 1.11 explorer sc->sc_dev.dv_xname, notify);
642 1.1 sommerfe }
643 1.14 explorer }
644 1.14 explorer
645 1.15 tshiozak void
646 1.14 explorer acpibat_init_envsys(struct acpibat_softc *sc)
647 1.14 explorer {
648 1.14 explorer int capunit, rateunit, i;
649 1.14 explorer const char *capstring, *ratestring;
650 1.14 explorer
651 1.15 tshiozak #if 0
652 1.14 explorer if (sc->sc_flags & ABAT_F_PWRUNIT_MA) {
653 1.15 tshiozak #endif
654 1.15 tshiozak /* XXX */
655 1.14 explorer sc->sc_sysmon.sme_ranges = acpibat_range_amp;
656 1.14 explorer capunit = ENVSYS_SAMPHOUR;
657 1.14 explorer capstring = "charge";
658 1.14 explorer rateunit = ENVSYS_SAMPS;
659 1.14 explorer ratestring = "current";
660 1.15 tshiozak #if 0
661 1.14 explorer } else {
662 1.14 explorer sc->sc_sysmon.sme_ranges = acpibat_range_watt;
663 1.14 explorer capunit = ENVSYS_SWATTHOUR;
664 1.14 explorer capstring = "energy";
665 1.14 explorer rateunit = ENVSYS_SWATTS;
666 1.14 explorer ratestring = "power";
667 1.14 explorer }
668 1.15 tshiozak #endif
669 1.14 explorer
670 1.14 explorer for (i = 0 ; i < ACPIBAT_NSENSORS; i++) {
671 1.14 explorer sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
672 1.14 explorer sc->sc_data[i].validflags |= (ENVSYS_FVALID | ENVSYS_FCURVALID);
673 1.14 explorer sc->sc_info[i].validflags = ENVSYS_FVALID;
674 1.14 explorer sc->sc_data[i].warnflags = 0;
675 1.14 explorer }
676 1.15 tshiozak INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present");
677 1.14 explorer INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap");
678 1.14 explorer INITDATA(ACPIBAT_LFCCAPACITY, capunit, "lfc cap");
679 1.14 explorer INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology");
680 1.14 explorer INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage");
681 1.14 explorer INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap");
682 1.14 explorer INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap");
683 1.14 explorer INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage");
684 1.14 explorer INITDATA(ACPIBAT_LOAD, rateunit, ratestring);
685 1.14 explorer INITDATA(ACPIBAT_CAPACITY, capunit, capstring);
686 1.14 explorer INITDATA(ACPIBAT_CHARGING, ENVSYS_INDICATOR, "charging");
687 1.14 explorer INITDATA(ACPIBAT_DISCHARGING, ENVSYS_INDICATOR, "discharging");
688 1.14 explorer
689 1.14 explorer /*
690 1.14 explorer * ACPIBAT_CAPACITY is the "gas gauge".
691 1.14 explorer * ACPIBAT_LFCCAPACITY is the "wear gauge".
692 1.14 explorer */
693 1.14 explorer sc->sc_data[ACPIBAT_CAPACITY].validflags |=
694 1.14 explorer ENVSYS_FMAXVALID | ENVSYS_FFRACVALID;
695 1.14 explorer sc->sc_data[ACPIBAT_LFCCAPACITY].validflags |=
696 1.14 explorer ENVSYS_FMAXVALID | ENVSYS_FFRACVALID;
697 1.14 explorer
698 1.14 explorer sc->sc_sysmon.sme_sensor_info = sc->sc_info;
699 1.14 explorer sc->sc_sysmon.sme_sensor_data = sc->sc_data;
700 1.14 explorer sc->sc_sysmon.sme_cookie = sc;
701 1.14 explorer sc->sc_sysmon.sme_gtredata = acpibat_gtredata;
702 1.14 explorer sc->sc_sysmon.sme_streinfo = acpibat_streinfo;
703 1.14 explorer sc->sc_sysmon.sme_nsensors = ACPIBAT_NSENSORS;
704 1.14 explorer sc->sc_sysmon.sme_envsys_version = 1000;
705 1.14 explorer
706 1.14 explorer if (sysmon_envsys_register(&sc->sc_sysmon))
707 1.14 explorer printf("%s: unable to register with sysmon\n",
708 1.14 explorer sc->sc_dev.dv_xname);
709 1.14 explorer }
710 1.14 explorer
711 1.14 explorer int
712 1.14 explorer acpibat_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
713 1.14 explorer {
714 1.14 explorer struct acpibat_softc *sc = sme->sme_cookie;
715 1.14 explorer
716 1.16 tshiozak acpibat_update(sc);
717 1.15 tshiozak
718 1.14 explorer /* XXX locking */
719 1.16 tshiozak /* XXX it should be checked whether info/stat is valid. */
720 1.14 explorer *tred = sc->sc_data[tred->sensor];
721 1.14 explorer /* XXX locking */
722 1.14 explorer
723 1.14 explorer return (0);
724 1.14 explorer }
725 1.14 explorer
726 1.14 explorer int
727 1.14 explorer acpibat_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
728 1.14 explorer {
729 1.14 explorer
730 1.14 explorer /* XXX Not implemented */
731 1.14 explorer binfo->validflags = 0;
732 1.14 explorer
733 1.14 explorer return (0);
734 1.1 sommerfe }
735