1/* $NetBSD: act8846.c,v 1.6 2019/07/27 16:02:27 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2015 Jared D. 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//#define ACT_DEBUG 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: act8846.c,v 1.6 2019/07/27 16:02:27 thorpej Exp $"); 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37#include <sys/device.h> 38#include <sys/conf.h> 39#include <sys/bus.h> 40#include <sys/kmem.h> 41 42#include <dev/i2c/i2cvar.h> 43#include <dev/i2c/act8846.h> 44 45#define ACT_BATTVOL_STATUS_REG 0x00 46#define ACT_THERMAL_CTRL_REG 0x01 47#define ACT_DCDC1_BASE_REG 0x10 48#define ACT_DCDC2_BASE_REG 0x20 49#define ACT_DCDC3_BASE_REG 0x30 50#define ACT_DCDC4_BASE_REG 0x40 51#define ACT_LDO1_BASE_REG 0x50 52#define ACT_LDO2_BASE_REG 0x58 53#define ACT_LDO3_BASE_REG 0x60 54#define ACT_LDO4_BASE_REG 0x68 55#define ACT_LDO5_BASE_REG 0x70 56#define ACT_LDO6_BASE_REG 0x80 57#define ACT_LDO7_BASE_REG 0x90 58#define ACT_LDO8_BASE_REG 0xa0 59#define ACT_LDO9_BASE_REG 0xb0 60 61#define ACT_VSET0_OFFSET 0 62#define ACT_VSET1_OFFSET 1 63#define ACT_DCDC_CTRL_OFFSET 2 64#define ACT_LDO_CTRL_OFFSET 1 65 66#define ACT_VSET_VSET __BITS(5,0) 67 68#define ACT_DCDC_CTRL_ON __BIT(7) 69 70#define ACT_LDO_CTRL_ON __BIT(7) 71 72enum act8846_ctrl_type { 73 ACT_CTRL_DCDC, 74 ACT_CTRL_LDO, 75}; 76 77#define ACT_VOLTAGE_MIN 600 78#define ACT_VOLTAGE_MAX 3900 79 80struct act8846_ctrl { 81 device_t c_dev; 82 83 const char * c_name; 84 u_int c_min; 85 u_int c_max; 86 uint8_t c_base; 87 enum act8846_ctrl_type c_type; 88}; 89 90#define ACT_CTRL(name, base, type) \ 91 { .c_name = (name), \ 92 .c_min = ACT_VOLTAGE_MIN, .c_max = ACT_VOLTAGE_MAX, \ 93 .c_base = ACT_ ## base ## _BASE_REG, .c_type = (type) } 94 95#define ACT_DCDC(name, base) ACT_CTRL(name, base, ACT_CTRL_DCDC) 96#define ACT_LDO(name, base) ACT_CTRL(name, base, ACT_CTRL_LDO) 97 98static const struct act8846_ctrl act8846_ctrls[] = { 99 ACT_DCDC("DCDC1", DCDC1), /* VCC_DDR */ 100 ACT_DCDC("DCDC2", DCDC2), /* VDD_LOG */ 101 ACT_DCDC("DCDC3", DCDC3), /* VDD_ARM */ 102 ACT_DCDC("DCDC4", DCDC4), /* VCC_IO */ 103 ACT_LDO("LDO1", LDO1), /* VDD_10 */ 104 ACT_LDO("LDO2", LDO2), /* VCC_25 */ 105 ACT_LDO("LDO3", LDO3), /* VCC18_CIF */ 106 ACT_LDO("LDO4", LDO4), /* VCCA_33 */ 107 ACT_LDO("LDO5", LDO5), /* VCC_TOUCH */ 108 ACT_LDO("LDO6", LDO6), /* VCC33 */ 109 ACT_LDO("LDO7", LDO7), /* VCC18_IO */ 110 ACT_LDO("LDO8", LDO8), /* VCC28_CIF */ 111#if 0 112 ACT_LDO("LDO9", LDO9), /* VDD_RTC (Always-ON) */ 113#endif 114}; 115 116/* From datasheet, Table 5: REGx/VSET[] Output Voltage Setting */ 117static const u_int act8846_vset[] = { 118 600, 625, 650, 675, 700, 725, 750, 775, 119 800, 825, 850, 875, 900, 925, 950, 975, 120 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 121 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 122 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 123 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 124 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, 125 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900 126}; 127 128struct act8846_softc { 129 device_t sc_dev; 130 i2c_tag_t sc_i2c; 131 i2c_addr_t sc_addr; 132 133 u_int sc_nctrl; 134 struct act8846_ctrl *sc_ctrl; 135}; 136 137static int act8846_match(device_t, cfdata_t, void *); 138static void act8846_attach(device_t, device_t, void *); 139 140static int act8846_read(struct act8846_softc *, uint8_t, uint8_t *); 141static int act8846_write(struct act8846_softc *, uint8_t, uint8_t); 142 143static void act8846_print(struct act8846_ctrl *c); 144 145CFATTACH_DECL_NEW(act8846pm, sizeof(struct act8846_softc), 146 act8846_match, act8846_attach, NULL, NULL); 147 148static int 149act8846_match(device_t parent, cfdata_t match, void *aux) 150{ 151 struct i2c_attach_args *ia = aux; 152 153 if (ia->ia_addr == 0x5a) 154 return I2C_MATCH_ADDRESS_ONLY; 155 156 return 0; 157} 158 159static void 160act8846_attach(device_t parent, device_t self, void *aux) 161{ 162 struct act8846_softc *sc = device_private(self); 163 struct i2c_attach_args *ia = aux; 164 u_int n; 165 166 sc->sc_dev = self; 167 sc->sc_i2c = ia->ia_tag; 168 sc->sc_addr = ia->ia_addr; 169 170 aprint_naive("\n"); 171 aprint_normal("\n"); 172 173 sc->sc_nctrl = __arraycount(act8846_ctrls); 174 sc->sc_ctrl = kmem_alloc(sizeof(act8846_ctrls), KM_SLEEP); 175 memcpy(sc->sc_ctrl, act8846_ctrls, sizeof(act8846_ctrls)); 176 for (n = 0; n < sc->sc_nctrl; n++) { 177 sc->sc_ctrl[n].c_dev = self; 178 } 179 180 for (n = 0; n < sc->sc_nctrl; n++) { 181 act8846_print(&sc->sc_ctrl[n]); 182 } 183} 184 185static int 186act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val) 187{ 188 return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0); 189} 190 191static int 192act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val) 193{ 194 return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0); 195} 196 197static void 198act8846_print(struct act8846_ctrl *c) 199{ 200 struct act8846_softc *sc = device_private(c->c_dev); 201 u_int voltage; 202 bool enabled; 203 204 device_printf(sc->sc_dev, "%s:", c->c_name); 205 if (act8846_get_voltage(c, &voltage)) { 206 printf(" [??? V]"); 207 } else { 208 printf(" [%d.%03dV]", voltage / 1000, 209 voltage % 1000); 210 } 211 if (act8846_is_enabled(c, &enabled)) { 212 printf(" [unknown state]"); 213 } else { 214 printf(" [%s]", enabled ? "ON" : "OFF"); 215 } 216 printf("\n"); 217} 218 219struct act8846_ctrl * 220act8846_lookup(device_t dev, const char *name) 221{ 222 struct act8846_softc *sc = device_private(dev); 223 struct act8846_ctrl *c; 224 u_int n; 225 226 for (n = 0; n < sc->sc_nctrl; n++) { 227 c = &sc->sc_ctrl[n]; 228 if (strcmp(c->c_name, name) == 0) { 229 return c; 230 } 231 } 232 233 return NULL; 234} 235 236int 237act8846_set_voltage(struct act8846_ctrl *c, u_int min, u_int max) 238{ 239 struct act8846_softc *sc = device_private(c->c_dev); 240 uint8_t val; 241 int error, n; 242 243 if (min < c->c_min || min > c->c_max || (min % 25) != 0) 244 return EINVAL; 245 246 for (n = 0; n < __arraycount(act8846_vset); n++) { 247 if (min >= act8846_vset[n] && max <= act8846_vset[n]) { 248 break; 249 } 250 } 251 if (n == __arraycount(act8846_vset)) 252 return EINVAL; 253 254 val = __SHIFTIN(n, ACT_VSET_VSET); 255 256 iic_acquire_bus(sc->sc_i2c, 0); 257 error = act8846_write(sc, c->c_base + ACT_VSET0_OFFSET, val); 258 iic_release_bus(sc->sc_i2c, 0); 259#ifdef ACT_DEBUG 260 if (error == 0) 261 act8846_print(c); 262#endif 263 return error; 264} 265 266int 267act8846_get_voltage(struct act8846_ctrl *c, u_int *pvol) 268{ 269 struct act8846_softc *sc = device_private(c->c_dev); 270 uint8_t val; 271 int error; 272 273 iic_acquire_bus(sc->sc_i2c, 0); 274 error = act8846_read(sc, c->c_base + ACT_VSET0_OFFSET, &val); 275 iic_release_bus(sc->sc_i2c, 0); 276 if (error) 277 return error; 278 279 *pvol = act8846_vset[__SHIFTOUT(val, ACT_VSET_VSET)]; 280 281 return 0; 282} 283 284int 285act8846_is_enabled(struct act8846_ctrl *c, bool *penabled) 286{ 287 struct act8846_softc *sc = device_private(c->c_dev); 288 uint8_t val, regoff, regmask; 289 int error; 290 291 if (c->c_type == ACT_CTRL_DCDC) { 292 regoff = ACT_DCDC_CTRL_OFFSET; 293 regmask = ACT_DCDC_CTRL_ON; 294 } else { 295 regoff = ACT_LDO_CTRL_OFFSET; 296 regmask = ACT_LDO_CTRL_ON; 297 } 298 299 iic_acquire_bus(sc->sc_i2c, 0); 300 error = act8846_read(sc, c->c_base + regoff, &val); 301 iic_release_bus(sc->sc_i2c, 0); 302 if (error) 303 return error; 304 305 *penabled = !!(val & regmask); 306 return 0; 307} 308 309int 310act8846_enable(struct act8846_ctrl *c) 311{ 312 struct act8846_softc *sc = device_private(c->c_dev); 313 uint8_t val, regoff, regmask; 314 int error; 315 316 if (c->c_type == ACT_CTRL_DCDC) { 317 regoff = ACT_DCDC_CTRL_OFFSET; 318 regmask = ACT_DCDC_CTRL_ON; 319 } else { 320 regoff = ACT_LDO_CTRL_OFFSET; 321 regmask = ACT_LDO_CTRL_ON; 322 } 323 324 iic_acquire_bus(sc->sc_i2c, 0); 325 if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0) 326 goto done; 327 val |= regmask; 328 error = act8846_write(sc, c->c_base + regoff, val); 329done: 330 iic_release_bus(sc->sc_i2c, 0); 331#ifdef ACT_DEBUG 332 if (error == 0) 333 act8846_print(c); 334#endif 335 336 return error; 337} 338 339int 340act8846_disable(struct act8846_ctrl *c) 341{ 342 struct act8846_softc *sc = device_private(c->c_dev); 343 uint8_t val, regoff, regmask; 344 int error; 345 346 if (c->c_type == ACT_CTRL_DCDC) { 347 regoff = ACT_DCDC_CTRL_OFFSET; 348 regmask = ACT_DCDC_CTRL_ON; 349 } else { 350 regoff = ACT_LDO_CTRL_OFFSET; 351 regmask = ACT_LDO_CTRL_ON; 352 } 353 354 iic_acquire_bus(sc->sc_i2c, 0); 355 if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0) 356 goto done; 357 val &= ~regmask; 358 error = act8846_write(sc, c->c_base + regoff, val); 359done: 360 iic_release_bus(sc->sc_i2c, 0); 361#ifdef ACT_DEBUG 362 if (error == 0) 363 act8846_print(c); 364#endif 365 366 return error; 367} 368