97dfbbae8e9cc33738ad35129df3a4f5eef9d602
[dragonfly.git] / sys / dev / acpica / acpi_smbat.c
1 /*-
2  * Copyright (c) 2005 Hans Petter Selasky
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/acpica/acpi_smbat.c,v 1.11 2011/11/07 15:43:11 ed Exp $
27  */
28
29 #include "opt_acpi.h"
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34
35 #include "acpi.h"
36 #include <acpivar.h>
37 #include <acpiio.h>
38 #include <acpi_smbus.h>
39
40 /* Transactions have failed after 500 ms. */
41 #define SMBUS_TIMEOUT   50
42
43 struct acpi_smbat_softc {
44         uint8_t         sb_base_addr;
45         device_t        ec_dev;
46
47         struct acpi_bif bif;
48         struct acpi_bst bst;
49         struct timespec bif_lastupdated;
50         struct timespec bst_lastupdated;
51 };
52
53 static int      acpi_smbat_probe(device_t dev);
54 static int      acpi_smbat_attach(device_t dev);
55 static int      acpi_smbat_shutdown(device_t dev);
56 static int      acpi_smbat_info_expired(struct timespec *lastupdated);
57 static void     acpi_smbat_info_updated(struct timespec *lastupdated);
58 static int      acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif);
59 static int      acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst);
60
61 ACPI_SERIAL_DECL(smbat, "ACPI Smart Battery");
62
63 SYSCTL_DECL(_debug_acpi);
64 static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL,
65     "Battery debugging");
66
67 /* On some laptops with smart batteries, enabling battery monitoring
68  * software causes keystrokes from atkbd to be lost.  This has also been
69  * reported on Linux, and is apparently due to the keyboard and I2C line
70  * for the battery being routed through the same chip.  Whether that's
71  * accurate or not, adding extra sleeps to the status checking code
72  * causes the problem to go away.
73  *
74  * If you experience that problem, try a value of 10ms and move up
75  * from there.
76  */
77 static int      batt_sleep_ms;
78 SYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0,
79     "Sleep during battery status updates to prevent keystroke loss.");
80
81 static device_method_t acpi_smbat_methods[] = {
82         /* device interface */
83         DEVMETHOD(device_probe, acpi_smbat_probe),
84         DEVMETHOD(device_attach, acpi_smbat_attach),
85         DEVMETHOD(device_shutdown, acpi_smbat_shutdown),
86
87         /* ACPI battery interface */
88         DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst),
89         DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif),
90
91         DEVMETHOD_END
92 };
93
94 static driver_t acpi_smbat_driver = {
95         "battery",
96         acpi_smbat_methods,
97         sizeof(struct acpi_smbat_softc),
98 };
99
100 static devclass_t acpi_smbat_devclass;
101 DRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass,
102     NULL, NULL);
103 MODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1);
104
105 static int
106 acpi_smbat_probe(device_t dev)
107 {
108         static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL};
109         ACPI_STATUS status;
110
111         if (acpi_disabled("smbat") ||
112             ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL)
113                 return (ENXIO);
114         status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL);
115         if (ACPI_FAILURE(status))
116                 return (ENXIO);
117
118         device_set_desc(dev, "ACPI Smart Battery");
119         return (0);
120 }
121
122 static int
123 acpi_smbat_attach(device_t dev)
124 {
125         struct acpi_smbat_softc *sc;
126         uint32_t base;
127
128         sc = device_get_softc(dev);
129         if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) {
130                 device_printf(dev, "cannot get EC base address\n");
131                 return (ENXIO);
132         }
133         sc->sb_base_addr = (base >> 8) & 0xff;
134
135         /* XXX Only works with one EC, but nearly all systems only have one. */
136         sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0);
137         if (sc->ec_dev == NULL) {
138                 device_printf(dev, "cannot find EC device\n");
139                 return (ENXIO);
140         }
141
142         timespecclear(&sc->bif_lastupdated);
143         timespecclear(&sc->bst_lastupdated);
144
145         if (acpi_battery_register(dev) != 0) {
146                 device_printf(dev, "cannot register battery\n");
147                 return (ENXIO);
148         }
149         return (0);
150 }
151
152 static int
153 acpi_smbat_shutdown(device_t dev)
154 {
155
156         acpi_battery_remove(dev);
157         return (0);
158 }
159
160 static int
161 acpi_smbat_info_expired(struct timespec *lastupdated)
162 {
163         struct timespec curtime;
164
165         ACPI_SERIAL_ASSERT(smbat);
166
167         if (lastupdated == NULL)
168                 return (TRUE);
169         if (!timespecisset(lastupdated))
170                 return (TRUE);
171
172         getnanotime(&curtime);
173         timespecsub(&curtime, lastupdated);
174         return (curtime.tv_sec < 0 ||
175             curtime.tv_sec > acpi_battery_get_info_expire());
176 }
177
178 static void
179 acpi_smbat_info_updated(struct timespec *lastupdated)
180 {
181
182         ACPI_SERIAL_ASSERT(smbat);
183
184         if (lastupdated != NULL)
185                 getnanotime(lastupdated);
186 }
187
188 static int
189 acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
190     uint16_t *ptr)
191 {
192         int error, to;
193         UINT64 val;
194
195         ACPI_SERIAL_ASSERT(smbat);
196
197         if (batt_sleep_ms)
198             AcpiOsSleep(batt_sleep_ms);
199
200         val = addr;
201         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
202             val, 1);
203         if (error)
204                 goto out;
205
206         val = cmd;
207         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
208             val, 1);
209         if (error)
210                 goto out;
211
212         val = 0x09; /* | 0x80 if PEC */
213         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
214             val, 1);
215         if (error)
216                 goto out;
217
218         if (batt_sleep_ms)
219             AcpiOsSleep(batt_sleep_ms);
220
221         for (to = SMBUS_TIMEOUT; to != 0; to--) {
222                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
223                     &val, 1);
224                 if (error)
225                         goto out;
226                 if (val == 0)
227                         break;
228                 AcpiOsSleep(10);
229         }
230         if (to == 0) {
231                 error = ETIMEDOUT;
232                 goto out;
233         }
234
235         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
236         if (error)
237                 goto out;
238         if (val & SMBUS_STS_MASK) {
239                 kprintf("%s: AE_ERROR 0x%x\n",
240                        __func__, (int)(val & SMBUS_STS_MASK));
241                 error = EIO;
242                 goto out;
243         }
244
245         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA,
246             &val, 2);
247         if (error)
248                 goto out;
249
250         *ptr = val;
251
252 out:
253         return (error);
254 }
255
256 static int
257 acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
258     uint8_t *ptr, uint16_t len)
259 {
260         UINT64 val;
261         uint8_t to;
262         int error;
263
264         ACPI_SERIAL_ASSERT(smbat);
265
266         if (batt_sleep_ms)
267             AcpiOsSleep(batt_sleep_ms);
268
269         val = addr;
270         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
271             val, 1);
272         if (error)
273                 goto out;
274
275         val = cmd;
276         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
277             val, 1);
278         if (error)
279                 goto out;
280
281         val = 0x0B /* | 0x80 if PEC */ ;
282         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
283             val, 1);
284         if (error)
285                 goto out;
286
287         if (batt_sleep_ms)
288             AcpiOsSleep(batt_sleep_ms);
289
290         for (to = SMBUS_TIMEOUT; to != 0; to--) {
291                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
292                     &val, 1);
293                 if (error)
294                         goto out;
295                 if (val == 0)
296                         break;
297                 AcpiOsSleep(10);
298         }
299         if (to == 0) {
300                 error = ETIMEDOUT;
301                 goto out;
302         }
303
304         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
305         if (error)
306                 goto out;
307         if (val & SMBUS_STS_MASK) {
308                 kprintf("%s: AE_ERROR 0x%x\n",
309                        __func__, (int)(val & SMBUS_STS_MASK));
310                 error = EIO;
311                 goto out;
312         }
313
314         /* get length */
315         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT,
316             &val, 1);
317         if (error)
318                 goto out;
319         val = (val & 0x1f) + 1;
320
321         bzero(ptr, len);
322         if (len > val)
323                 len = val;
324
325         if (batt_sleep_ms)
326             AcpiOsSleep(batt_sleep_ms);
327
328         while (len--) {
329                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA
330                     + len, &val, 1);
331                 if (error)
332                         goto out;
333
334                 ptr[len] = val;
335                 if (batt_sleep_ms)
336                     AcpiOsSleep(batt_sleep_ms);
337         }
338
339 out:
340         return (error);
341 }
342
343 static int
344 acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst)
345 {
346         struct acpi_smbat_softc *sc;
347         int error;
348         uint32_t factor;
349         int16_t val;
350         uint8_t addr;
351
352         ACPI_SERIAL_BEGIN(smbat);
353
354         addr = SMBATT_ADDRESS;
355         error = ENXIO;
356         sc = device_get_softc(dev);
357
358         if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) {
359                 error = 0;
360                 goto out;
361         }
362
363         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
364                 goto out;
365         if (val & SMBATT_BM_CAPACITY_MODE)
366                 factor = 10;
367         else
368                 factor = 1;
369
370         /* get battery status */
371         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val))
372                 goto out;
373
374         sc->bst.state = 0;
375         if (val & SMBATT_BS_DISCHARGING)
376                 sc->bst.state |= ACPI_BATT_STAT_DISCHARG;
377
378         if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM)
379                 sc->bst.state |= ACPI_BATT_STAT_CRITICAL;
380
381         /*
382          * If the rate is negative, it is discharging.  Otherwise,
383          * it is charging.
384          */
385         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val))
386                 goto out;
387
388         if (val > 0) {
389                 sc->bst.rate = val * factor;
390                 sc->bst.state &= ~SMBATT_BS_DISCHARGING;
391                 sc->bst.state |= ACPI_BATT_STAT_CHARGING;
392         } else if (val < 0)
393                 sc->bst.rate = (-val) * factor;
394         else
395                 sc->bst.rate = 0;
396
397         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val))
398                 goto out;
399         sc->bst.cap = val * factor;
400
401         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val))
402                 goto out;
403         sc->bst.volt = val;
404
405         acpi_smbat_info_updated(&sc->bst_lastupdated);
406         error = 0;
407
408 out:
409         if (error == 0)
410                 memcpy(bst, &sc->bst, sizeof(sc->bst));
411         ACPI_SERIAL_END(smbat);
412         return (error);
413 }
414
415 static int
416 acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
417 {
418         struct acpi_smbat_softc *sc;
419         int error;
420         uint32_t factor;
421         uint16_t val;
422         uint8_t addr;
423
424         ACPI_SERIAL_BEGIN(smbat);
425
426         addr = SMBATT_ADDRESS;
427         error = ENXIO;
428         sc = device_get_softc(dev);
429
430         if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) {
431                 error = 0;
432                 goto out;
433         }
434
435         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
436                 goto out;
437         if (val & SMBATT_BM_CAPACITY_MODE) {
438                 factor = 10;
439                 sc->bif.units = ACPI_BIF_UNITS_MW;
440         } else {
441                 factor = 1;
442                 sc->bif.units = ACPI_BIF_UNITS_MA;
443         }
444
445         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val))
446                 goto out;
447         sc->bif.dcap = val * factor;
448
449         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val))
450                 goto out;
451         sc->bif.lfcap = val * factor;
452         sc->bif.btech = 1;              /* secondary (rechargeable) */
453
454         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val))
455                 goto out;
456         sc->bif.dvol = val;
457
458         sc->bif.wcap = sc->bif.dcap / 10;
459         sc->bif.lcap = sc->bif.dcap / 10;
460
461         sc->bif.gra1 = factor;  /* not supported */
462         sc->bif.gra2 = factor;  /* not supported */
463
464         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME,
465             sc->bif.model, sizeof(sc->bif.model)))
466                 goto out;
467
468         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val))
469                 goto out;
470         ksnprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val);
471
472         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY,
473             sc->bif.type, sizeof(sc->bif.type)))
474                 goto out;
475
476         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA,
477             sc->bif.oeminfo, sizeof(sc->bif.oeminfo)))
478                 goto out;
479
480         /* XXX check if device was replugged during read? */
481
482         acpi_smbat_info_updated(&sc->bif_lastupdated);
483         error = 0;
484
485 out:
486         if (error == 0)
487                 memcpy(bif, &sc->bif, sizeof(sc->bif));
488         ACPI_SERIAL_END(smbat);
489         return (error);
490 }