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