1*4d01e983Smacallan /* $NetBSD: battery.c,v 1.15 2011/07/26 08:37:45 macallan Exp $ */
2f41db58cSmacallan
3f41db58cSmacallan /*-
4f41db58cSmacallan * Copyright (c) 2007 Michael Lorenz
5f41db58cSmacallan * All rights reserved.
6f41db58cSmacallan *
7f41db58cSmacallan * Redistribution and use in source and binary forms, with or without
8f41db58cSmacallan * modification, are permitted provided that the following conditions
9f41db58cSmacallan * are met:
10f41db58cSmacallan * 1. Redistributions of source code must retain the above copyright
11f41db58cSmacallan * notice, this list of conditions and the following disclaimer.
12f41db58cSmacallan * 2. Redistributions in binary form must reproduce the above copyright
13f41db58cSmacallan * notice, this list of conditions and the following disclaimer in the
14f41db58cSmacallan * documentation and/or other materials provided with the distribution.
15f41db58cSmacallan *
16f41db58cSmacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17f41db58cSmacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18f41db58cSmacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19f41db58cSmacallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20f41db58cSmacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21f41db58cSmacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22f41db58cSmacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23f41db58cSmacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24f41db58cSmacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25f41db58cSmacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26f41db58cSmacallan * POSSIBILITY OF SUCH DAMAGE.
27f41db58cSmacallan */
28f41db58cSmacallan
29f41db58cSmacallan #include <sys/cdefs.h>
30*4d01e983Smacallan __KERNEL_RCSID(0, "$NetBSD: battery.c,v 1.15 2011/07/26 08:37:45 macallan Exp $");
31f41db58cSmacallan
32f41db58cSmacallan #include <sys/param.h>
33f41db58cSmacallan #include <sys/systm.h>
34f41db58cSmacallan #include <sys/kernel.h>
35f41db58cSmacallan #include <sys/device.h>
36f41db58cSmacallan #include <sys/proc.h>
37f41db58cSmacallan
38f41db58cSmacallan #include <dev/sysmon/sysmonvar.h>
39f41db58cSmacallan #include <dev/sysmon/sysmon_taskq.h>
40f41db58cSmacallan
41f41db58cSmacallan #include <macppc/dev/pmuvar.h>
42f41db58cSmacallan #include <macppc/dev/batteryvar.h>
43b6584574Sdyoung #include <sys/bus.h>
44d974db0aSgarbled #include <machine/pio.h>
45f41db58cSmacallan #include "opt_battery.h"
46f41db58cSmacallan
47f41db58cSmacallan #ifdef BATTERY_DEBUG
48f41db58cSmacallan #define DPRINTF printf
49f41db58cSmacallan #else
50f41db58cSmacallan #define DPRINTF while (0) printf
51f41db58cSmacallan #endif
52f41db58cSmacallan
53f41db58cSmacallan #define BTYPE_COMET 1
54f41db58cSmacallan #define BTYPE_HOOPER 2
55f41db58cSmacallan
56f41db58cSmacallan #define BAT_CPU_TEMPERATURE 0
57f41db58cSmacallan #define BAT_AC_PRESENT 1
58f41db58cSmacallan #define BAT_PRESENT 2
59f41db58cSmacallan #define BAT_VOLTAGE 3
60f41db58cSmacallan #define BAT_CURRENT 4
61f41db58cSmacallan #define BAT_MAX_CHARGE 5
62f41db58cSmacallan #define BAT_CHARGE 6
63f41db58cSmacallan #define BAT_CHARGING 7
648957a299Sxtraeme #define BAT_FULL 8
658957a299Sxtraeme #define BAT_TEMPERATURE 9
668957a299Sxtraeme #define BAT_NSENSORS 10 /* number of sensors */
67f41db58cSmacallan
68f41db58cSmacallan struct battery_softc {
6905b09539Smatt device_t sc_dev;
70f41db58cSmacallan struct pmu_ops *sc_pmu_ops;
71f41db58cSmacallan int sc_type;
72f41db58cSmacallan
73f41db58cSmacallan /* envsys stuff */
7431962fc6Sxtraeme struct sysmon_envsys *sc_sme;
758957a299Sxtraeme envsys_data_t sc_sensor[BAT_NSENSORS];
760c12bd26Smacallan struct sysmon_pswitch sc_sm_acpower;
77f41db58cSmacallan
78f41db58cSmacallan /* battery status */
79f41db58cSmacallan int sc_flags;
800c12bd26Smacallan int sc_oflags;
81f41db58cSmacallan int sc_voltage;
82f41db58cSmacallan int sc_charge;
83f41db58cSmacallan int sc_current;
84f41db58cSmacallan int sc_time;
85f41db58cSmacallan int sc_cpu_temp;
86f41db58cSmacallan int sc_bat_temp;
87f41db58cSmacallan int sc_vmax_charged;
88f41db58cSmacallan int sc_vmax_charging;
890c12bd26Smacallan uint32_t sc_timestamp;
90f41db58cSmacallan };
91f41db58cSmacallan
9205b09539Smatt static void battery_attach(device_t, device_t, void *);
9305b09539Smatt static int battery_match(device_t, cfdata_t, void *);
94f41db58cSmacallan static int battery_update(struct battery_softc *, int);
95f41db58cSmacallan static void battery_setup_envsys(struct battery_softc *);
9631962fc6Sxtraeme static void battery_refresh(struct sysmon_envsys *, envsys_data_t *);
970c12bd26Smacallan static void battery_poll(void *);
98f41db58cSmacallan
9905b09539Smatt CFATTACH_DECL_NEW(battery, sizeof(struct battery_softc),
100f41db58cSmacallan battery_match, battery_attach, NULL, NULL);
101f41db58cSmacallan
102f41db58cSmacallan static int
battery_match(device_t parent,cfdata_t cf,void * aux)10305b09539Smatt battery_match(device_t parent, cfdata_t cf, void *aux)
104f41db58cSmacallan {
105f41db58cSmacallan struct battery_attach_args *baa = aux;
106f41db58cSmacallan
107f41db58cSmacallan if (baa->baa_type == BATTERY_TYPE_LEGACY)
108f41db58cSmacallan return 1;
109f41db58cSmacallan
110f41db58cSmacallan return 0;
111f41db58cSmacallan }
112f41db58cSmacallan
113f41db58cSmacallan static void
battery_attach(device_t parent,device_t self,void * aux)11405b09539Smatt battery_attach(device_t parent, device_t self, void *aux)
115f41db58cSmacallan {
116f41db58cSmacallan struct battery_attach_args *baa = aux;
11705b09539Smatt struct battery_softc *sc = device_private(self);
118f41db58cSmacallan uint32_t reg;
119f41db58cSmacallan
120*4d01e983Smacallan sc->sc_dev = self;
121f41db58cSmacallan sc->sc_pmu_ops = baa->baa_pmu_ops;
12205b09539Smatt aprint_normal(": legacy battery ");
123f41db58cSmacallan
124f41db58cSmacallan reg = in32rb(0xf3000034);
125f41db58cSmacallan DPRINTF("reg: %08x\n", reg);
126f41db58cSmacallan if (reg & 0x20000000) {
127f41db58cSmacallan sc->sc_type = BTYPE_HOOPER;
128f41db58cSmacallan sc->sc_vmax_charged = 330;
129f41db58cSmacallan sc->sc_vmax_charging = 365;
130f41db58cSmacallan printf("[hooper]\n");
131f41db58cSmacallan } else {
132f41db58cSmacallan sc->sc_type = BTYPE_COMET;
133f41db58cSmacallan sc->sc_vmax_charged = 189;
134f41db58cSmacallan sc->sc_vmax_charging = 213;
135f41db58cSmacallan printf("[comet]\n");
136f41db58cSmacallan }
137f41db58cSmacallan battery_update(sc, 1);
1380c12bd26Smacallan /* trigger a status update */
1390c12bd26Smacallan sc->sc_oflags = ~sc->sc_flags;
1400c12bd26Smacallan
141f41db58cSmacallan battery_setup_envsys(sc);
1420c12bd26Smacallan sc->sc_pmu_ops->register_callback(sc->sc_pmu_ops->cookie, battery_poll,
1430c12bd26Smacallan sc);
1440c12bd26Smacallan
1450c12bd26Smacallan memset(&sc->sc_sm_acpower, 0, sizeof(struct sysmon_pswitch));
1460c12bd26Smacallan sc->sc_sm_acpower.smpsw_name = "AC Power";
1470c12bd26Smacallan sc->sc_sm_acpower.smpsw_type = PSWITCH_TYPE_ACADAPTER;
1480c12bd26Smacallan if (sysmon_pswitch_register(&sc->sc_sm_acpower) != 0)
14905b09539Smatt aprint_error_dev(self,
15005b09539Smatt "unable to register AC power status with sysmon\n");
151f41db58cSmacallan }
152f41db58cSmacallan
153f41db58cSmacallan static int
battery_update(struct battery_softc * sc,int out)154f41db58cSmacallan battery_update(struct battery_softc *sc, int out)
155f41db58cSmacallan {
156f41db58cSmacallan int len, vmax, pcharge, vb;
157f41db58cSmacallan uint8_t buf[16];
158f41db58cSmacallan
1590c12bd26Smacallan if (sc->sc_timestamp == time_second)
1600c12bd26Smacallan return 0;
1610c12bd26Smacallan sc->sc_timestamp = time_second;
1620c12bd26Smacallan
163f41db58cSmacallan len = sc->sc_pmu_ops->do_command(sc->sc_pmu_ops->cookie,
164d0ebad92Smacallan PMU_BATTERY_STATE, 0, NULL, 16, buf);
165f41db58cSmacallan if (len != 9)
166f41db58cSmacallan return -1;
167f41db58cSmacallan
168f41db58cSmacallan sc->sc_flags = buf[1];
169f41db58cSmacallan
170f41db58cSmacallan if (out) {
171f41db58cSmacallan if (buf[1] & PMU_PWR_AC_PRESENT)
172f41db58cSmacallan printf(" AC");
173f41db58cSmacallan if (buf[1] & PMU_PWR_BATT_CHARGING)
174f41db58cSmacallan printf(" charging");
175f41db58cSmacallan if (buf[1] & PMU_PWR_BATT_PRESENT)
176f41db58cSmacallan printf(" present");
177f41db58cSmacallan if (buf[1] & PMU_PWR_BATT_FULL)
178f41db58cSmacallan printf(" full");
179f41db58cSmacallan printf("\n");
180f41db58cSmacallan }
181f41db58cSmacallan
182f41db58cSmacallan sc->sc_cpu_temp = buf[4];
183f41db58cSmacallan
184f41db58cSmacallan if ((sc->sc_flags & PMU_PWR_BATT_PRESENT) == 0) {
185f41db58cSmacallan sc->sc_voltage = 0;
186f41db58cSmacallan sc->sc_current = 0;
187f41db58cSmacallan sc->sc_bat_temp = 0;
188f41db58cSmacallan sc->sc_charge = 0;
189f41db58cSmacallan sc->sc_time = 0;
190f41db58cSmacallan return 0;
191f41db58cSmacallan }
192f41db58cSmacallan
193f41db58cSmacallan vmax = sc->sc_vmax_charged;
194f41db58cSmacallan vb = (buf[2] << 8) | buf[3];
195f41db58cSmacallan sc->sc_voltage = (vb * 265 + 72665) / 10;
196f41db58cSmacallan sc->sc_current = buf[6];
197f41db58cSmacallan if ((sc->sc_flags & PMU_PWR_AC_PRESENT) == 0) {
198f41db58cSmacallan if (sc->sc_current > 200)
199f41db58cSmacallan vb += ((sc->sc_current - 200) * 15) / 100;
200f41db58cSmacallan } else {
201f41db58cSmacallan vmax = sc->sc_vmax_charging;
202f41db58cSmacallan }
203f41db58cSmacallan sc->sc_charge = (100 * vb) / vmax;
204f41db58cSmacallan if (sc->sc_flags & PMU_PWR_PCHARGE_RESET) {
205f41db58cSmacallan pcharge = (buf[7] << 8) | buf[8];
206f41db58cSmacallan if (pcharge > 6500)
207f41db58cSmacallan pcharge = 6500;
208f41db58cSmacallan pcharge = 100 - pcharge * 100 / 6500;
209f41db58cSmacallan if (pcharge < sc->sc_charge)
210f41db58cSmacallan sc->sc_charge = pcharge;
211f41db58cSmacallan }
212f41db58cSmacallan if (sc->sc_current > 0) {
213f41db58cSmacallan sc->sc_time = (sc->sc_charge * 16440) / sc->sc_current;
214f41db58cSmacallan } else
215f41db58cSmacallan sc->sc_time = 0;
216f41db58cSmacallan
217f41db58cSmacallan sc->sc_bat_temp = buf[5];
218f41db58cSmacallan
219f41db58cSmacallan if (out) {
220f41db58cSmacallan printf("voltage: %d.%03d\n", sc->sc_voltage / 1000,
221f41db58cSmacallan sc->sc_voltage % 1000);
222f41db58cSmacallan printf("charge: %d%%\n", sc->sc_charge);
223f41db58cSmacallan if (sc->sc_time > 0)
224f41db58cSmacallan printf("time: %d:%02d\n", sc->sc_time / 60,
225f41db58cSmacallan sc->sc_time % 60);
226f41db58cSmacallan }
227f41db58cSmacallan
228f41db58cSmacallan return 0;
229f41db58cSmacallan }
230f41db58cSmacallan
231f41db58cSmacallan #define INITDATA(index, unit, string) \
2328957a299Sxtraeme sc->sc_sensor[index].units = unit; \
2330537bed9Spgoyette sc->sc_sensor[index].state = ENVSYS_SINVALID; \
2348957a299Sxtraeme snprintf(sc->sc_sensor[index].desc, \
2358957a299Sxtraeme sizeof(sc->sc_sensor[index].desc), "%s", string);
236f41db58cSmacallan
237f41db58cSmacallan static void
battery_setup_envsys(struct battery_softc * sc)238f41db58cSmacallan battery_setup_envsys(struct battery_softc *sc)
239f41db58cSmacallan {
24031962fc6Sxtraeme int i;
24131962fc6Sxtraeme
24231962fc6Sxtraeme sc->sc_sme = sysmon_envsys_create();
24331962fc6Sxtraeme
244f41db58cSmacallan INITDATA(BAT_CPU_TEMPERATURE, ENVSYS_STEMP, "CPU temperature");
245f41db58cSmacallan INITDATA(BAT_AC_PRESENT, ENVSYS_INDICATOR, "AC present");
246f41db58cSmacallan INITDATA(BAT_PRESENT, ENVSYS_INDICATOR, "Battery present");
247f41db58cSmacallan INITDATA(BAT_VOLTAGE, ENVSYS_SVOLTS_DC, "Battery voltage");
248c1b8e352Saymeric INITDATA(BAT_CHARGE, ENVSYS_SAMPHOUR, "Battery charge");
249c1b8e352Saymeric INITDATA(BAT_MAX_CHARGE, ENVSYS_SAMPHOUR, "Battery design cap");
250f41db58cSmacallan INITDATA(BAT_CURRENT, ENVSYS_SAMPS, "Battery current");
251f41db58cSmacallan INITDATA(BAT_TEMPERATURE, ENVSYS_STEMP, "Battery temperature");
252c1b8e352Saymeric INITDATA(BAT_CHARGING, ENVSYS_BATTERY_CHARGE, "Battery charging");
253f41db58cSmacallan INITDATA(BAT_FULL, ENVSYS_INDICATOR, "Battery full");
254f41db58cSmacallan #undef INITDATA
255f41db58cSmacallan
2567f37ac38Smacallan for (i = 0; i < BAT_NSENSORS; i++) {
2577f37ac38Smacallan if (sysmon_envsys_sensor_attach(sc->sc_sme,
2587f37ac38Smacallan &sc->sc_sensor[i])) {
2597f37ac38Smacallan sysmon_envsys_destroy(sc->sc_sme);
2607f37ac38Smacallan return;
2617f37ac38Smacallan }
2627f37ac38Smacallan }
2637f37ac38Smacallan
26405b09539Smatt sc->sc_sme->sme_name = device_xname(sc->sc_dev);
26531962fc6Sxtraeme sc->sc_sme->sme_cookie = sc;
26631962fc6Sxtraeme sc->sc_sme->sme_refresh = battery_refresh;
267f41db58cSmacallan
26831962fc6Sxtraeme if (sysmon_envsys_register(sc->sc_sme)) {
26905b09539Smatt aprint_error_dev(sc->sc_dev,
27005b09539Smatt "unable to register with sysmon\n");
27131962fc6Sxtraeme sysmon_envsys_destroy(sc->sc_sme);
27231962fc6Sxtraeme }
273f41db58cSmacallan }
274f41db58cSmacallan
27531962fc6Sxtraeme static void
battery_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)27631962fc6Sxtraeme battery_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
277f41db58cSmacallan {
278f41db58cSmacallan struct battery_softc *sc = sme->sme_cookie;
2798957a299Sxtraeme int which = edata->sensor;
280f41db58cSmacallan
281f41db58cSmacallan battery_update(sc, 0);
282f41db58cSmacallan
2838957a299Sxtraeme switch (which) {
2848957a299Sxtraeme case BAT_CPU_TEMPERATURE:
2858957a299Sxtraeme edata->value_cur = sc->sc_cpu_temp * 1000000 + 273150000;
2868957a299Sxtraeme break;
2878957a299Sxtraeme case BAT_AC_PRESENT:
2888957a299Sxtraeme edata->value_cur = (sc->sc_flags & PMU_PWR_AC_PRESENT);
2898957a299Sxtraeme break;
2903b8eb423Smacallan case BAT_PRESENT:
2913b8eb423Smacallan edata->value_cur = (sc->sc_flags & PMU_PWR_BATT_PRESENT);
2923b8eb423Smacallan break;
2938957a299Sxtraeme case BAT_VOLTAGE:
2948957a299Sxtraeme edata->value_cur = sc->sc_voltage * 1000;
2958957a299Sxtraeme break;
2968957a299Sxtraeme case BAT_CURRENT:
2978957a299Sxtraeme edata->value_cur = sc->sc_current * 1000;
2988957a299Sxtraeme break;
2998957a299Sxtraeme case BAT_CHARGE:
3008957a299Sxtraeme edata->value_cur = sc->sc_charge;
3018957a299Sxtraeme break;
3028957a299Sxtraeme case BAT_MAX_CHARGE:
3038957a299Sxtraeme edata->value_cur = 100;
3048957a299Sxtraeme break;
3058957a299Sxtraeme case BAT_TEMPERATURE:
3068957a299Sxtraeme edata->value_cur = sc->sc_bat_temp * 1000000 + 273150000;
3078957a299Sxtraeme break;
3088957a299Sxtraeme case BAT_CHARGING:
3098957a299Sxtraeme if ((sc->sc_flags & PMU_PWR_BATT_CHARGING) &&
3108957a299Sxtraeme (sc->sc_flags & PMU_PWR_AC_PRESENT))
3118957a299Sxtraeme edata->value_cur = 1;
3128957a299Sxtraeme else
3138957a299Sxtraeme edata->value_cur = 0;
314f41db58cSmacallan
3158957a299Sxtraeme break;
3168957a299Sxtraeme case BAT_FULL:
3178957a299Sxtraeme edata->value_cur = (sc->sc_flags & PMU_PWR_BATT_FULL);
3188957a299Sxtraeme break;
319f41db58cSmacallan }
320f41db58cSmacallan
32122d9c7edSxtraeme edata->state = ENVSYS_SVALID;
322f41db58cSmacallan }
3230c12bd26Smacallan
3240c12bd26Smacallan static void
battery_poll(void * cookie)3250c12bd26Smacallan battery_poll(void *cookie)
3260c12bd26Smacallan {
3270c12bd26Smacallan struct battery_softc *sc = cookie;
3280c12bd26Smacallan
329d974db0aSgarbled
3300c12bd26Smacallan battery_update(sc, 0);
3310c12bd26Smacallan if ((sc->sc_flags & PMU_PWR_AC_PRESENT) == sc->sc_oflags)
3320c12bd26Smacallan return;
3330c12bd26Smacallan
3340c12bd26Smacallan sc->sc_oflags = sc->sc_flags & PMU_PWR_AC_PRESENT;
3350c12bd26Smacallan
3360c12bd26Smacallan sysmon_pswitch_event(&sc->sc_sm_acpower,
3370c12bd26Smacallan sc->sc_oflags ? PSWITCH_EVENT_PRESSED :
3380c12bd26Smacallan PSWITCH_EVENT_RELEASED);
3390c12bd26Smacallan }
340