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