37672d205db4704be8cd7b15e32823e6813ce744
[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 static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL,
64     "Battery debugging");
65
66 /* On some laptops with smart batteries, enabling battery monitoring
67  * software causes keystrokes from atkbd to be lost.  This has also been
68  * reported on Linux, and is apparently due to the keyboard and I2C line
69  * for the battery being routed through the same chip.  Whether that's
70  * accurate or not, adding extra sleeps to the status checking code
71  * causes the problem to go away.
72  *
73  * If you experience that problem, try a value of 10ms and move up
74  * from there.
75  */
76 static int      batt_sleep_ms;
77 SYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0,
78     "Sleep during battery status updates to prevent keystroke loss.");
79
80 static device_method_t acpi_smbat_methods[] = {
81         /* device interface */
82         DEVMETHOD(device_probe, acpi_smbat_probe),
83         DEVMETHOD(device_attach, acpi_smbat_attach),
84         DEVMETHOD(device_shutdown, acpi_smbat_shutdown),
85
86         /* ACPI battery interface */
87         DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst),
88         DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif),
89
90         DEVMETHOD_END
91 };
92
93 static driver_t acpi_smbat_driver = {
94         "battery",
95         acpi_smbat_methods,
96         sizeof(struct acpi_smbat_softc),
97         .gpri = KOBJ_GPRI_ACPI
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         ACPI_SERIAL_INIT(smbat);
130         if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) {
131                 device_printf(dev, "cannot get EC base address\n");
132                 return (ENXIO);
133         }
134         sc->sb_base_addr = (base >> 8) & 0xff;
135
136         /* XXX Only works with one EC, but nearly all systems only have one. */
137         sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0);
138         if (sc->ec_dev == NULL) {
139                 device_printf(dev, "cannot find EC device\n");
140                 return (ENXIO);
141         }
142
143         timespecclear(&sc->bif_lastupdated);
144         timespecclear(&sc->bst_lastupdated);
145
146         if (acpi_battery_register(dev) != 0) {
147                 device_printf(dev, "cannot register battery\n");
148                 return (ENXIO);
149         }
150         return (0);
151 }
152
153 static int
154 acpi_smbat_shutdown(device_t dev)
155 {
156
157         acpi_battery_remove(dev);
158         return (0);
159 }
160
161 static int
162 acpi_smbat_info_expired(struct timespec *lastupdated)
163 {
164         struct timespec curtime;
165
166         ACPI_SERIAL_ASSERT(smbat);
167
168         if (lastupdated == NULL)
169                 return (TRUE);
170         if (!timespecisset(lastupdated))
171                 return (TRUE);
172
173         getnanotime(&curtime);
174         timespecsub(&curtime, lastupdated);
175         return (curtime.tv_sec < 0 ||
176             curtime.tv_sec > acpi_battery_get_info_expire());
177 }
178
179 static void
180 acpi_smbat_info_updated(struct timespec *lastupdated)
181 {
182
183         ACPI_SERIAL_ASSERT(smbat);
184
185         if (lastupdated != NULL)
186                 getnanotime(lastupdated);
187 }
188
189 static int
190 acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
191     uint16_t *ptr)
192 {
193         int error, to;
194         UINT64 val;
195
196         ACPI_SERIAL_ASSERT(smbat);
197
198         if (batt_sleep_ms)
199             AcpiOsSleep(batt_sleep_ms);
200
201         val = addr;
202         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
203             val, 1);
204         if (error)
205                 goto out;
206
207         val = cmd;
208         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
209             val, 1);
210         if (error)
211                 goto out;
212
213         val = 0x09; /* | 0x80 if PEC */
214         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
215             val, 1);
216         if (error)
217                 goto out;
218
219         if (batt_sleep_ms)
220             AcpiOsSleep(batt_sleep_ms);
221
222         for (to = SMBUS_TIMEOUT; to != 0; to--) {
223                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
224                     &val, 1);
225                 if (error)
226                         goto out;
227                 if (val == 0)
228                         break;
229                 AcpiOsSleep(10);
230         }
231         if (to == 0) {
232                 error = ETIMEDOUT;
233                 goto out;
234         }
235
236         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
237         if (error)
238                 goto out;
239         if (val & SMBUS_STS_MASK) {
240                 kprintf("%s: AE_ERROR 0x%x\n",
241                        __func__, (int)(val & SMBUS_STS_MASK));
242                 error = EIO;
243                 goto out;
244         }
245
246         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA,
247             &val, 2);
248         if (error)
249                 goto out;
250
251         *ptr = val;
252
253 out:
254         return (error);
255 }
256
257 static int
258 acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
259     uint8_t *ptr, uint16_t len)
260 {
261         UINT64 val;
262         uint8_t to;
263         int error;
264
265         ACPI_SERIAL_ASSERT(smbat);
266
267         if (batt_sleep_ms)
268             AcpiOsSleep(batt_sleep_ms);
269
270         val = addr;
271         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
272             val, 1);
273         if (error)
274                 goto out;
275
276         val = cmd;
277         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
278             val, 1);
279         if (error)
280                 goto out;
281
282         val = 0x0B /* | 0x80 if PEC */ ;
283         error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
284             val, 1);
285         if (error)
286                 goto out;
287
288         if (batt_sleep_ms)
289             AcpiOsSleep(batt_sleep_ms);
290
291         for (to = SMBUS_TIMEOUT; to != 0; to--) {
292                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
293                     &val, 1);
294                 if (error)
295                         goto out;
296                 if (val == 0)
297                         break;
298                 AcpiOsSleep(10);
299         }
300         if (to == 0) {
301                 error = ETIMEDOUT;
302                 goto out;
303         }
304
305         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
306         if (error)
307                 goto out;
308         if (val & SMBUS_STS_MASK) {
309                 kprintf("%s: AE_ERROR 0x%x\n",
310                        __func__, (int)(val & SMBUS_STS_MASK));
311                 error = EIO;
312                 goto out;
313         }
314
315         /* get length */
316         error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT,
317             &val, 1);
318         if (error)
319                 goto out;
320         val = (val & 0x1f) + 1;
321
322         bzero(ptr, len);
323         if (len > val)
324                 len = val;
325
326         if (batt_sleep_ms)
327             AcpiOsSleep(batt_sleep_ms);
328
329         while (len--) {
330                 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA
331                     + len, &val, 1);
332                 if (error)
333                         goto out;
334
335                 ptr[len] = val;
336                 if (batt_sleep_ms)
337                     AcpiOsSleep(batt_sleep_ms);
338         }
339
340 out:
341         return (error);
342 }
343
344 static int
345 acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst)
346 {
347         struct acpi_smbat_softc *sc;
348         int error;
349         uint32_t factor;
350         int16_t val;
351         uint8_t addr;
352
353         ACPI_SERIAL_BEGIN(smbat);
354
355         addr = SMBATT_ADDRESS;
356         error = ENXIO;
357         sc = device_get_softc(dev);
358
359         if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) {
360                 error = 0;
361                 goto out;
362         }
363
364         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
365                 goto out;
366         if (val & SMBATT_BM_CAPACITY_MODE)
367                 factor = 10;
368         else
369                 factor = 1;
370
371         /* get battery status */
372         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val))
373                 goto out;
374
375         sc->bst.state = 0;
376         if (val & SMBATT_BS_DISCHARGING)
377                 sc->bst.state |= ACPI_BATT_STAT_DISCHARG;
378
379         if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM)
380                 sc->bst.state |= ACPI_BATT_STAT_CRITICAL;
381
382         /*
383          * If the rate is negative, it is discharging.  Otherwise,
384          * it is charging.
385          */
386         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val))
387                 goto out;
388
389         if (val > 0) {
390                 sc->bst.rate = val * factor;
391                 sc->bst.state &= ~SMBATT_BS_DISCHARGING;
392                 sc->bst.state |= ACPI_BATT_STAT_CHARGING;
393         } else if (val < 0)
394                 sc->bst.rate = (-val) * factor;
395         else
396                 sc->bst.rate = 0;
397
398         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val))
399                 goto out;
400         sc->bst.cap = val * factor;
401
402         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val))
403                 goto out;
404         sc->bst.volt = val;
405
406         acpi_smbat_info_updated(&sc->bst_lastupdated);
407         error = 0;
408
409 out:
410         if (error == 0)
411                 memcpy(bst, &sc->bst, sizeof(sc->bst));
412         ACPI_SERIAL_END(smbat);
413         return (error);
414 }
415
416 static int
417 acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
418 {
419         struct acpi_smbat_softc *sc;
420         int error;
421         uint32_t factor;
422         uint16_t val;
423         uint8_t addr;
424
425         ACPI_SERIAL_BEGIN(smbat);
426
427         addr = SMBATT_ADDRESS;
428         error = ENXIO;
429         sc = device_get_softc(dev);
430
431         if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) {
432                 error = 0;
433                 goto out;
434         }
435
436         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
437                 goto out;
438         if (val & SMBATT_BM_CAPACITY_MODE) {
439                 factor = 10;
440                 sc->bif.units = ACPI_BIF_UNITS_MW;
441         } else {
442                 factor = 1;
443                 sc->bif.units = ACPI_BIF_UNITS_MA;
444         }
445
446         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val))
447                 goto out;
448         sc->bif.dcap = val * factor;
449
450         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val))
451                 goto out;
452         sc->bif.lfcap = val * factor;
453         sc->bif.btech = 1;              /* secondary (rechargeable) */
454
455         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val))
456                 goto out;
457         sc->bif.dvol = val;
458
459         sc->bif.wcap = sc->bif.dcap / 10;
460         sc->bif.lcap = sc->bif.dcap / 10;
461
462         sc->bif.gra1 = factor;  /* not supported */
463         sc->bif.gra2 = factor;  /* not supported */
464
465         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME,
466             sc->bif.model, sizeof(sc->bif.model)))
467                 goto out;
468
469         if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val))
470                 goto out;
471         ksnprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val);
472
473         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY,
474             sc->bif.type, sizeof(sc->bif.type)))
475                 goto out;
476
477         if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA,
478             sc->bif.oeminfo, sizeof(sc->bif.oeminfo)))
479                 goto out;
480
481         /* XXX check if device was replugged during read? */
482
483         acpi_smbat_info_updated(&sc->bif_lastupdated);
484         error = 0;
485
486 out:
487         if (error == 0)
488                 memcpy(bif, &sc->bif, sizeof(sc->bif));
489         ACPI_SERIAL_END(smbat);
490         return (error);
491 }