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 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 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 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 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 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 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