2 * Copyright (c) 2005 Hans Petter Selasky
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
26 * $FreeBSD: src/sys/dev/acpica/acpi_smbat.c,v 1.11 2011/11/07 15:43:11 ed Exp $
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
38 #include <acpi_smbus.h>
40 /* Transactions have failed after 500 ms. */
41 #define SMBUS_TIMEOUT 50
43 struct acpi_smbat_softc
{
49 struct timespec bif_lastupdated
;
50 struct timespec bst_lastupdated
;
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
);
61 ACPI_SERIAL_DECL(smbat
, "ACPI Smart Battery");
63 static SYSCTL_NODE(_debug_acpi
, OID_AUTO
, batt
, CTLFLAG_RD
, NULL
,
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.
73 * If you experience that problem, try a value of 10ms and move up
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.");
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
),
86 /* ACPI battery interface */
87 DEVMETHOD(acpi_batt_get_status
, acpi_smbat_get_bst
),
88 DEVMETHOD(acpi_batt_get_info
, acpi_smbat_get_bif
),
93 static driver_t acpi_smbat_driver
= {
96 sizeof(struct acpi_smbat_softc
),
97 .gpri
= KOBJ_GPRI_ACPI
100 static devclass_t acpi_smbat_devclass
;
101 DRIVER_MODULE(acpi_smbat
, acpi
, acpi_smbat_driver
, acpi_smbat_devclass
,
103 MODULE_DEPEND(acpi_smbat
, acpi
, 1, 1, 1);
106 acpi_smbat_probe(device_t dev
)
108 static char *smbat_ids
[] = {"ACPI0001", "ACPI0005", NULL
};
111 if (acpi_disabled("smbat") ||
112 ACPI_ID_PROBE(device_get_parent(dev
), dev
, smbat_ids
) == NULL
)
114 status
= AcpiEvaluateObject(acpi_get_handle(dev
), "_EC", NULL
, NULL
);
115 if (ACPI_FAILURE(status
))
118 device_set_desc(dev
, "ACPI Smart Battery");
123 acpi_smbat_attach(device_t dev
)
125 struct acpi_smbat_softc
*sc
;
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");
134 sc
->sb_base_addr
= (base
>> 8) & 0xff;
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");
143 timespecclear(&sc
->bif_lastupdated
);
144 timespecclear(&sc
->bst_lastupdated
);
146 if (acpi_battery_register(dev
) != 0) {
147 device_printf(dev
, "cannot register battery\n");
154 acpi_smbat_shutdown(device_t dev
)
157 acpi_battery_remove(dev
);
162 acpi_smbat_info_expired(struct timespec
*lastupdated
)
164 struct timespec curtime
;
166 ACPI_SERIAL_ASSERT(smbat
);
168 if (lastupdated
== NULL
)
170 if (!timespecisset(lastupdated
))
173 getnanotime(&curtime
);
174 timespecsub(&curtime
, lastupdated
);
175 return (curtime
.tv_sec
< 0 ||
176 curtime
.tv_sec
> acpi_battery_get_info_expire());
180 acpi_smbat_info_updated(struct timespec
*lastupdated
)
183 ACPI_SERIAL_ASSERT(smbat
);
185 if (lastupdated
!= NULL
)
186 getnanotime(lastupdated
);
190 acpi_smbus_read_2(struct acpi_smbat_softc
*sc
, uint8_t addr
, uint8_t cmd
,
196 ACPI_SERIAL_ASSERT(smbat
);
199 AcpiOsSleep(batt_sleep_ms
);
202 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_ADDR
,
208 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_CMD
,
213 val
= 0x09; /* | 0x80 if PEC */
214 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_PRTCL
,
220 AcpiOsSleep(batt_sleep_ms
);
222 for (to
= SMBUS_TIMEOUT
; to
!= 0; to
--) {
223 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_PRTCL
,
236 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_STS
, &val
, 1);
239 if (val
& SMBUS_STS_MASK
) {
240 kprintf("%s: AE_ERROR 0x%x\n",
241 __func__
, (int)(val
& SMBUS_STS_MASK
));
246 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_DATA
,
258 acpi_smbus_read_multi_1(struct acpi_smbat_softc
*sc
, uint8_t addr
, uint8_t cmd
,
259 uint8_t *ptr
, uint16_t len
)
265 ACPI_SERIAL_ASSERT(smbat
);
268 AcpiOsSleep(batt_sleep_ms
);
271 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_ADDR
,
277 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_CMD
,
282 val
= 0x0B /* | 0x80 if PEC */ ;
283 error
= ACPI_EC_WRITE(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_PRTCL
,
289 AcpiOsSleep(batt_sleep_ms
);
291 for (to
= SMBUS_TIMEOUT
; to
!= 0; to
--) {
292 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_PRTCL
,
305 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_STS
, &val
, 1);
308 if (val
& SMBUS_STS_MASK
) {
309 kprintf("%s: AE_ERROR 0x%x\n",
310 __func__
, (int)(val
& SMBUS_STS_MASK
));
316 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_BCNT
,
320 val
= (val
& 0x1f) + 1;
327 AcpiOsSleep(batt_sleep_ms
);
330 error
= ACPI_EC_READ(sc
->ec_dev
, sc
->sb_base_addr
+ SMBUS_DATA
337 AcpiOsSleep(batt_sleep_ms
);
345 acpi_smbat_get_bst(device_t dev
, struct acpi_bst
*bst
)
347 struct acpi_smbat_softc
*sc
;
353 ACPI_SERIAL_BEGIN(smbat
);
355 addr
= SMBATT_ADDRESS
;
357 sc
= device_get_softc(dev
);
359 if (!acpi_smbat_info_expired(&sc
->bst_lastupdated
)) {
364 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_BATTERY_MODE
, &val
))
366 if (val
& SMBATT_BM_CAPACITY_MODE
)
371 /* get battery status */
372 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_BATTERY_STATUS
, &val
))
376 if (val
& SMBATT_BS_DISCHARGING
)
377 sc
->bst
.state
|= ACPI_BATT_STAT_DISCHARG
;
379 if (val
& SMBATT_BS_REMAINING_CAPACITY_ALARM
)
380 sc
->bst
.state
|= ACPI_BATT_STAT_CRITICAL
;
383 * If the rate is negative, it is discharging. Otherwise,
386 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_CURRENT
, &val
))
390 sc
->bst
.rate
= val
* factor
;
391 sc
->bst
.state
&= ~SMBATT_BS_DISCHARGING
;
392 sc
->bst
.state
|= ACPI_BATT_STAT_CHARGING
;
394 sc
->bst
.rate
= (-val
) * factor
;
398 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_REMAINING_CAPACITY
, &val
))
400 sc
->bst
.cap
= val
* factor
;
402 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_VOLTAGE
, &val
))
406 acpi_smbat_info_updated(&sc
->bst_lastupdated
);
411 memcpy(bst
, &sc
->bst
, sizeof(sc
->bst
));
412 ACPI_SERIAL_END(smbat
);
417 acpi_smbat_get_bif(device_t dev
, struct acpi_bif
*bif
)
419 struct acpi_smbat_softc
*sc
;
425 ACPI_SERIAL_BEGIN(smbat
);
427 addr
= SMBATT_ADDRESS
;
429 sc
= device_get_softc(dev
);
431 if (!acpi_smbat_info_expired(&sc
->bif_lastupdated
)) {
436 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_BATTERY_MODE
, &val
))
438 if (val
& SMBATT_BM_CAPACITY_MODE
) {
440 sc
->bif
.units
= ACPI_BIF_UNITS_MW
;
443 sc
->bif
.units
= ACPI_BIF_UNITS_MA
;
446 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_DESIGN_CAPACITY
, &val
))
448 sc
->bif
.dcap
= val
* factor
;
450 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_FULL_CHARGE_CAPACITY
, &val
))
452 sc
->bif
.lfcap
= val
* factor
;
453 sc
->bif
.btech
= 1; /* secondary (rechargeable) */
455 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_DESIGN_VOLTAGE
, &val
))
459 sc
->bif
.wcap
= sc
->bif
.dcap
/ 10;
460 sc
->bif
.lcap
= sc
->bif
.dcap
/ 10;
462 sc
->bif
.gra1
= factor
; /* not supported */
463 sc
->bif
.gra2
= factor
; /* not supported */
465 if (acpi_smbus_read_multi_1(sc
, addr
, SMBATT_CMD_DEVICE_NAME
,
466 sc
->bif
.model
, sizeof(sc
->bif
.model
)))
469 if (acpi_smbus_read_2(sc
, addr
, SMBATT_CMD_SERIAL_NUMBER
, &val
))
471 ksnprintf(sc
->bif
.serial
, sizeof(sc
->bif
.serial
), "0x%04x", val
);
473 if (acpi_smbus_read_multi_1(sc
, addr
, SMBATT_CMD_DEVICE_CHEMISTRY
,
474 sc
->bif
.type
, sizeof(sc
->bif
.type
)))
477 if (acpi_smbus_read_multi_1(sc
, addr
, SMBATT_CMD_MANUFACTURER_DATA
,
478 sc
->bif
.oeminfo
, sizeof(sc
->bif
.oeminfo
)))
481 /* XXX check if device was replugged during read? */
483 acpi_smbat_info_updated(&sc
->bif_lastupdated
);
488 memcpy(bif
, &sc
->bif
, sizeof(sc
->bif
));
489 ACPI_SERIAL_END(smbat
);