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