1 /* $OpenBSD: cwfg.c,v 1.6 2021/04/01 12:06:00 kn Exp $ */ 2 /* $NetBSD: cwfg.c,v 1.1 2020/01/03 18:00:05 jmcneill Exp $ */ 3 /*- 4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> 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/param.h> 30 #include <sys/systm.h> 31 #include <sys/device.h> 32 #include <sys/malloc.h> 33 #include <sys/sensors.h> 34 35 #include <machine/apmvar.h> 36 #include <machine/fdt.h> 37 38 #include <dev/ofw/openfirm.h> 39 40 #include <dev/i2c/i2cvar.h> 41 42 #include "apm.h" 43 44 #define VERSION_REG 0x00 45 #define VCELL_HI_REG 0x02 46 #define VCELL_HI_MASK 0x3f 47 #define VCELL_HI_SHIFT 0 48 #define VCELL_LO_REG 0x03 49 #define VCELL_LO_MASK 0xff 50 #define VCELL_LO_SHIFT 0 51 #define SOC_HI_REG 0x04 52 #define SOC_LO_REG 0x05 53 #define RTT_ALRT_HI_REG 0x06 54 #define RTT_ALRT (1 << 7) 55 #define RTT_HI_MASK 0x1f 56 #define RTT_HI_SHIFT 0 57 #define RTT_ALRT_LO_REG 0x07 58 #define RTT_LO_MASK 0xff 59 #define RTT_LO_SHIFT 0 60 #define CONFIG_REG 0x08 61 #define CONFIG_UFG (1 << 1) 62 #define MODE_REG 0x0a 63 #define MODE_SLEEP_MASK (0x3 << 6) 64 #define MODE_SLEEP_WAKE (0x0 << 6) 65 #define MODE_SLEEP_SLEEP (0x3 << 6) 66 #define MODE_QSTRT_MASK 0x3 67 #define MODE_QSTRT_SHIFT 4 68 #define MODE_POR (0xf << 0) 69 #define BATINFO_REG(n) (0x10 + (n)) 70 71 #define VCELL_STEP 312 72 #define VCELL_DIV 1024 73 #define BATINFO_SIZE 64 74 #define RESET_COUNT 30 75 #define RESET_DELAY 100000 76 77 enum cwfg_sensor { 78 CWFG_SENSOR_VCELL, 79 CWFG_SENSOR_SOC, 80 CWFG_SENSOR_RTT, 81 CWFG_NSENSORS 82 }; 83 84 struct cwfg_softc { 85 struct device sc_dev; 86 i2c_tag_t sc_tag; 87 i2c_addr_t sc_addr; 88 int sc_node; 89 90 uint8_t sc_batinfo[BATINFO_SIZE]; 91 92 struct ksensor sc_sensor[CWFG_NSENSORS]; 93 struct ksensordev sc_sensordev; 94 }; 95 96 int cwfg_match(struct device *, void *, void *); 97 void cwfg_attach(struct device *, struct device *, void *); 98 99 int cwfg_init(struct cwfg_softc *); 100 int cwfg_set_config(struct cwfg_softc *); 101 int cwfg_lock(struct cwfg_softc *); 102 void cwfg_unlock(struct cwfg_softc *); 103 int cwfg_read(struct cwfg_softc *, uint8_t, uint8_t *); 104 int cwfg_write(struct cwfg_softc *, uint8_t, uint8_t); 105 void cwfg_update_sensors(void *); 106 107 struct cfattach cwfg_ca = { 108 sizeof(struct cwfg_softc), cwfg_match, cwfg_attach 109 }; 110 111 struct cfdriver cwfg_cd = { 112 NULL, "cwfg", DV_DULL 113 }; 114 115 #if NAPM > 0 116 struct apm_power_info cwfg_power = { 117 .battery_state = APM_BATT_UNKNOWN, 118 .ac_state = APM_AC_UNKNOWN, 119 .battery_life = 0, 120 .minutes_left = -1, 121 }; 122 123 int 124 cwfg_apminfo(struct apm_power_info *info) 125 { 126 memcpy(info, &cwfg_power, sizeof(*info)); 127 return 0; 128 } 129 #endif 130 131 int 132 cwfg_match(struct device *parent, void *match, void *aux) 133 { 134 struct i2c_attach_args *ia = aux; 135 136 if (strcmp(ia->ia_name, "cellwise,cw2015") == 0) 137 return 1; 138 139 return 0; 140 } 141 142 void 143 cwfg_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct cwfg_softc *sc = (struct cwfg_softc *)self; 146 struct i2c_attach_args *ia = aux; 147 uint32_t *batinfo; 148 ssize_t len; 149 int n; 150 151 sc->sc_tag = ia->ia_tag; 152 sc->sc_addr = ia->ia_addr; 153 sc->sc_node = *(int *)ia->ia_cookie; 154 155 len = OF_getproplen(sc->sc_node, "cellwise,battery-profile"); 156 if (len <= 0) { 157 printf(": missing or invalid battery info\n"); 158 return; 159 } 160 161 batinfo = malloc(len, M_TEMP, M_WAITOK); 162 OF_getprop(sc->sc_node, "cellwise,battery-profile", batinfo, len); 163 switch (len) { 164 case BATINFO_SIZE: 165 memcpy(sc->sc_batinfo, batinfo, BATINFO_SIZE); 166 break; 167 case BATINFO_SIZE * 4: 168 for (n = 0; n < BATINFO_SIZE; n++) 169 sc->sc_batinfo[n] = be32toh(batinfo[n]); 170 break; 171 default: 172 printf(": invalid battery info\n"); 173 free(batinfo, M_TEMP, len); 174 return; 175 } 176 free(batinfo, M_TEMP, len); 177 178 if (cwfg_init(sc) != 0) { 179 printf(": failed to initialize device\n"); 180 return; 181 } 182 183 strlcpy(sc->sc_sensor[CWFG_SENSOR_VCELL].desc, "battery voltage", 184 sizeof(sc->sc_sensor[CWFG_SENSOR_VCELL].desc)); 185 sc->sc_sensor[CWFG_SENSOR_VCELL].type = SENSOR_VOLTS_DC; 186 sc->sc_sensor[CWFG_SENSOR_VCELL].flags = SENSOR_FINVALID; 187 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[CWFG_SENSOR_VCELL]); 188 189 strlcpy(sc->sc_sensor[CWFG_SENSOR_SOC].desc, "battery percent", 190 sizeof(sc->sc_sensor[CWFG_SENSOR_SOC].desc)); 191 sc->sc_sensor[CWFG_SENSOR_SOC].type = SENSOR_PERCENT; 192 sc->sc_sensor[CWFG_SENSOR_SOC].flags = SENSOR_FINVALID; 193 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[CWFG_SENSOR_SOC]); 194 195 strlcpy(sc->sc_sensor[CWFG_SENSOR_RTT].desc, "battery remaining " 196 "minutes", sizeof(sc->sc_sensor[CWFG_SENSOR_RTT].desc)); 197 sc->sc_sensor[CWFG_SENSOR_RTT].type = SENSOR_INTEGER; 198 sc->sc_sensor[CWFG_SENSOR_RTT].flags = SENSOR_FINVALID; 199 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[CWFG_SENSOR_RTT]); 200 201 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 202 sizeof(sc->sc_sensordev.xname)); 203 sensordev_install(&sc->sc_sensordev); 204 205 sensor_task_register(sc, cwfg_update_sensors, 5); 206 207 #if NAPM > 0 208 apm_setinfohook(cwfg_apminfo); 209 #endif 210 211 printf("\n"); 212 } 213 214 int 215 cwfg_init(struct cwfg_softc *sc) 216 { 217 uint8_t mode, soc; 218 int error, retry; 219 220 cwfg_lock(sc); 221 222 /* If the device is in sleep mode, wake it up */ 223 if ((error = cwfg_read(sc, MODE_REG, &mode)) != 0) 224 goto done; 225 if ((mode & MODE_SLEEP_MASK) == MODE_SLEEP_SLEEP) { 226 mode &= ~MODE_SLEEP_MASK; 227 mode |= MODE_SLEEP_WAKE; 228 if ((error = cwfg_write(sc, MODE_REG, mode)) != 0) 229 goto done; 230 } 231 232 /* Load battery profile */ 233 if ((error = cwfg_set_config(sc)) != 0) 234 goto done; 235 236 /* Wait for chip to become ready */ 237 for (retry = RESET_COUNT; retry > 0; retry--) { 238 if ((error = cwfg_read(sc, SOC_HI_REG, &soc)) != 0) 239 goto done; 240 if (soc != 0xff) 241 break; 242 delay(RESET_DELAY); 243 } 244 if (retry == 0) 245 printf("%s: timeout waiting for chip ready\n", 246 sc->sc_dev.dv_xname); 247 248 done: 249 cwfg_unlock(sc); 250 251 return error; 252 } 253 254 int 255 cwfg_set_config(struct cwfg_softc *sc) 256 { 257 uint8_t config, mode, val; 258 int need_update; 259 int error, n; 260 261 /* Read current config */ 262 if ((error = cwfg_read(sc, CONFIG_REG, &config)) != 0) 263 return error; 264 265 /* 266 * We need to upload a battery profile if either the UFG flag 267 * is unset, or the current battery profile differs from the 268 * one in the DT. 269 */ 270 need_update = !(config & CONFIG_UFG); 271 if (!need_update) { 272 for (n = 0; n < BATINFO_SIZE; n++) { 273 if ((error = cwfg_read(sc, BATINFO_REG(n), &val)) != 0) 274 return error; 275 if (sc->sc_batinfo[n] != val) { 276 need_update = 1; 277 break; 278 } 279 } 280 } 281 if (!need_update) 282 return 0; 283 284 /* Update battery profile */ 285 for (n = 0; n < BATINFO_SIZE; n++) { 286 val = sc->sc_batinfo[n]; 287 if ((error = cwfg_write(sc, BATINFO_REG(n), val)) != 0) 288 return error; 289 } 290 291 /* Set UFG flag to switch to new profile */ 292 if ((error = cwfg_read(sc, CONFIG_REG, &config)) != 0) 293 return error; 294 config |= CONFIG_UFG; 295 if ((error = cwfg_write(sc, CONFIG_REG, config)) != 0) 296 return error; 297 298 /* Restart the IC with new profile */ 299 if ((error = cwfg_read(sc, MODE_REG, &mode)) != 0) 300 return error; 301 mode |= MODE_POR; 302 if ((error = cwfg_write(sc, MODE_REG, mode)) != 0) 303 return error; 304 delay(20000); 305 mode &= ~MODE_POR; 306 if ((error = cwfg_write(sc, MODE_REG, mode)) != 0) 307 return error; 308 309 return error; 310 } 311 312 void 313 cwfg_update_sensors(void *arg) 314 { 315 struct cwfg_softc *sc = arg; 316 uint32_t vcell, rtt, tmp; 317 uint8_t val; 318 int error, n; 319 320 /* invalidate all previous reads to avoid stale/incoherent values 321 * in case of transient cwfg_read() failures below */ 322 sc->sc_sensor[CWFG_SENSOR_VCELL].flags |= SENSOR_FINVALID; 323 sc->sc_sensor[CWFG_SENSOR_SOC].flags |= SENSOR_FINVALID; 324 sc->sc_sensor[CWFG_SENSOR_RTT].flags |= SENSOR_FINVALID; 325 326 #if NAPM > 0 327 cwfg_power.battery_state = APM_BATT_UNKNOWN; 328 cwfg_power.ac_state = APM_AC_UNKNOWN; 329 cwfg_power.battery_life = 0; 330 cwfg_power.minutes_left = -1; 331 #endif 332 333 if ((error = cwfg_lock(sc)) != 0) 334 return; 335 336 /* VCELL: Take the average of three readings */ 337 vcell = 0; 338 for (n = 0; n < 3; n++) { 339 if ((error = cwfg_read(sc, VCELL_HI_REG, &val)) != 0) 340 goto done; 341 tmp = ((val >> VCELL_HI_SHIFT) & VCELL_HI_MASK) << 8; 342 if ((error = cwfg_read(sc, VCELL_LO_REG, &val)) != 0) 343 goto done; 344 tmp |= ((val >> VCELL_LO_SHIFT) & VCELL_LO_MASK); 345 vcell += tmp; 346 } 347 vcell /= 3; 348 sc->sc_sensor[CWFG_SENSOR_VCELL].value = 349 ((vcell * VCELL_STEP) / VCELL_DIV) * 1000; 350 sc->sc_sensor[CWFG_SENSOR_VCELL].flags &= ~SENSOR_FINVALID; 351 352 /* SOC */ 353 if ((error = cwfg_read(sc, SOC_HI_REG, &val)) != 0) 354 goto done; 355 if (val != 0xff) { 356 sc->sc_sensor[CWFG_SENSOR_SOC].value = val * 1000; 357 sc->sc_sensor[CWFG_SENSOR_SOC].flags &= ~SENSOR_FINVALID; 358 #if NAPM > 0 359 cwfg_power.battery_life = val; 360 if (val > 50) 361 cwfg_power.battery_state = APM_BATT_HIGH; 362 else if (val > 25) 363 cwfg_power.battery_state = APM_BATT_LOW; 364 else 365 cwfg_power.battery_state = APM_BATT_CRITICAL; 366 #endif 367 } 368 369 /* RTT */ 370 if ((error = cwfg_read(sc, RTT_ALRT_HI_REG, &val)) != 0) 371 goto done; 372 rtt = ((val >> RTT_HI_SHIFT) & RTT_HI_MASK) << 8; 373 if ((error = cwfg_read(sc, RTT_ALRT_LO_REG, &val)) != 0) 374 goto done; 375 rtt |= ((val >> RTT_LO_SHIFT) & RTT_LO_MASK); 376 if (rtt != 0x1fff) { 377 sc->sc_sensor[CWFG_SENSOR_RTT].value = rtt; 378 sc->sc_sensor[CWFG_SENSOR_RTT].flags &= ~SENSOR_FINVALID; 379 #if NAPM > 0 380 cwfg_power.minutes_left = rtt; 381 #endif 382 } 383 384 done: 385 cwfg_unlock(sc); 386 } 387 388 int 389 cwfg_lock(struct cwfg_softc *sc) 390 { 391 return iic_acquire_bus(sc->sc_tag, 0); 392 } 393 394 void 395 cwfg_unlock(struct cwfg_softc *sc) 396 { 397 iic_release_bus(sc->sc_tag, 0); 398 } 399 400 int 401 cwfg_read(struct cwfg_softc *sc, uint8_t reg, uint8_t *val) 402 { 403 return iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, reg, val, 0); 404 } 405 406 int 407 cwfg_write(struct cwfg_softc *sc, uint8_t reg, uint8_t val) 408 { 409 return iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, reg, val, 0); 410 } 411