summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi_cmbat.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpica/acpi_cmbat.c')
-rw-r--r--sys/dev/acpica/acpi_cmbat.c509
1 files changed, 149 insertions, 360 deletions
diff --git a/sys/dev/acpica/acpi_cmbat.c b/sys/dev/acpica/acpi_cmbat.c
index 54e4982..e60ea17 100644
--- a/sys/dev/acpica/acpi_cmbat.c
+++ b/sys/dev/acpica/acpi_cmbat.c
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2005 Nate Lawson
* Copyright (c) 2000 Munehiro Matsuda
* Copyright (c) 2000 Takanori Watanabe
* Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
@@ -24,10 +25,11 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
@@ -60,41 +62,29 @@ ACPI_MODULE_NAME("BATTERY")
struct acpi_cmbat_softc {
device_t dev;
+ int flags;
struct acpi_bif bif;
struct acpi_bst bst;
struct timespec bif_lastupdated;
struct timespec bst_lastupdated;
-
- int flags;
- int present;
- int cap;
- int min;
- int full_charge_time;
- int initializing;
- int phys_unit;
};
-static struct timespec acpi_cmbat_info_lastupdated;
ACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
-/* XXX: devclass_get_maxunit() don't give us the current allocated units. */
-static int acpi_cmbat_units = 0;
-
-static int acpi_cmbat_info_expired(struct timespec *);
-static void acpi_cmbat_info_updated(struct timespec *);
-static void acpi_cmbat_get_bst(void *);
-static void acpi_cmbat_get_bif(void *);
-static void acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *);
-static int acpi_cmbat_probe(device_t);
-static int acpi_cmbat_attach(device_t);
-static int acpi_cmbat_detach(device_t);
-static int acpi_cmbat_resume(device_t);
-static int acpi_cmbat_ioctl(u_long, caddr_t, void *);
-static int acpi_cmbat_is_bst_valid(struct acpi_bst*);
-static int acpi_cmbat_is_bif_valid(struct acpi_bif*);
-static int acpi_cmbat_get_total_battinfo(struct acpi_battinfo *);
-static void acpi_cmbat_init_battery(void *);
+static int acpi_cmbat_probe(device_t dev);
+static int acpi_cmbat_attach(device_t dev);
+static int acpi_cmbat_detach(device_t dev);
+static int acpi_cmbat_resume(device_t dev);
+static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
+ void *context);
+static int acpi_cmbat_info_expired(struct timespec *lastupdated);
+static void acpi_cmbat_info_updated(struct timespec *lastupdated);
+static void acpi_cmbat_get_bst(device_t dev);
+static void acpi_cmbat_get_bif(device_t dev);
+static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp);
+static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp);
+static void acpi_cmbat_init_battery(void *arg);
static device_method_t acpi_cmbat_methods[] = {
/* Device interface */
@@ -103,11 +93,15 @@ static device_method_t acpi_cmbat_methods[] = {
DEVMETHOD(device_detach, acpi_cmbat_detach),
DEVMETHOD(device_resume, acpi_cmbat_resume),
+ /* ACPI battery interface */
+ DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif),
+ DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
+
{0, 0}
};
static driver_t acpi_cmbat_driver = {
- "acpi_cmbat",
+ "battery",
acpi_cmbat_methods,
sizeof(struct acpi_cmbat_softc),
};
@@ -117,6 +111,96 @@ DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
static int
+acpi_cmbat_probe(device_t dev)
+{
+ static char *cmbat_ids[] = { "PNP0C0A", NULL };
+
+ if (acpi_disabled("cmbat") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI Control Method Battery");
+ return (0);
+}
+
+static int
+acpi_cmbat_attach(device_t dev)
+{
+ int error;
+ ACPI_HANDLE handle;
+ struct acpi_cmbat_softc *sc;
+
+ sc = device_get_softc(dev);
+ handle = acpi_get_handle(dev);
+ sc->dev = dev;
+
+ timespecclear(&sc->bif_lastupdated);
+ timespecclear(&sc->bst_lastupdated);
+
+ error = acpi_battery_register(dev);
+ if (error != 0) {
+ device_printf(dev, "registering battery failed\n");
+ return (error);
+ }
+
+ /*
+ * Install a system notify handler in addition to the device notify.
+ * Toshiba notebook uses this alternate notify for its battery.
+ */
+ AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
+ acpi_cmbat_notify_handler, dev);
+
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
+
+ return (0);
+}
+
+static int
+acpi_cmbat_detach(device_t dev)
+{
+
+ acpi_battery_remove(dev);
+ return (0);
+}
+
+static int
+acpi_cmbat_resume(device_t dev)
+{
+
+ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
+ return (0);
+}
+
+static void
+acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_cmbat_softc *sc;
+ device_t dev;
+
+ dev = (device_t)context;
+ sc = device_get_softc(dev);
+
+ /*
+ * Clear the appropriate last updated time. The next call to retrieve
+ * the battery status will get the new value for us. We don't need to
+ * acquire a lock since we are only clearing the time stamp and since
+ * calling _BST/_BIF can trigger a notify, we could deadlock also.
+ */
+ switch (notify) {
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ case ACPI_BATTERY_BST_CHANGE:
+ timespecclear(&sc->bst_lastupdated);
+ break;
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_BATTERY_BIF_CHANGE:
+ timespecclear(&sc->bif_lastupdated);
+ break;
+ }
+
+ acpi_UserNotify("CMBAT", h, notify);
+}
+
+static int
acpi_cmbat_info_expired(struct timespec *lastupdated)
{
struct timespec curtime;
@@ -145,9 +229,8 @@ acpi_cmbat_info_updated(struct timespec *lastupdated)
}
static void
-acpi_cmbat_get_bst(void *context)
+acpi_cmbat_get_bst(device_t dev)
{
- device_t dev;
struct acpi_cmbat_softc *sc;
ACPI_STATUS as;
ACPI_OBJECT *res;
@@ -156,7 +239,6 @@ acpi_cmbat_get_bst(void *context)
ACPI_SERIAL_ASSERT(cmbat);
- dev = context;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
bst_buffer.Pointer = NULL;
@@ -205,9 +287,8 @@ end:
}
static void
-acpi_cmbat_get_bif(void *context)
+acpi_cmbat_get_bif(device_t dev)
{
- device_t dev;
struct acpi_cmbat_softc *sc;
ACPI_STATUS as;
ACPI_OBJECT *res;
@@ -216,7 +297,6 @@ acpi_cmbat_get_bif(void *context)
ACPI_SERIAL_ASSERT(cmbat);
- dev = context;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
bif_buffer.Pointer = NULL;
@@ -273,319 +353,59 @@ end:
AcpiOsFree(bif_buffer.Pointer);
}
-static void
-acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
-{
- device_t dev;
- struct acpi_cmbat_softc *sc;
-
- dev = (device_t)context;
- sc = device_get_softc(dev);
-
- acpi_UserNotify("CMBAT", h, notify);
-
- /*
- * Clear the appropriate last updated time. The next call to retrieve
- * the battery status will get the new value for us. We don't need to
- * acquire a lock since we are only clearing the time stamp and since
- * calling _BST/_BIF can trigger a notify, we could deadlock also.
- */
- switch (notify) {
- case ACPI_NOTIFY_DEVICE_CHECK:
- case ACPI_BATTERY_BST_CHANGE:
- timespecclear(&sc->bst_lastupdated);
- break;
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_BATTERY_BIF_CHANGE:
- timespecclear(&sc->bif_lastupdated);
- break;
- default:
- break;
- }
-}
-
-static int
-acpi_cmbat_probe(device_t dev)
-{
- static char *cmbat_ids[] = { "PNP0C0A", NULL };
-
- if (acpi_disabled("cmbat") ||
- ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
- return (ENXIO);
-
- device_set_desc(dev, "Control Method Battery");
- return (0);
-}
-
static int
-acpi_cmbat_attach(device_t dev)
+acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp)
{
- int error;
- ACPI_HANDLE handle;
struct acpi_cmbat_softc *sc;
sc = device_get_softc(dev);
- handle = acpi_get_handle(dev);
- sc->dev = dev;
-
- /*
- * Install a system notify handler in addition to the device notify.
- * Toshiba notebook uses this alternate notify for its battery.
- */
- AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
- acpi_cmbat_notify_handler, dev);
-
- ACPI_SERIAL_BEGIN(cmbat);
- timespecclear(&sc->bif_lastupdated);
- timespecclear(&sc->bst_lastupdated);
- if (acpi_cmbat_units == 0) {
- error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF,
- acpi_cmbat_ioctl, NULL);
- if (error != 0) {
- device_printf(dev, "register bif ioctl failed\n");
- return (error);
- }
- error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST,
- acpi_cmbat_ioctl, NULL);
- if (error != 0) {
- device_printf(dev, "register bst ioctl failed\n");
- return (error);
- }
- }
-
- sc->phys_unit = acpi_cmbat_units;
- error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, sc->phys_unit);
- if (error != 0) {
- device_printf(dev, "registering battery %d failed\n", sc->phys_unit);
- return (error);
- }
- acpi_cmbat_units++;
- timespecclear(&acpi_cmbat_info_lastupdated);
- ACPI_SERIAL_END(cmbat);
-
- AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
-
- return (0);
-}
-
-static int
-acpi_cmbat_detach(device_t dev)
-{
- struct acpi_cmbat_softc *sc;
-
- sc = device_get_softc(dev);
ACPI_SERIAL_BEGIN(cmbat);
- acpi_battery_remove(ACPI_BATT_TYPE_CMBAT, sc->phys_unit);
- acpi_cmbat_units--;
+ acpi_cmbat_get_bif(dev);
+ bifp->units = sc->bif.units;
+ bifp->dcap = sc->bif.dcap;
+ bifp->lfcap = sc->bif.lfcap;
+ bifp->btech = sc->bif.btech;
+ bifp->dvol = sc->bif.dvol;
+ bifp->wcap = sc->bif.wcap;
+ bifp->lcap = sc->bif.lcap;
+ bifp->gra1 = sc->bif.gra1;
+ bifp->gra2 = sc->bif.gra2;
+ strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
+ strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
+ strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
+ strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
ACPI_SERIAL_END(cmbat);
- return (0);
-}
-static int
-acpi_cmbat_resume(device_t dev)
-{
- AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
return (0);
}
static int
-acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg)
+acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
{
- device_t dev;
- union acpi_battery_ioctl_arg *ioctl_arg;
struct acpi_cmbat_softc *sc;
- struct acpi_bif *bifp;
- struct acpi_bst *bstp;
- ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
- dev = devclass_get_device(acpi_cmbat_devclass, ioctl_arg->unit);
- if (dev == NULL)
- return (ENXIO);
sc = device_get_softc(dev);
- /*
- * No security check required: information retrieval only. If
- * new functions are added here, a check might be required.
- */
ACPI_SERIAL_BEGIN(cmbat);
- switch (cmd) {
- case ACPIIO_CMBAT_GET_BIF:
- acpi_cmbat_get_bif(dev);
- bifp = &ioctl_arg->bif;
- bifp->units = sc->bif.units;
- bifp->dcap = sc->bif.dcap;
- bifp->lfcap = sc->bif.lfcap;
- bifp->btech = sc->bif.btech;
- bifp->dvol = sc->bif.dvol;
- bifp->wcap = sc->bif.wcap;
- bifp->lcap = sc->bif.lcap;
- bifp->gra1 = sc->bif.gra1;
- bifp->gra2 = sc->bif.gra2;
- strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
- strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
- strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
- strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
- break;
- case ACPIIO_CMBAT_GET_BST:
- bstp = &ioctl_arg->bst;
- if (acpi_BatteryIsPresent(dev)) {
- acpi_cmbat_get_bst(dev);
- bstp->state = sc->bst.state;
- bstp->rate = sc->bst.rate;
- bstp->cap = sc->bst.cap;
- bstp->volt = sc->bst.volt;
- } else {
- bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
- }
- break;
- default:
- break;
- }
+ if (acpi_BatteryIsPresent(dev)) {
+ acpi_cmbat_get_bst(dev);
+ bstp->state = sc->bst.state;
+ bstp->rate = sc->bst.rate;
+ bstp->cap = sc->bst.cap;
+ bstp->volt = sc->bst.volt;
+ } else
+ bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
ACPI_SERIAL_END(cmbat);
return (0);
}
-static int
-acpi_cmbat_is_bst_valid(struct acpi_bst *bst)
-{
- if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff ||
- bst->volt == 0xffffffff)
- return (FALSE);
- else
- return (TRUE);
-}
-
-static int
-acpi_cmbat_is_bif_valid(struct acpi_bif *bif)
-{
- if (bif->lfcap == 0)
- return (FALSE);
- else
- return (TRUE);
-}
-
-static int
-acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo)
-{
- int i;
- int error;
- int batt_stat;
- int valid_rate, valid_units;
- int cap, min;
- int total_cap, total_min, total_full;
- struct acpi_cmbat_softc *sc;
-
- ACPI_SERIAL_ASSERT(cmbat);
-
- cap = min = -1;
- batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
- error = 0;
-
- /* Get battery status, valid rate and valid units */
- batt_stat = valid_rate = valid_units = 0;
- for (i = 0; i < acpi_cmbat_units; i++) {
- sc = devclass_get_softc(acpi_cmbat_devclass, i);
- if (sc == NULL)
- continue;
- sc->present = acpi_BatteryIsPresent(sc->dev);
- if (!sc->present)
- continue;
- acpi_cmbat_get_bst(sc->dev);
-
- /* If battery not installed, we get strange values */
- if (!acpi_cmbat_is_bst_valid(&sc->bst) ||
- !acpi_cmbat_is_bif_valid(&sc->bif)) {
- sc->present = FALSE;
- continue;
- }
-
- valid_units++;
- sc->cap = 100 * sc->bst.cap / sc->bif.lfcap;
-
- /*
- * Some laptops report the "design-capacity" instead of the
- * "real-capacity" when the battery is fully charged.
- * That breaks the above arithmetic as it needs to be 100% maximum.
- */
- if (sc->cap > 100)
- sc->cap = 100;
-
- batt_stat |= sc->bst.state;
-
- /*
- * XXX Hack to calculate total battery time.
- *
- * On systems with more than one battery, they may get used
- * sequentially, thus bst.rate may only signify the one in use.
- * For the remaining batteries, bst.rate will be zero, which
- * makes it impossible to calculate the remaining time. Some
- * other systems may need the sum of all the bst.rate values
- * when discharging. Therefore, we sum the bst.rate for valid
- * batteries (ones in the discharging state) and use the sum
- * to calculate the total remaining time.
- */
- if (sc->bst.rate > 0) {
- if (sc->bst.state & ACPI_BATT_STAT_DISCHARG)
- valid_rate += sc->bst.rate;
- }
- }
-
- /* Calculate total battery capacity and time */
- total_cap = total_min = total_full = 0;
- for (i = 0; i < acpi_cmbat_units; i++) {
- sc = devclass_get_softc(acpi_cmbat_devclass, i);
- if (!sc->present)
- continue;
-
- /*
- * If any batteries are discharging, use the sum of the bst.rate
- * values. Otherwise, use the full charge time to estimate
- * remaining time. If neither are available, assume no charge.
- */
- if (valid_rate > 0)
- sc->min = 60 * sc->bst.cap / valid_rate;
- else if (sc->full_charge_time > 0)
- sc->min = (sc->full_charge_time * sc->cap) / 100;
- else
- sc->min = 0;
- total_min += sc->min;
- total_cap += sc->cap;
- total_full += sc->full_charge_time;
- }
-
- /* Battery life */
- if (valid_units == 0) {
- cap = -1;
- batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
- } else
- cap = total_cap / valid_units;
-
- /* Battery time */
- if (valid_units == 0)
- min = -1;
- else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) {
- if (total_full == 0)
- min = -1;
- else
- min = (total_full * cap) / 100;
- } else
- min = total_min;
- acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated);
-
- battinfo->cap = cap;
- battinfo->min = min;
- battinfo->state = batt_stat;
-
- return (error);
-}
-
static void
acpi_cmbat_init_battery(void *arg)
{
struct acpi_cmbat_softc *sc;
- int retry;
+ int retry, valid;
device_t dev;
dev = (device_t)arg;
@@ -593,9 +413,13 @@ acpi_cmbat_init_battery(void *arg)
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery initialization start\n");
+ /*
+ * Try repeatedly to get valid data from the battery. Since the
+ * embedded controller isn't always ready just after boot, we may have
+ * to wait a while.
+ */
for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
- sc->present = acpi_BatteryIsPresent(dev);
- if (!sc->present)
+ if (!acpi_BatteryIsPresent(dev))
continue;
ACPI_SERIAL_BEGIN(cmbat);
@@ -603,10 +427,11 @@ acpi_cmbat_init_battery(void *arg)
timespecclear(&sc->bif_lastupdated);
acpi_cmbat_get_bst(dev);
acpi_cmbat_get_bif(dev);
+ valid = acpi_battery_bst_valid(&sc->bst) &&
+ acpi_battery_bif_valid(&sc->bif);
ACPI_SERIAL_END(cmbat);
- if (acpi_cmbat_is_bst_valid(&sc->bst) &&
- acpi_cmbat_is_bif_valid(&sc->bif))
+ if (valid)
break;
}
@@ -618,39 +443,3 @@ acpi_cmbat_init_battery(void *arg)
"battery initialization done, tried %d times\n", retry + 1);
}
}
-
-/*
- * Public interfaces.
- */
-int
-acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo)
-{
- int error;
- struct acpi_cmbat_softc *sc;
-
- ACPI_SERIAL_BEGIN(cmbat);
- error = acpi_cmbat_get_total_battinfo(battinfo);
- if (unit == -1 || error)
- goto out;
-
- error = ENXIO;
- if (unit >= acpi_cmbat_units)
- goto out;
- if ((sc = devclass_get_softc(acpi_cmbat_devclass, unit)) == NULL)
- goto out;
-
- if (!sc->present) {
- battinfo->cap = -1;
- battinfo->min = -1;
- battinfo->state = ACPI_BATT_STAT_NOT_PRESENT;
- } else {
- battinfo->cap = sc->cap;
- battinfo->min = sc->min;
- battinfo->state = sc->bst.state;
- }
- error = 0;
-
-out:
- ACPI_SERIAL_END(cmbat);
- return (error);
-}
OpenPOWER on IntegriCloud