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