xref: /netbsd-src/sys/arch/macppc/dev/battery.c (revision 4d01e983ed9985069b834834fe8bba933533ed4d)
1 /*	$NetBSD: battery.c,v 1.15 2011/07/26 08:37:45 macallan Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Michael Lorenz
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: battery.c,v 1.15 2011/07/26 08:37:45 macallan Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/proc.h>
37 
38 #include <dev/sysmon/sysmonvar.h>
39 #include <dev/sysmon/sysmon_taskq.h>
40 
41 #include <macppc/dev/pmuvar.h>
42 #include <macppc/dev/batteryvar.h>
43 #include <sys/bus.h>
44 #include <machine/pio.h>
45 #include "opt_battery.h"
46 
47 #ifdef BATTERY_DEBUG
48 #define DPRINTF printf
49 #else
50 #define DPRINTF while (0) printf
51 #endif
52 
53 #define BTYPE_COMET	1
54 #define BTYPE_HOOPER	2
55 
56 #define BAT_CPU_TEMPERATURE	0
57 #define BAT_AC_PRESENT		1
58 #define BAT_PRESENT		2
59 #define BAT_VOLTAGE		3
60 #define BAT_CURRENT		4
61 #define BAT_MAX_CHARGE		5
62 #define BAT_CHARGE		6
63 #define BAT_CHARGING		7
64 #define BAT_FULL		8
65 #define BAT_TEMPERATURE		9
66 #define BAT_NSENSORS		10  /* number of sensors */
67 
68 struct battery_softc {
69 	device_t sc_dev;
70 	struct pmu_ops *sc_pmu_ops;
71 	int sc_type;
72 
73 	/* envsys stuff */
74 	struct sysmon_envsys *sc_sme;
75 	envsys_data_t sc_sensor[BAT_NSENSORS];
76 	struct sysmon_pswitch sc_sm_acpower;
77 
78 	/* battery status */
79 	int sc_flags;
80 	int sc_oflags;
81 	int sc_voltage;
82 	int sc_charge;
83 	int sc_current;
84 	int sc_time;
85 	int sc_cpu_temp;
86 	int sc_bat_temp;
87 	int sc_vmax_charged;
88 	int sc_vmax_charging;
89 	uint32_t sc_timestamp;
90 };
91 
92 static void battery_attach(device_t, device_t, void *);
93 static int battery_match(device_t, cfdata_t, void *);
94 static int battery_update(struct battery_softc *, int);
95 static void battery_setup_envsys(struct battery_softc *);
96 static void battery_refresh(struct sysmon_envsys *, envsys_data_t *);
97 static void battery_poll(void *);
98 
99 CFATTACH_DECL_NEW(battery, sizeof(struct battery_softc),
100     battery_match, battery_attach, NULL, NULL);
101 
102 static int
battery_match(device_t parent,cfdata_t cf,void * aux)103 battery_match(device_t parent, cfdata_t cf, void *aux)
104 {
105 	struct battery_attach_args *baa = aux;
106 
107 	if (baa->baa_type == BATTERY_TYPE_LEGACY)
108 		return 1;
109 
110 	return 0;
111 }
112 
113 static void
battery_attach(device_t parent,device_t self,void * aux)114 battery_attach(device_t parent, device_t self, void *aux)
115 {
116 	struct battery_attach_args *baa = aux;
117 	struct battery_softc *sc = device_private(self);
118 	uint32_t reg;
119 
120 	sc->sc_dev = self;
121 	sc->sc_pmu_ops = baa->baa_pmu_ops;
122 	aprint_normal(": legacy battery ");
123 
124 	reg = in32rb(0xf3000034);
125 	DPRINTF("reg: %08x\n", reg);
126 	if (reg & 0x20000000) {
127 		sc->sc_type = BTYPE_HOOPER;
128 		sc->sc_vmax_charged = 330;
129 		sc->sc_vmax_charging = 365;
130 		printf("[hooper]\n");
131 	} else {
132 		sc->sc_type = BTYPE_COMET;
133 		sc->sc_vmax_charged = 189;
134 		sc->sc_vmax_charging = 213;
135 		printf("[comet]\n");
136 	}
137 	battery_update(sc, 1);
138 	/* trigger a status update */
139 	sc->sc_oflags = ~sc->sc_flags;
140 
141 	battery_setup_envsys(sc);
142 	sc->sc_pmu_ops->register_callback(sc->sc_pmu_ops->cookie, battery_poll,
143 	    sc);
144 
145 	memset(&sc->sc_sm_acpower, 0, sizeof(struct sysmon_pswitch));
146 	sc->sc_sm_acpower.smpsw_name = "AC Power";
147 	sc->sc_sm_acpower.smpsw_type = PSWITCH_TYPE_ACADAPTER;
148 	if (sysmon_pswitch_register(&sc->sc_sm_acpower) != 0)
149 		aprint_error_dev(self,
150 		    "unable to register AC power status with sysmon\n");
151 }
152 
153 static int
battery_update(struct battery_softc * sc,int out)154 battery_update(struct battery_softc *sc, int out)
155 {
156 	int len, vmax, pcharge, vb;
157 	uint8_t buf[16];
158 
159 	if (sc->sc_timestamp == time_second)
160 		return 0;
161 	sc->sc_timestamp = time_second;
162 
163 	len = sc->sc_pmu_ops->do_command(sc->sc_pmu_ops->cookie,
164 	    PMU_BATTERY_STATE, 0, NULL, 16, buf);
165 	if (len != 9)
166 		return -1;
167 
168 	sc->sc_flags = buf[1];
169 
170 	if (out) {
171 		if (buf[1] & PMU_PWR_AC_PRESENT)
172 			printf(" AC");
173 		if (buf[1] & PMU_PWR_BATT_CHARGING)
174 			printf(" charging");
175 		if (buf[1] & PMU_PWR_BATT_PRESENT)
176 			printf(" present");
177 		if (buf[1] & PMU_PWR_BATT_FULL)
178 			printf(" full");
179 		printf("\n");
180 	}
181 
182 	sc->sc_cpu_temp = buf[4];
183 
184 	if ((sc->sc_flags & PMU_PWR_BATT_PRESENT) == 0) {
185 		sc->sc_voltage = 0;
186 		sc->sc_current = 0;
187 		sc->sc_bat_temp = 0;
188 		sc->sc_charge = 0;
189 		sc->sc_time = 0;
190 		return 0;
191 	}
192 
193 	vmax = sc->sc_vmax_charged;
194 	vb = (buf[2] << 8) | buf[3];
195 	sc->sc_voltage = (vb * 265 + 72665) / 10;
196 	sc->sc_current = buf[6];
197 	if ((sc->sc_flags & PMU_PWR_AC_PRESENT) == 0) {
198 	    	if (sc->sc_current > 200)
199 			vb += ((sc->sc_current - 200) * 15) / 100;
200 	} else {
201 		vmax = sc->sc_vmax_charging;
202 	}
203 	sc->sc_charge = (100 * vb) / vmax;
204 	if (sc->sc_flags & PMU_PWR_PCHARGE_RESET) {
205 		pcharge = (buf[7] << 8) | buf[8];
206 		if (pcharge > 6500)
207 			pcharge = 6500;
208 		pcharge = 100 - pcharge * 100 / 6500;
209 		if (pcharge < sc->sc_charge)
210 			sc->sc_charge = pcharge;
211 	}
212 	if (sc->sc_current > 0) {
213 		sc->sc_time = (sc->sc_charge * 16440) / sc->sc_current;
214 	} else
215 		sc->sc_time = 0;
216 
217 	sc->sc_bat_temp = buf[5];
218 
219 	if (out) {
220 		printf("voltage: %d.%03d\n", sc->sc_voltage / 1000,
221 		    sc->sc_voltage % 1000);
222 		printf("charge:  %d%%\n", sc->sc_charge);
223 		if (sc->sc_time > 0)
224 			printf("time:    %d:%02d\n", sc->sc_time / 60,
225 			    sc->sc_time % 60);
226 	}
227 
228 	return 0;
229 }
230 
231 #define INITDATA(index, unit, string)					\
232 	sc->sc_sensor[index].units = unit;     				\
233 	sc->sc_sensor[index].state = ENVSYS_SINVALID;			\
234 	snprintf(sc->sc_sensor[index].desc,				\
235 	    sizeof(sc->sc_sensor[index].desc), "%s", string);
236 
237 static void
battery_setup_envsys(struct battery_softc * sc)238 battery_setup_envsys(struct battery_softc *sc)
239 {
240 	int i;
241 
242 	sc->sc_sme = sysmon_envsys_create();
243 
244 	INITDATA(BAT_CPU_TEMPERATURE, ENVSYS_STEMP, "CPU temperature");
245 	INITDATA(BAT_AC_PRESENT, ENVSYS_INDICATOR, "AC present");
246 	INITDATA(BAT_PRESENT, ENVSYS_INDICATOR, "Battery present");
247 	INITDATA(BAT_VOLTAGE, ENVSYS_SVOLTS_DC, "Battery voltage");
248 	INITDATA(BAT_CHARGE, ENVSYS_SAMPHOUR, "Battery charge");
249 	INITDATA(BAT_MAX_CHARGE, ENVSYS_SAMPHOUR, "Battery design cap");
250 	INITDATA(BAT_CURRENT, ENVSYS_SAMPS, "Battery current");
251 	INITDATA(BAT_TEMPERATURE, ENVSYS_STEMP, "Battery temperature");
252 	INITDATA(BAT_CHARGING, ENVSYS_BATTERY_CHARGE, "Battery charging");
253 	INITDATA(BAT_FULL, ENVSYS_INDICATOR, "Battery full");
254 #undef INITDATA
255 
256 	for (i = 0; i < BAT_NSENSORS; i++) {
257 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
258 						&sc->sc_sensor[i])) {
259 			sysmon_envsys_destroy(sc->sc_sme);
260 			return;
261 		}
262 	}
263 
264 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
265 	sc->sc_sme->sme_cookie = sc;
266 	sc->sc_sme->sme_refresh = battery_refresh;
267 
268 	if (sysmon_envsys_register(sc->sc_sme)) {
269 		aprint_error_dev(sc->sc_dev,
270 		    "unable to register with sysmon\n");
271 		sysmon_envsys_destroy(sc->sc_sme);
272 	}
273 }
274 
275 static void
battery_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)276 battery_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
277 {
278 	struct battery_softc *sc = sme->sme_cookie;
279 	int which = edata->sensor;
280 
281 	battery_update(sc, 0);
282 
283 	switch (which) {
284 	case BAT_CPU_TEMPERATURE:
285 		edata->value_cur = sc->sc_cpu_temp * 1000000 + 273150000;
286 		break;
287 	case BAT_AC_PRESENT:
288 		edata->value_cur = (sc->sc_flags & PMU_PWR_AC_PRESENT);
289 		break;
290 	case BAT_PRESENT:
291 		edata->value_cur = (sc->sc_flags & PMU_PWR_BATT_PRESENT);
292 		break;
293 	case BAT_VOLTAGE:
294 		edata->value_cur = sc->sc_voltage * 1000;
295 		break;
296 	case BAT_CURRENT:
297 		edata->value_cur = sc->sc_current * 1000;
298 		break;
299 	case BAT_CHARGE:
300 		edata->value_cur = sc->sc_charge;
301 		break;
302 	case BAT_MAX_CHARGE:
303 		edata->value_cur = 100;
304 		break;
305 	case BAT_TEMPERATURE:
306 		edata->value_cur = sc->sc_bat_temp * 1000000 + 273150000;
307 		break;
308 	case BAT_CHARGING:
309 		if ((sc->sc_flags & PMU_PWR_BATT_CHARGING) &&
310 		    (sc->sc_flags & PMU_PWR_AC_PRESENT))
311 			edata->value_cur = 1;
312 		else
313 			edata->value_cur = 0;
314 
315 		break;
316 	case BAT_FULL:
317 		edata->value_cur = (sc->sc_flags & PMU_PWR_BATT_FULL);
318 		break;
319 	}
320 
321 	edata->state = ENVSYS_SVALID;
322 }
323 
324 static void
battery_poll(void * cookie)325 battery_poll(void *cookie)
326 {
327 	struct battery_softc *sc = cookie;
328 
329 
330 	battery_update(sc, 0);
331 	if ((sc->sc_flags & PMU_PWR_AC_PRESENT) == sc->sc_oflags)
332 		return;
333 
334 	sc->sc_oflags = sc->sc_flags & PMU_PWR_AC_PRESENT;
335 
336 	sysmon_pswitch_event(&sc->sc_sm_acpower,
337 	    sc->sc_oflags ? PSWITCH_EVENT_PRESSED :
338 	    PSWITCH_EVENT_RELEASED);
339 }
340