1 /* $NetBSD: battery.c,v 1.10 2008/04/29 06:53:02 martin Exp $ */
4 * Copyright (c) 2007 Michael Lorenz
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: battery.c,v 1.10 2008/04/29 06:53:02 martin Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
38 #include <dev/sysmon/sysmonvar.h>
39 #include <dev/sysmon/sysmon_taskq.h>
41 #include <macppc/dev/pmuvar.h>
42 #include <macppc/dev/batteryvar.h>
43 #include <machine/bus.h>
44 #include <machine/pio.h>
45 #include "opt_battery.h"
48 #define DPRINTF printf
50 #define DPRINTF while (0) printf
54 #define BTYPE_HOOPER 2
56 #define BAT_CPU_TEMPERATURE 0
57 #define BAT_AC_PRESENT 1
61 #define BAT_MAX_CHARGE 5
63 #define BAT_CHARGING 7
65 #define BAT_TEMPERATURE 9
66 #define BAT_NSENSORS 10 /* number of sensors */
68 struct battery_softc
{
70 struct pmu_ops
*sc_pmu_ops
;
74 struct sysmon_envsys
*sc_sme
;
75 envsys_data_t sc_sensor
[BAT_NSENSORS
];
76 struct sysmon_pswitch sc_sm_acpower
;
89 uint32_t sc_timestamp
;
92 static void battery_attach(struct device
*, struct device
*, void *);
93 static int battery_match(struct device
*, struct cfdata
*, 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 *);
99 CFATTACH_DECL(battery
, sizeof(struct battery_softc
),
100 battery_match
, battery_attach
, NULL
, NULL
);
103 battery_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
105 struct battery_attach_args
*baa
= aux
;
107 if (baa
->baa_type
== BATTERY_TYPE_LEGACY
)
114 battery_attach(struct device
*parent
, struct device
*self
, void *aux
)
116 struct battery_attach_args
*baa
= aux
;
117 struct battery_softc
*sc
= (struct battery_softc
*)self
;
120 sc
->sc_pmu_ops
= baa
->baa_pmu_ops
;
121 printf(": legacy battery ");
123 reg
= in32rb(0xf3000034);
124 DPRINTF("reg: %08x\n", reg
);
125 if (reg
& 0x20000000) {
126 sc
->sc_type
= BTYPE_HOOPER
;
127 sc
->sc_vmax_charged
= 330;
128 sc
->sc_vmax_charging
= 365;
129 printf("[hooper]\n");
131 sc
->sc_type
= BTYPE_COMET
;
132 sc
->sc_vmax_charged
= 189;
133 sc
->sc_vmax_charging
= 213;
136 battery_update(sc
, 1);
137 /* trigger a status update */
138 sc
->sc_oflags
= ~sc
->sc_flags
;
140 battery_setup_envsys(sc
);
141 sc
->sc_pmu_ops
->register_callback(sc
->sc_pmu_ops
->cookie
, battery_poll
,
144 memset(&sc
->sc_sm_acpower
, 0, sizeof(struct sysmon_pswitch
));
145 sc
->sc_sm_acpower
.smpsw_name
= "AC Power";
146 sc
->sc_sm_acpower
.smpsw_type
= PSWITCH_TYPE_ACADAPTER
;
147 if (sysmon_pswitch_register(&sc
->sc_sm_acpower
) != 0)
148 printf("%s: unable to register AC power status with sysmon\n",
149 sc
->sc_dev
.dv_xname
);
154 battery_update(struct battery_softc
*sc
, int out
)
156 int len
, vmax
, pcharge
, vb
;
159 if (sc
->sc_timestamp
== time_second
)
161 sc
->sc_timestamp
= time_second
;
163 len
= sc
->sc_pmu_ops
->do_command(sc
->sc_pmu_ops
->cookie
,
164 PMU_BATTERY_STATE
, 0, NULL
, 16, buf
);
168 sc
->sc_flags
= buf
[1];
171 if (buf
[1] & PMU_PWR_AC_PRESENT
)
173 if (buf
[1] & PMU_PWR_BATT_CHARGING
)
175 if (buf
[1] & PMU_PWR_BATT_PRESENT
)
177 if (buf
[1] & PMU_PWR_BATT_FULL
)
182 sc
->sc_cpu_temp
= buf
[4];
184 if ((sc
->sc_flags
& PMU_PWR_BATT_PRESENT
) == 0) {
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;
201 vmax
= sc
->sc_vmax_charging
;
203 sc
->sc_charge
= (100 * vb
) / vmax
;
204 if (sc
->sc_flags
& PMU_PWR_PCHARGE_RESET
) {
205 pcharge
= (buf
[7] << 8) | buf
[8];
208 pcharge
= 100 - pcharge
* 100 / 6500;
209 if (pcharge
< sc
->sc_charge
)
210 sc
->sc_charge
= pcharge
;
212 if (sc
->sc_current
> 0) {
213 sc
->sc_time
= (sc
->sc_charge
* 16440) / sc
->sc_current
;
217 sc
->sc_bat_temp
= buf
[5];
220 printf("voltage: %d.%03d\n", sc
->sc_voltage
/ 1000,
221 sc
->sc_voltage
% 1000);
222 printf("charge: %d%%\n", sc
->sc_charge
);
224 printf("time: %d:%02d\n", sc
->sc_time
/ 60,
231 #define INITDATA(index, unit, string) \
232 sc->sc_sensor[index].units = unit; \
233 snprintf(sc->sc_sensor[index].desc, \
234 sizeof(sc->sc_sensor[index].desc), "%s", string);
237 battery_setup_envsys(struct battery_softc
*sc
)
241 sc
->sc_sme
= sysmon_envsys_create();
243 INITDATA(BAT_CPU_TEMPERATURE
, ENVSYS_STEMP
, "CPU temperature");
244 INITDATA(BAT_AC_PRESENT
, ENVSYS_INDICATOR
, "AC present");
245 INITDATA(BAT_PRESENT
, ENVSYS_INDICATOR
, "Battery present");
246 INITDATA(BAT_VOLTAGE
, ENVSYS_SVOLTS_DC
, "Battery voltage");
247 INITDATA(BAT_CHARGE
, ENVSYS_SAMPHOUR
, "Battery charge");
248 INITDATA(BAT_MAX_CHARGE
, ENVSYS_SAMPHOUR
, "Battery design cap");
249 INITDATA(BAT_CURRENT
, ENVSYS_SAMPS
, "Battery current");
250 INITDATA(BAT_TEMPERATURE
, ENVSYS_STEMP
, "Battery temperature");
251 INITDATA(BAT_CHARGING
, ENVSYS_BATTERY_CHARGE
, "Battery charging");
252 INITDATA(BAT_FULL
, ENVSYS_INDICATOR
, "Battery full");
255 for (i
= 0; i
< BAT_NSENSORS
; i
++) {
256 if (sysmon_envsys_sensor_attach(sc
->sc_sme
,
257 &sc
->sc_sensor
[i
])) {
258 sysmon_envsys_destroy(sc
->sc_sme
);
263 sc
->sc_sme
->sme_name
= sc
->sc_dev
.dv_xname
;
264 sc
->sc_sme
->sme_cookie
= sc
;
265 sc
->sc_sme
->sme_refresh
= battery_refresh
;
267 if (sysmon_envsys_register(sc
->sc_sme
)) {
268 aprint_error("%s: unable to register with sysmon\n",
269 sc
->sc_dev
.dv_xname
);
270 sysmon_envsys_destroy(sc
->sc_sme
);
275 battery_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
277 struct battery_softc
*sc
= sme
->sme_cookie
;
278 int which
= edata
->sensor
;
280 battery_update(sc
, 0);
283 case BAT_CPU_TEMPERATURE
:
284 edata
->value_cur
= sc
->sc_cpu_temp
* 1000000 + 273150000;
287 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_AC_PRESENT
);
290 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_BATT_PRESENT
);
293 edata
->value_cur
= sc
->sc_voltage
* 1000;
296 edata
->value_cur
= sc
->sc_current
* 1000;
299 edata
->value_cur
= sc
->sc_charge
;
302 edata
->value_cur
= 100;
304 case BAT_TEMPERATURE
:
305 edata
->value_cur
= sc
->sc_bat_temp
* 1000000 + 273150000;
308 if ((sc
->sc_flags
& PMU_PWR_BATT_CHARGING
) &&
309 (sc
->sc_flags
& PMU_PWR_AC_PRESENT
))
310 edata
->value_cur
= 1;
312 edata
->value_cur
= 0;
316 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_BATT_FULL
);
320 edata
->state
= ENVSYS_SVALID
;
324 battery_poll(void *cookie
)
326 struct battery_softc
*sc
= cookie
;
329 battery_update(sc
, 0);
330 if ((sc
->sc_flags
& PMU_PWR_AC_PRESENT
) == sc
->sc_oflags
)
333 sc
->sc_oflags
= sc
->sc_flags
& PMU_PWR_AC_PRESENT
;
335 sysmon_pswitch_event(&sc
->sc_sm_acpower
,
336 sc
->sc_oflags
? PSWITCH_EVENT_PRESSED
:
337 PSWITCH_EVENT_RELEASED
);