1 /* $NetBSD: smartbat.c,v 1.2 2008/04/29 06:53:02 martin Exp $ */
4 * Copyright (c) 2007 Michael Lorenz
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: smartbat.c,v 1.2 2008/04/29 06:53:02 martin Exp $");
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/device.h>
39 #include <dev/sysmon/sysmonvar.h>
40 #include <dev/sysmon/sysmon_taskq.h>
42 #include <macppc/dev/pmuvar.h>
43 #include <macppc/dev/batteryvar.h>
44 #include <machine/bus.h>
45 #include "opt_battery.h"
48 #define DPRINTF printf
50 #define DPRINTF while (0) printf
53 #define BAT_AC_PRESENT 0
57 #define BAT_MAX_CHARGE 4
59 #define BAT_CHARGING 6
61 #define BAT_NSENSORS 8 /* number of sensors */
63 struct smartbat_softc
{
65 struct pmu_ops
*sc_pmu_ops
;
69 struct sysmon_envsys
*sc_sme
;
70 envsys_data_t sc_sensor
[BAT_NSENSORS
];
71 struct sysmon_pswitch sc_sm_acpower
;
81 uint32_t sc_timestamp
;
84 static void smartbat_attach(struct device
*, struct device
*, void *);
85 static int smartbat_match(struct device
*, struct cfdata
*, void *);
86 static void smartbat_setup_envsys(struct smartbat_softc
*);
87 static void smartbat_refresh(struct sysmon_envsys
*, envsys_data_t
*);
88 static void smartbat_poll(void *);
89 static int smartbat_update(struct smartbat_softc
*, int);
91 CFATTACH_DECL(smartbat
, sizeof(struct smartbat_softc
),
92 smartbat_match
, smartbat_attach
, NULL
, NULL
);
95 smartbat_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
97 struct battery_attach_args
*baa
= aux
;
99 if (baa
->baa_type
== BATTERY_TYPE_SMART
)
106 smartbat_attach(struct device
*parent
, struct device
*self
, void *aux
)
108 struct battery_attach_args
*baa
= aux
;
109 struct smartbat_softc
*sc
= (struct smartbat_softc
*)self
;
111 sc
->sc_pmu_ops
= baa
->baa_pmu_ops
;
112 sc
->sc_num
= baa
->baa_num
;
114 printf(" addr %d: smart battery\n", sc
->sc_num
);
116 smartbat_update(sc
, 1);
117 /* trigger a status update */
118 sc
->sc_oflags
= ~sc
->sc_flags
;
120 smartbat_setup_envsys(sc
);
121 sc
->sc_pmu_ops
->register_callback(sc
->sc_pmu_ops
->cookie
, smartbat_poll
,
124 memset(&sc
->sc_sm_acpower
, 0, sizeof(struct sysmon_pswitch
));
125 sc
->sc_sm_acpower
.smpsw_name
= "AC Power";
126 sc
->sc_sm_acpower
.smpsw_type
= PSWITCH_TYPE_ACADAPTER
;
127 if (sysmon_pswitch_register(&sc
->sc_sm_acpower
) != 0)
128 printf("%s: unable to register AC power status with sysmon\n",
129 sc
->sc_dev
.dv_xname
);
132 #define INITDATA(index, unit, string) \
133 sc->sc_sensor[index].units = unit; \
134 snprintf(sc->sc_sensor[index].desc, \
135 sizeof(sc->sc_sensor[index].desc), "%s", string);
138 smartbat_setup_envsys(struct smartbat_softc
*sc
)
142 sc
->sc_sme
= sysmon_envsys_create();
144 INITDATA(BAT_AC_PRESENT
, ENVSYS_INDICATOR
, "AC present");
145 INITDATA(BAT_PRESENT
, ENVSYS_INDICATOR
, "Battery present");
146 INITDATA(BAT_VOLTAGE
, ENVSYS_SVOLTS_DC
, "Battery voltage");
147 INITDATA(BAT_CURRENT
, ENVSYS_SAMPS
, "Battery current");
148 INITDATA(BAT_MAX_CHARGE
, ENVSYS_SAMPHOUR
, "Battery design cap");
149 INITDATA(BAT_CHARGE
, ENVSYS_SAMPHOUR
, "Battery charge");
150 INITDATA(BAT_CHARGING
, ENVSYS_BATTERY_CHARGE
, "Battery charging");
151 INITDATA(BAT_FULL
, ENVSYS_INDICATOR
, "Battery full");
154 for (i
= 0; i
< BAT_NSENSORS
; i
++) {
155 if (sysmon_envsys_sensor_attach(sc
->sc_sme
,
156 &sc
->sc_sensor
[i
])) {
157 sysmon_envsys_destroy(sc
->sc_sme
);
162 sc
->sc_sme
->sme_name
= sc
->sc_dev
.dv_xname
;
163 sc
->sc_sme
->sme_cookie
= sc
;
164 sc
->sc_sme
->sme_refresh
= smartbat_refresh
;
166 if (sysmon_envsys_register(sc
->sc_sme
)) {
167 aprint_error("%s: unable to register with sysmon\n",
168 sc
->sc_dev
.dv_xname
);
169 sysmon_envsys_destroy(sc
->sc_sme
);
174 smartbat_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
176 struct smartbat_softc
*sc
= sme
->sme_cookie
;
177 int which
= edata
->sensor
;
179 smartbat_update(sc
, 0);
183 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_AC_PRESENT
);
186 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_BATT_PRESENT
);
189 edata
->value_cur
= sc
->sc_voltage
* 1000;
192 edata
->value_cur
= sc
->sc_draw
* 1000;
195 edata
->value_cur
= sc
->sc_max_charge
* 1000;
198 edata
->value_cur
= sc
->sc_charge
* 1000;
201 if ((sc
->sc_flags
& PMU_PWR_BATT_CHARGING
) &&
202 (sc
->sc_flags
& PMU_PWR_AC_PRESENT
))
203 edata
->value_cur
= 1;
205 edata
->value_cur
= 0;
209 edata
->value_cur
= (sc
->sc_flags
& PMU_PWR_BATT_FULL
);
213 edata
->state
= ENVSYS_SVALID
;
217 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
218 * for a clear description of the PMU results.
221 smartbat_update(struct smartbat_softc
*sc
, int out
)
225 uint8_t battery_number
;
227 if (sc
->sc_timestamp
== time_second
)
229 sc
->sc_timestamp
= time_second
;
231 /* sc_num starts from 0, but we need to start from 1 */
232 battery_number
= sc
->sc_num
+ 1;
233 len
= sc
->sc_pmu_ops
->do_command(sc
->sc_pmu_ops
->cookie
,
234 PMU_SMART_BATTERY_STATE
,
239 DPRINTF("%s: couldn't get battery data\n", sc
->sc_dev
.dv_xname
);
240 /* XXX: the return value is never checked */
244 /* Now, buf[0] is the command number, which we already know.
245 That's why all indexes are off by one compared to
246 pm_battery_info_smart in pm_direct.c.
248 sc
->sc_flags
= buf
[2];
250 /* XXX: are these all valid for smart batteries? */
252 printf(" flags: %x", buf
[2]);
253 if (buf
[2] & PMU_PWR_AC_PRESENT
)
255 if (buf
[2] & PMU_PWR_BATT_CHARGING
)
257 if (buf
[2] & PMU_PWR_BATT_PRESENT
)
259 if (buf
[2] & PMU_PWR_BATT_FULL
)
267 sc
->sc_charge
= buf
[3];
268 sc
->sc_max_charge
= buf
[4];
269 sc
->sc_draw
= *((signed char *)&buf
[5]);
270 sc
->sc_voltage
= buf
[6];
273 sc
->sc_charge
= ((buf
[3] << 8) | (buf
[4]));
274 sc
->sc_max_charge
= ((buf
[5] << 8) | (buf
[6]));
275 sc
->sc_draw
= *((signed short *)&buf
[7]);
276 sc
->sc_voltage
= ((buf
[9] << 8) | (buf
[8]));
279 /* XXX - Error condition */
280 DPRINTF("%s: why is buf[1] %x?\n", sc
->sc_dev
.dv_xname
, buf
[1]);
282 sc
->sc_max_charge
= 0;
292 smartbat_poll(void *cookie
)
294 struct smartbat_softc
*sc
= cookie
;
296 smartbat_update(sc
, 0);
297 if ((sc
->sc_flags
& PMU_PWR_AC_PRESENT
) == sc
->sc_oflags
)
300 sc
->sc_oflags
= sc
->sc_flags
& PMU_PWR_AC_PRESENT
;
302 sysmon_pswitch_event(&sc
->sc_sm_acpower
,
303 sc
->sc_oflags
? PSWITCH_EVENT_PRESSED
:
304 PSWITCH_EVENT_RELEASED
);