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