Date: Mon, 8 Dec 2008 02:37:08 +0000 (UTC) From: Nathan Whitehorn <nwhitehorn@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r185754 - in head: share/man/man4/man4.powerpc sys/powerpc/powermac Message-ID: <200812080237.mB82b9Hs042873@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: nwhitehorn Date: Mon Dec 8 02:37:08 2008 New Revision: 185754 URL: http://svn.freebsd.org/changeset/base/185754 Log: Add facilities to pmu(4) to interrogate battery status on Apple PowerPC laptops. This includes battery presence detection, charging status, current and voltage readouts, and charge level indication. The sysctl interface is somewhat ACPI-like. Modified: head/share/man/man4/man4.powerpc/pmu.4 head/sys/powerpc/powermac/pmu.c head/sys/powerpc/powermac/pmuvar.h Modified: head/share/man/man4/man4.powerpc/pmu.4 ============================================================================== --- head/share/man/man4/man4.powerpc/pmu.4 Mon Dec 8 02:34:13 2008 (r185753) +++ head/share/man/man4/man4.powerpc/pmu.4 Mon Dec 8 02:37:08 2008 (r185754) @@ -69,13 +69,32 @@ The driver provides power management services in addition to an .Xr adb 4 interface. The following sysctls can be used to control the -power management behavior. +power management behavior and to examine current system power and +thermal conditions. .Bl -tag -width indent .It Va dev.pmu.%d.server_mode Restart after power failure behavior (1 causes system to reboot after power -cut, 0 causes system to remain off) +cut, 0 causes system to remain off). +.It Va dev.pmu.%d.batteries.%d.present +Indicates whether the relevant battery is inserted. +.It Va dev.pmu.%d.batteries.%d.charging +Indicates whether the battery is currently charging. +.It Va dev.pmu.%d.batteries.%d.charge +The current battery charge, in milliamp hours. +.It Va dev.pmu.%d.batteries.%d.maxcharge +The battery's self-reported maximum charge, in milliamp hours. +.It Va dev.pmu.%d.batteries.%d.rate +The current into the battery, in milliamps. While the battery is discharging, +this will be negative. +.It Va dev.pmu.%d.batteries.%d.voltage +Battery voltage, in millivolts. +.It Va dev.pmu.%d.batteries.%d.time +Estimated time until full battery charge (or discharge), in minutes. +.It Va dev.pmu.%d.batteries.%d.life +Current fraction of the battery's maximum charge, in percent. .Sh SEE ALSO -.Xr adb 4 +.Xr adb 4 , +.Xr acpi 4 .Sh HISTORY The .Nm Modified: head/sys/powerpc/powermac/pmu.c ============================================================================== --- head/sys/powerpc/powermac/pmu.c Mon Dec 8 02:34:13 2008 (r185753) +++ head/sys/powerpc/powermac/pmu.c Mon Dec 8 02:37:08 2008 (r185754) @@ -67,10 +67,29 @@ static int pmu_attach(device_t); static int pmu_detach(device_t); static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, - u_char *data, u_char poll); + u_char *data, u_char poll); static u_int pmu_adb_autopoll(device_t dev, uint16_t mask); static void pmu_poll(device_t dev); + static int pmu_server_mode(SYSCTL_HANDLER_ARGS); +static int pmu_query_battery(struct pmu_softc *sc, int batt, + struct pmu_battstate *info); +static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS); + +/* + * List of battery-related sysctls we might ask for + */ + +enum { + PMU_BATSYSCTL_PRESENT = 1 << 8, + PMU_BATSYSCTL_CHARGING = 2 << 8, + PMU_BATSYSCTL_CHARGE = 3 << 8, + PMU_BATSYSCTL_MAXCHARGE = 4 << 8, + PMU_BATSYSCTL_CURRENT = 5 << 8, + PMU_BATSYSCTL_VOLTAGE = 6 << 8, + PMU_BATSYSCTL_TIME = 7 << 8, + PMU_BATSYSCTL_LIFE = 8 << 8 +}; static device_method_t pmu_methods[] = { /* Device interface */ @@ -280,6 +299,7 @@ pmu_attach(device_t dev) { struct pmu_softc *sc; + int i; uint8_t reg; uint8_t cmd[2] = {2, 0}; uint8_t resp[16]; @@ -312,8 +332,6 @@ pmu_attach(device_t dev) return (ENXIO); } - sc->sc_error = 0; - sc->sc_polling = 0; sc->sc_autopoll = 0; /* Init PMU */ @@ -343,6 +361,18 @@ pmu_attach(device_t dev) if (strncmp(name, "adb", 4) == 0) { sc->adb_bus = device_add_child(dev,"adb",-1); } + + if (strncmp(name, "power-mgt", 9) == 0) { + uint32_t prim_info[9]; + + if (OF_getprop(child, "prim-info", prim_info, + sizeof(prim_info)) >= 7) + sc->sc_batteries = (prim_info[6] >> 16) & 0xff; + + if (bootverbose && sc->sc_batteries > 0) + device_printf(dev, "%d batteries detected\n", + sc->sc_batteries); + } } /* @@ -353,8 +383,61 @@ pmu_attach(device_t dev) tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0, - pmu_server_mode, "I", "Enable reboot after power failure"); + "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + pmu_server_mode, "I", "Enable reboot after power failure"); + + if (sc->sc_batteries > 0) { + struct sysctl_oid *oid, *battroot; + char battnum[2]; + + battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "batteries", CTLFLAG_RD, 0, "Battery Information"); + + for (i = 0; i < sc->sc_batteries; i++) { + battnum[0] = i + '0'; + battnum[1] = '\0'; + + oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot), + OID_AUTO, battnum, CTLFLAG_RD, 0, + "Battery Information"); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "present", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl, + "I", "Battery present"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "charging", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl, + "I", "Battery charging"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "charge", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl, + "I", "Battery charge (mAh)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl, + "I", "Maximum battery capacity (mAh)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "rate", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl, + "I", "Battery discharge rate (mA)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "voltage", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl, + "I", "Battery voltage (mV)"); + + /* Knobs for mental compatibility with ACPI */ + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "time", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl, + "I", "Time Remaining (minutes)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "life", CTLTYPE_INT | CTLFLAG_RD, sc, + PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl, + "I", "Capacity remaining (percent)"); + } + } return (bus_generic_attach(dev)); } @@ -437,7 +520,6 @@ pmu_send(void *cookie, int cmd, int leng /* wait idle */ do {} while (pmu_intr_state(sc)); - sc->sc_error = 0; /* send command */ pmu_send_byte(sc, cmd); @@ -671,3 +753,124 @@ pmu_server_mode(SYSCTL_HANDLER_ARGS) return (0); } +static int +pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info) +{ + uint8_t reg; + uint8_t resp[16]; + int len; + + reg = batt + 1; + + mtx_lock(&sc->sc_mutex); + len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, ®, 16, resp); + mtx_unlock(&sc->sc_mutex); + + if (len < 3) + return (-1); + + /* All PMU battery info replies share a common header: + * Byte 1 Payload Format + * Byte 2 Battery Flags + */ + + info->state = resp[2]; + + switch (resp[1]) { + case 3: + case 4: + /* + * Formats 3 and 4 appear to be the same: + * Byte 3 Charge + * Byte 4 Max Charge + * Byte 5 Current + * Byte 6 Voltage + */ + + info->charge = resp[3]; + info->maxcharge = resp[4]; + /* Current can be positive or negative */ + info->current = (int8_t)resp[5]; + info->voltage = resp[6]; + break; + case 5: + /* + * Formats 5 is a wider version of formats 3 and 4 + * Byte 3-4 Charge + * Byte 5-6 Max Charge + * Byte 7-8 Current + * Byte 9-10 Voltage + */ + + info->charge = (resp[3] << 8) | resp[4]; + info->maxcharge = (resp[5] << 8) | resp[6]; + /* Current can be positive or negative */ + info->current = (int16_t)((resp[7] << 8) | resp[8]); + info->voltage = (resp[9] << 8) | resp[10]; + break; + default: + device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n", + resp[1]); + return (-1); + } + + return (0); +} + +static int +pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct pmu_softc *sc; + struct pmu_battstate batt; + int error, result; + + sc = arg1; + + error = pmu_query_battery(sc, arg2 & 0x00ff, &batt); + + if (error != 0) + return (error); + + switch (arg2 & 0xff00) { + case PMU_BATSYSCTL_PRESENT: + result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0; + break; + case PMU_BATSYSCTL_CHARGING: + result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0; + break; + case PMU_BATSYSCTL_CHARGE: + result = batt.charge; + break; + case PMU_BATSYSCTL_MAXCHARGE: + result = batt.maxcharge; + break; + case PMU_BATSYSCTL_CURRENT: + result = batt.current; + break; + case PMU_BATSYSCTL_VOLTAGE: + result = batt.voltage; + break; + case PMU_BATSYSCTL_TIME: + /* Time remaining until full charge/discharge, in minutes */ + + if (batt.current >= 0) + result = (batt.maxcharge - batt.charge) /* mAh */ * 60 + / batt.current /* mA */; + else + result = (batt.charge /* mAh */ * 60) + / (-batt.current /* mA */); + break; + case PMU_BATSYSCTL_LIFE: + /* Battery charge fraction, in percent */ + result = (batt.charge * 100) / batt.maxcharge; + break; + default: + /* This should never happen */ + result = -1; + }; + + error = sysctl_handle_int(oidp, &result, 0, req); + + return (error); +} + Modified: head/sys/powerpc/powermac/pmuvar.h ============================================================================== --- head/sys/powerpc/powermac/pmuvar.h Mon Dec 8 02:34:13 2008 (r185753) +++ head/sys/powerpc/powermac/pmuvar.h Mon Dec 8 02:37:08 2008 (r185754) @@ -158,14 +158,18 @@ struct pmu_softc { void *sc_ih; struct mtx sc_mutex; - device_t adb_bus; + volatile int sc_autopoll; + int sc_batteries; +}; + +struct pmu_battstate { + int state; - int sc_node; - volatile int sc_state; - int sc_polling; - int sc_error; - volatile int sc_autopoll; + int charge; + int maxcharge; + int current; + int voltage; }; #endif /* PMUVAR_H */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812080237.mB82b9Hs042873>