1 /* $OpenBSD: bd718x7.c,v 1.2 2020/03/27 12:23:52 patrick Exp $ */ 2 /* 3 * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se> 4 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <dev/ofw/openfirm.h> 25 #include <dev/ofw/ofw_regulator.h> 26 #include <dev/ofw/fdt.h> 27 28 #include <dev/i2c/i2cvar.h> 29 30 #define BDPMIC_REGLOCK 0x2f 31 #define BDPMIC_REGLOCK_PWRSEQ (1 << 0) 32 #define BDPMIC_REGLOCK_VREG (1 << 4) 33 34 struct bdpmic_regdata { 35 const char *name; 36 uint8_t reg, mask; 37 uint32_t base, delta; 38 }; 39 40 struct bdpmic_regdata bd71837_regdata[] = { 41 { "BUCK2", 0x10, 0x3f, 700000, 10000 }, 42 { } 43 }; 44 45 struct bdpmic_regdata bd71847_regdata[] = { 46 { "BUCK2", 0x10, 0x3f, 700000, 10000 }, 47 { } 48 }; 49 50 struct bdpmic_softc { 51 struct device sc_dev; 52 i2c_tag_t sc_tag; 53 i2c_addr_t sc_addr; 54 55 struct bdpmic_regdata *sc_regdata; 56 }; 57 58 int bdpmic_match(struct device *, void *, void *); 59 void bdpmic_attach(struct device *, struct device *, void *); 60 61 void bdpmic_attach_regulator(struct bdpmic_softc *, int); 62 uint8_t bdpmic_reg_read(struct bdpmic_softc *, int); 63 void bdpmic_reg_write(struct bdpmic_softc *, int, uint8_t); 64 65 struct cfattach bdpmic_ca = { 66 sizeof(struct bdpmic_softc), bdpmic_match, bdpmic_attach 67 }; 68 69 struct cfdriver bdpmic_cd = { 70 NULL, "bdpmic", DV_DULL 71 }; 72 73 int 74 bdpmic_match(struct device *parent, void *match, void *aux) 75 { 76 struct i2c_attach_args *ia = aux; 77 int node = *(int *)ia->ia_cookie; 78 79 return (OF_is_compatible(node, "rohm,bd71837") || 80 OF_is_compatible(node, "rohm,bd71847")); 81 } 82 83 void 84 bdpmic_attach(struct device *parent, struct device *self, void *aux) 85 { 86 struct bdpmic_softc *sc = (struct bdpmic_softc *)self; 87 struct i2c_attach_args *ia = aux; 88 int node = *(int *)ia->ia_cookie; 89 const char *chip; 90 91 sc->sc_tag = ia->ia_tag; 92 sc->sc_addr = ia->ia_addr; 93 94 if (OF_is_compatible(node, "rohm,bd71837")) { 95 chip = "BD71837"; 96 sc->sc_regdata = bd71837_regdata; 97 } else { 98 chip = "BD71847"; 99 sc->sc_regdata = bd71847_regdata; 100 } 101 printf(": %s\n", chip); 102 103 node = OF_getnodebyname(node, "regulators"); 104 if (node == 0) 105 return; 106 for (node = OF_child(node); node; node = OF_peer(node)) 107 bdpmic_attach_regulator(sc, node); 108 } 109 110 struct bdpmic_regulator { 111 struct bdpmic_softc *bd_sc; 112 113 uint8_t bd_reg, bd_mask; 114 uint32_t bd_base, bd_delta; 115 116 struct regulator_device bd_rd; 117 }; 118 119 uint32_t bdpmic_get_voltage(void *); 120 int bdpmic_set_voltage(void *, uint32_t); 121 122 void 123 bdpmic_attach_regulator(struct bdpmic_softc *sc, int node) 124 { 125 struct bdpmic_regulator *bd; 126 char name[32]; 127 int i; 128 129 name[0] = 0; 130 OF_getprop(node, "name", name, sizeof(name)); 131 name[sizeof(name) - 1] = 0; 132 for (i = 0; sc->sc_regdata[i].name; i++) { 133 if (strcmp(sc->sc_regdata[i].name, name) == 0) 134 break; 135 } 136 if (sc->sc_regdata[i].name == NULL) 137 return; 138 139 bd = malloc(sizeof(*bd), M_DEVBUF, M_WAITOK | M_ZERO); 140 bd->bd_sc = sc; 141 142 bd->bd_reg = sc->sc_regdata[i].reg; 143 bd->bd_mask = sc->sc_regdata[i].mask; 144 bd->bd_base = sc->sc_regdata[i].base; 145 bd->bd_delta = sc->sc_regdata[i].delta; 146 147 bd->bd_rd.rd_node = node; 148 bd->bd_rd.rd_cookie = bd; 149 bd->bd_rd.rd_get_voltage = bdpmic_get_voltage; 150 bd->bd_rd.rd_set_voltage = bdpmic_set_voltage; 151 regulator_register(&bd->bd_rd); 152 } 153 154 uint32_t 155 bdpmic_get_voltage(void *cookie) 156 { 157 struct bdpmic_regulator *bd = cookie; 158 uint8_t vsel; 159 160 vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg); 161 return bd->bd_base + (vsel & bd->bd_mask) * bd->bd_delta; 162 } 163 164 int 165 bdpmic_set_voltage(void *cookie, uint32_t voltage) 166 { 167 struct bdpmic_regulator *bd = cookie; 168 uint32_t vmin = bd->bd_base; 169 uint32_t vmax = vmin + bd->bd_mask * bd->bd_delta; 170 uint8_t vsel; 171 172 if (voltage < vmin || voltage > vmax) 173 return EINVAL; 174 175 /* Unlock */ 176 bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK, 177 BDPMIC_REGLOCK_PWRSEQ); 178 179 vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg); 180 vsel &= ~bd->bd_mask; 181 vsel |= (voltage - bd->bd_base) / bd->bd_delta; 182 bdpmic_reg_write(bd->bd_sc, bd->bd_reg, vsel); 183 184 /* Lock */ 185 bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK, 186 BDPMIC_REGLOCK_PWRSEQ | BDPMIC_REGLOCK_VREG); 187 188 return 0; 189 } 190 191 uint8_t 192 bdpmic_reg_read(struct bdpmic_softc *sc, int reg) 193 { 194 uint8_t cmd = reg; 195 uint8_t val; 196 int error; 197 198 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 199 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 200 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 201 iic_release_bus(sc->sc_tag, I2C_F_POLL); 202 203 if (error) { 204 printf("%s: can't read register 0x%02x\n", 205 sc->sc_dev.dv_xname, reg); 206 val = 0xff; 207 } 208 209 return val; 210 } 211 212 void 213 bdpmic_reg_write(struct bdpmic_softc *sc, int reg, uint8_t val) 214 { 215 uint8_t cmd = reg; 216 int error; 217 218 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 219 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 220 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 221 iic_release_bus(sc->sc_tag, I2C_F_POLL); 222 223 if (error) { 224 printf("%s: can't write register 0x%02x\n", 225 sc->sc_dev.dv_xname, reg); 226 } 227 } 228