xref: /netbsd-src/sys/arch/macppc/dev/battery.c (revision 4d01e983ed9985069b834834fe8bba933533ed4d)
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