1 /* $OpenBSD: bd718x7.c,v 1.3 2020/11/12 10:47:07 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 78 return (strcmp(ia->ia_name, "rohm,bd71837") == 0 || 79 strcmp(ia->ia_name, "rohm,bd71847") == 0); 80 } 81 82 void 83 bdpmic_attach(struct device *parent, struct device *self, void *aux) 84 { 85 struct bdpmic_softc *sc = (struct bdpmic_softc *)self; 86 struct i2c_attach_args *ia = aux; 87 int node = *(int *)ia->ia_cookie; 88 const char *chip; 89 90 sc->sc_tag = ia->ia_tag; 91 sc->sc_addr = ia->ia_addr; 92 93 if (OF_is_compatible(node, "rohm,bd71837")) { 94 chip = "BD71837"; 95 sc->sc_regdata = bd71837_regdata; 96 } else { 97 chip = "BD71847"; 98 sc->sc_regdata = bd71847_regdata; 99 } 100 printf(": %s\n", chip); 101 102 node = OF_getnodebyname(node, "regulators"); 103 if (node == 0) 104 return; 105 for (node = OF_child(node); node; node = OF_peer(node)) 106 bdpmic_attach_regulator(sc, node); 107 } 108 109 struct bdpmic_regulator { 110 struct bdpmic_softc *bd_sc; 111 112 uint8_t bd_reg, bd_mask; 113 uint32_t bd_base, bd_delta; 114 115 struct regulator_device bd_rd; 116 }; 117 118 uint32_t bdpmic_get_voltage(void *); 119 int bdpmic_set_voltage(void *, uint32_t); 120 121 void 122 bdpmic_attach_regulator(struct bdpmic_softc *sc, int node) 123 { 124 struct bdpmic_regulator *bd; 125 char name[32]; 126 int i; 127 128 name[0] = 0; 129 OF_getprop(node, "name", name, sizeof(name)); 130 name[sizeof(name) - 1] = 0; 131 for (i = 0; sc->sc_regdata[i].name; i++) { 132 if (strcmp(sc->sc_regdata[i].name, name) == 0) 133 break; 134 } 135 if (sc->sc_regdata[i].name == NULL) 136 return; 137 138 bd = malloc(sizeof(*bd), M_DEVBUF, M_WAITOK | M_ZERO); 139 bd->bd_sc = sc; 140 141 bd->bd_reg = sc->sc_regdata[i].reg; 142 bd->bd_mask = sc->sc_regdata[i].mask; 143 bd->bd_base = sc->sc_regdata[i].base; 144 bd->bd_delta = sc->sc_regdata[i].delta; 145 146 bd->bd_rd.rd_node = node; 147 bd->bd_rd.rd_cookie = bd; 148 bd->bd_rd.rd_get_voltage = bdpmic_get_voltage; 149 bd->bd_rd.rd_set_voltage = bdpmic_set_voltage; 150 regulator_register(&bd->bd_rd); 151 } 152 153 uint32_t 154 bdpmic_get_voltage(void *cookie) 155 { 156 struct bdpmic_regulator *bd = cookie; 157 uint8_t vsel; 158 159 vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg); 160 return bd->bd_base + (vsel & bd->bd_mask) * bd->bd_delta; 161 } 162 163 int 164 bdpmic_set_voltage(void *cookie, uint32_t voltage) 165 { 166 struct bdpmic_regulator *bd = cookie; 167 uint32_t vmin = bd->bd_base; 168 uint32_t vmax = vmin + bd->bd_mask * bd->bd_delta; 169 uint8_t vsel; 170 171 if (voltage < vmin || voltage > vmax) 172 return EINVAL; 173 174 /* Unlock */ 175 bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK, 176 BDPMIC_REGLOCK_PWRSEQ); 177 178 vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg); 179 vsel &= ~bd->bd_mask; 180 vsel |= (voltage - bd->bd_base) / bd->bd_delta; 181 bdpmic_reg_write(bd->bd_sc, bd->bd_reg, vsel); 182 183 /* Lock */ 184 bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK, 185 BDPMIC_REGLOCK_PWRSEQ | BDPMIC_REGLOCK_VREG); 186 187 return 0; 188 } 189 190 uint8_t 191 bdpmic_reg_read(struct bdpmic_softc *sc, int reg) 192 { 193 uint8_t cmd = reg; 194 uint8_t val; 195 int error; 196 197 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 198 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 199 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 200 iic_release_bus(sc->sc_tag, I2C_F_POLL); 201 202 if (error) { 203 printf("%s: can't read register 0x%02x\n", 204 sc->sc_dev.dv_xname, reg); 205 val = 0xff; 206 } 207 208 return val; 209 } 210 211 void 212 bdpmic_reg_write(struct bdpmic_softc *sc, int reg, uint8_t val) 213 { 214 uint8_t cmd = reg; 215 int error; 216 217 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 218 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 219 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 220 iic_release_bus(sc->sc_tag, I2C_F_POLL); 221 222 if (error) { 223 printf("%s: can't write register 0x%02x\n", 224 sc->sc_dev.dv_xname, reg); 225 } 226 } 227