axppmic.c revision 1.1
1/* $OpenBSD: axppmic.c,v 1.1 2017/12/17 18:25:25 kettenis Exp $ */ 2/* 3 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/device.h> 21#include <sys/malloc.h> 22 23#include <dev/fdt/rsbvar.h> 24 25#include <dev/ofw/openfirm.h> 26#include <dev/ofw/ofw_regulator.h> 27#include <dev/ofw/fdt.h> 28 29#define AXP806_REG_ADDR_EXT 0xff 30#define AXP806_REG_ADDR_EXT_MASTER_MODE (0 << 4) 31#define AXP806_REG_ADDR_EXT_SLAVE_MODE (1 << 4) 32 33struct axppmic_regdata { 34 const char *name; 35 uint8_t ereg, emask, eval, dval; 36 uint8_t vreg, vmask; 37 uint32_t base, delta; 38 uint32_t base2, delta2; 39}; 40 41struct axppmic_regdata axp806_regdata[] = { 42 { "dcdca", 0x10, (1 << 0), (1 << 0), (0 << 0), 43 0x12, 0x7f, 600000, 10000, 1120000, 20000 }, 44 { "dcdcb", 0x10, (1 << 1), (1 << 1), (0 << 1), 45 0x13, 0x1f, 1000000, 50000 }, 46 { "dcdcc", 0x10, (1 << 2), (1 << 2), (0 << 2), 47 0x14, 0x7f, 600000, 10000, 1120000, 20000 }, 48 { "dcdcd", 0x10, (1 << 3), (1 << 3), (0 << 3), 49 0x15, 0x3f, 600000, 20000, 1600000, 100000 }, 50 { "dcdce", 0x10, (1 << 4), (1 << 4), (0 << 4), 51 0x16, 0x1f, 1100000, 100000 }, 52 { "aldo1", 0x10, (1 << 5), (1 << 5), (0 << 5), 53 0x17, 0x1f, 700000, 100000 }, 54 { "aldo2", 0x10, (1 << 6), (1 << 6), (0 << 6), 55 0x18, 0x1f, 700000, 100000 }, 56 { "aldo3", 0x10, (1 << 7), (1 << 7), (0 << 7), 57 0x19, 0x1f, 700000, 100000 }, 58 { "bldo1", 0x11, (1 << 0), (1 << 0), (0 << 0), 59 0x20, 0x0f, 700000, 100000 }, 60 { "bldo2", 0x11, (1 << 1), (1 << 1), (0 << 1), 61 0x21, 0x0f, 700000, 100000 }, 62 { "bldo3", 0x11, (1 << 2), (1 << 2), (0 << 2), 63 0x22, 0x0f, 700000, 100000 }, 64 { "bldo4", 0x11, (1 << 3), (1 << 3), (0 << 3), 65 0x23, 0x0f, 700000, 100000 }, 66 { "cldo1", 0x11, (1 << 4), (1 << 4), (0 << 4), 67 0x24, 0x1f, 700000, 100000 }, 68 { "cldo2", 0x11, (1 << 5), (1 << 5), (0 << 5), 69 0x25, 0x1f, 700000, 100000, 3600000, 200000 }, 70 { "cldo3", 0x11, (1 << 6), (1 << 6), (0 << 6), 71 0x26, 0x1f, 700000, 100000 }, 72 { "sw", 0x11, (1 << 7), (1 << 7), (0 << 7) }, 73 { NULL } 74}; 75 76struct axppmic_regdata axp809_regdata[] = { 77 { "dcdc1", 0x10, (1 << 1), (1 << 1), (0 << 1), 78 0x21, 0x1f, 1600000, 100000 }, 79 { "dcdc2", 0x10, (1 << 2), (1 << 2), (0 << 2), 80 0x22, 0x3f, 600000, 20000 }, 81 { "dcdc3", 0x10, (1 << 3), (1 << 3), (0 << 3), 82 0x23, 0x3f, 600000, 20000 }, 83 { "dcdc4", 0x10, (1 << 4), (1 << 4), (0 << 4), 84 0x24, 0x3f, 600000, 20000, 1800000, 100000 }, 85 { "dcdc5", 0x10, (1 << 5), (1 << 5), (0 << 5), 86 0x25, 0x1f, 1000000, 50000 }, 87 { "dc5ldo", 0x10, (1 << 0), (1 << 0), (0 << 0), 88 0x1c, 0x07, 700000, 100000 }, 89 { "aldo1", 0x10, (1 << 6), (1 << 6), (0 << 6), 90 0x28, 0x1f, 700000, 100000 }, 91 { "aldo2", 0x10, (1 << 7), (1 << 7), (0 << 7), 92 0x28, 0x1f, 700000, 100000 }, 93 { "aldo3", 0x12, (1 << 5), (1 << 5), (0 << 5), 94 0x28, 0x1f, 700000, 100000 }, 95 { "dldo1", 0x12, (1 << 3), (1 << 3), (0 << 3), 96 0x15, 0x1f, 700000, 100000 }, 97 { "dldo2", 0x12, (1 << 4), (1 << 4), (0 << 4), 98 0x16, 0x1f, 700000, 100000 }, 99 { "eldo1", 0x12, (1 << 0), (1 << 0), (0 << 0), 100 0x19, 0x1f, 700000, 100000 }, 101 { "eldo2", 0x12, (1 << 1), (1 << 1), (0 << 1), 102 0x1a, 0x1f, 700000, 100000 }, 103 { "eldo3", 0x12, (1 << 2), (1 << 2), (0 << 2), 104 0x19, 0x1f, 700000, 100000 }, 105 { "ldo_io0", 0x90, 0x07, 0x03, 0x04, 106 0x91, 0x1f, 700000, 100000 }, 107 { "ldo_io1", 0x92, 0x07, 0x03, 0x04, 108 0x93, 0x1f, 700000, 100000 }, 109 { NULL } 110}; 111 112struct axppmic_device { 113 const char *name; 114 const char *chip; 115 struct axppmic_regdata *regdata; 116}; 117 118struct axppmic_device axppmic_devices[] = { 119 { "x-powers,axp806", "AXP806", axp806_regdata }, 120 { "x-powers,axp809", "AXP809", axp809_regdata } 121}; 122 123const struct axppmic_device * 124axppmic_lookup(const char *name) 125{ 126 int i; 127 128 for (i = 0; i < nitems(axppmic_devices); i++) { 129 if (strcmp(name, axppmic_devices[i].name) == 0) 130 return &axppmic_devices[i]; 131 } 132 133 return NULL; 134} 135 136struct axppmic_softc { 137 struct device sc_dev; 138 void *sc_cookie; 139 uint16_t sc_rta; 140 141 struct axppmic_regdata *sc_regdata; 142}; 143 144inline uint8_t 145axppmic_read_reg(struct axppmic_softc *sc, uint8_t reg) 146{ 147 return rsb_read_1(sc->sc_cookie, sc->sc_rta, reg); 148} 149 150inline void 151axppmic_write_reg(struct axppmic_softc *sc, uint8_t reg, uint8_t value) 152{ 153 rsb_write_1(sc->sc_cookie, sc->sc_rta, reg, value); 154} 155 156int axppmic_match(struct device *, void *, void *); 157void axppmic_attach(struct device *, struct device *, void *); 158 159struct cfattach axppmic_rsb_ca = { 160 sizeof(struct axppmic_softc), axppmic_match, axppmic_attach 161}; 162 163struct cfdriver axppmic_rsb_cd = { 164 NULL, "axppmic", DV_DULL 165}; 166 167void axppmic_attach_regulator(struct axppmic_softc *, int); 168 169int 170axppmic_match(struct device *parent, void *match, void *aux) 171{ 172 struct rsb_attach_args *ra = aux; 173 174 if (axppmic_lookup(ra->ra_name)) 175 return 1; 176 return 0; 177} 178 179void 180axppmic_attach(struct device *parent, struct device *self, void *aux) 181{ 182 struct axppmic_softc *sc = (struct axppmic_softc *)self; 183 const struct axppmic_device *device; 184 struct rsb_attach_args *ra = aux; 185 int node; 186 187 sc->sc_cookie = ra->ra_cookie; 188 sc->sc_rta = ra->ra_rta; 189 190 device = axppmic_lookup(ra->ra_name); 191 printf(": %s\n", device->chip); 192 193 sc->sc_regdata = device->regdata; 194 195 /* Switch AXP806 into master or slave mode. */ 196 if (strcmp(ra->ra_name, "x-powers,axp806") == 0) { 197 if (OF_getproplen(ra->ra_node, "x-powers,master-mode") == 0) { 198 axppmic_write_reg(sc, AXP806_REG_ADDR_EXT, 199 AXP806_REG_ADDR_EXT_MASTER_MODE); 200 } else { 201 axppmic_write_reg(sc, AXP806_REG_ADDR_EXT, 202 AXP806_REG_ADDR_EXT_SLAVE_MODE); 203 } 204 } 205 206 node = OF_getnodebyname(ra->ra_node, "regulators"); 207 if (node == 0) 208 return; 209 for (node = OF_child(node); node; node = OF_peer(node)) 210 axppmic_attach_regulator(sc, node); 211} 212 213struct axppmic_regulator { 214 struct axppmic_softc *ar_sc; 215 216 uint8_t ar_ereg, ar_emask; 217 uint8_t ar_eval, ar_dval; 218 219 uint8_t ar_vreg, ar_vmask; 220 uint32_t ar_base, ar_delta; 221 uint32_t ar_base2, ar_delta2; 222 223 struct regulator_device ar_rd; 224}; 225 226uint32_t axppmic_get_voltage(void *); 227int axppmic_set_voltage(void *, uint32_t); 228int axppmic_enable(void *, int); 229 230void 231axppmic_attach_regulator(struct axppmic_softc *sc, int node) 232{ 233 struct axppmic_regulator *ar; 234 char name[32]; 235 int i; 236 237 name[0] = 0; 238 OF_getprop(node, "name", name, sizeof(name)); 239 name[sizeof(name) - 1] = 0; 240 for (i = 0; sc->sc_regdata[i].name; i++) { 241 if (strcmp(sc->sc_regdata[i].name, name) == 0) 242 break; 243 } 244 if (sc->sc_regdata[i].name == NULL) 245 return; 246 247 ar = malloc(sizeof(*ar), M_DEVBUF, M_WAITOK | M_ZERO); 248 ar->ar_sc = sc; 249 250 ar->ar_ereg = sc->sc_regdata[i].ereg; 251 ar->ar_emask = sc->sc_regdata[i].emask; 252 ar->ar_eval = sc->sc_regdata[i].eval; 253 ar->ar_dval = sc->sc_regdata[i].dval; 254 ar->ar_vreg = sc->sc_regdata[i].vreg; 255 ar->ar_vmask = sc->sc_regdata[i].vmask; 256 ar->ar_base = sc->sc_regdata[i].base; 257 ar->ar_delta = sc->sc_regdata[i].delta; 258 259 ar->ar_rd.rd_node = node; 260 ar->ar_rd.rd_cookie = ar; 261 ar->ar_rd.rd_get_voltage = axppmic_get_voltage; 262 ar->ar_rd.rd_set_voltage = axppmic_set_voltage; 263 ar->ar_rd.rd_enable = axppmic_enable; 264 regulator_register(&ar->ar_rd); 265} 266 267uint32_t 268axppmic_get_voltage(void *cookie) 269{ 270 struct axppmic_regulator *ar = cookie; 271 uint32_t voltage; 272 uint8_t value; 273 274 value = axppmic_read_reg(ar->ar_sc, ar->ar_vreg); 275 value &= ar->ar_vmask; 276 voltage = ar->ar_base + value * ar->ar_delta; 277 if (ar->ar_base2 > 0 && voltage > ar->ar_base2) { 278 value -= (ar->ar_base2 - ar->ar_base) / ar->ar_delta; 279 voltage = ar->ar_base2 + value * ar->ar_delta2; 280 } 281 return voltage; 282} 283 284int 285axppmic_set_voltage(void *cookie, uint32_t voltage) 286{ 287 struct axppmic_regulator *ar = cookie; 288 uint32_t value, reg; 289 290 if (voltage < ar->ar_base) 291 return EINVAL; 292 value = (voltage - ar->ar_base) / ar->ar_delta; 293 if (ar->ar_base2 > 0 && voltage > ar->ar_base2) { 294 value = (ar->ar_base2 - ar->ar_base) / ar->ar_delta; 295 value += (voltage - ar->ar_base2) / ar->ar_delta2; 296 } 297 if (value > ar->ar_vmask) 298 return EINVAL; 299 300 reg = axppmic_read_reg(ar->ar_sc, ar->ar_vreg); 301 reg &= ar->ar_vmask; 302 axppmic_write_reg(ar->ar_sc, ar->ar_vreg, reg | value); 303 return 0; 304} 305 306int 307axppmic_enable(void *cookie, int on) 308{ 309 struct axppmic_regulator *ar = cookie; 310 uint8_t reg; 311 312 reg = axppmic_read_reg(ar->ar_sc, ar->ar_ereg); 313 reg &= ~ar->ar_emask; 314 if (on) 315 reg |= ar->ar_eval; 316 else 317 reg |= ar->ar_dval; 318 axppmic_write_reg(ar->ar_sc, ar->ar_ereg, reg); 319 return 0; 320} 321