1 /* $OpenBSD: sypwr.c,v 1.5 2021/10/24 17:52:27 mpi 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/ofw/openfirm.h> 24 #include <dev/ofw/ofw_regulator.h> 25 #include <dev/ofw/fdt.h> 26 27 #include <dev/i2c/i2cvar.h> 28 29 #define SY8106A_VOUT1_SEL 0x01 30 #define SY8106A_VOUT1_SEL_I2C (1 << 7) 31 #define SY8106A_VOUT1_SEL_MASK 0x7f 32 33 struct sypwr_softc { 34 struct device sc_dev; 35 i2c_tag_t sc_tag; 36 i2c_addr_t sc_addr; 37 38 uint32_t sc_fixed_microvolt; 39 40 struct regulator_device sc_rd; 41 }; 42 43 int sypwr_match(struct device *, void *, void *); 44 void sypwr_attach(struct device *, struct device *, void *); 45 int sypwr_activate(struct device *, int); 46 47 const struct cfattach sypwr_ca = { 48 sizeof(struct sypwr_softc), sypwr_match, sypwr_attach, 49 NULL, sypwr_activate 50 }; 51 52 struct cfdriver sypwr_cd = { 53 NULL, "sypwr", DV_DULL 54 }; 55 56 uint8_t sypwr_read(struct sypwr_softc *, int); 57 void sypwr_write(struct sypwr_softc *, int, uint8_t); 58 uint32_t sypwr_get_voltage(void *); 59 int sypwr_set_voltage(void *, uint32_t); 60 61 int 62 sypwr_match(struct device *parent, void *match, void *aux) 63 { 64 struct i2c_attach_args *ia = aux; 65 66 return (strcmp(ia->ia_name, "silergy,sy8106a") == 0); 67 } 68 69 void 70 sypwr_attach(struct device *parent, struct device *self, void *aux) 71 { 72 struct sypwr_softc *sc = (struct sypwr_softc *)self; 73 struct i2c_attach_args *ia = aux; 74 int node = *(int *)ia->ia_cookie; 75 uint8_t reg; 76 77 sc->sc_tag = ia->ia_tag; 78 sc->sc_addr = ia->ia_addr; 79 80 sc->sc_fixed_microvolt = 81 OF_getpropint(node, "silergy,fixed-microvolt", 0); 82 83 /* 84 * Only register the regulator if it is under I2C control 85 * (i.e. initialized by the firmware) or if the device tree 86 * specifies its fixed voltage. Otherwise we have no idea 87 * what the current output voltage is, which will confuse the 88 * regulator framework. 89 */ 90 reg = sypwr_read(sc, SY8106A_VOUT1_SEL); 91 if (reg & SY8106A_VOUT1_SEL_I2C || sc->sc_fixed_microvolt != 0) { 92 uint32_t voltage; 93 94 voltage = sypwr_get_voltage(sc); 95 printf(": %d.%02d VDC", voltage / 1000000, 96 (voltage % 1000000) / 10000); 97 98 sc->sc_rd.rd_node = node; 99 sc->sc_rd.rd_cookie = sc; 100 sc->sc_rd.rd_get_voltage = sypwr_get_voltage; 101 sc->sc_rd.rd_set_voltage = sypwr_set_voltage; 102 regulator_register(&sc->sc_rd); 103 } 104 105 printf("\n"); 106 } 107 108 int 109 sypwr_activate(struct device *self, int act) 110 { 111 struct sypwr_softc *sc = (struct sypwr_softc *)self; 112 uint8_t reg; 113 114 switch (act) { 115 case DVACT_POWERDOWN: 116 /* 117 * Restore fixed voltage otherwise we might hang after 118 * a warm reset. 119 */ 120 if (sc->sc_fixed_microvolt != 0) { 121 reg = sypwr_read(sc, SY8106A_VOUT1_SEL); 122 reg &= ~SY8106A_VOUT1_SEL_I2C; 123 sypwr_write(sc, SY8106A_VOUT1_SEL, reg); 124 } 125 break; 126 } 127 128 return 0; 129 } 130 131 uint8_t 132 sypwr_read(struct sypwr_softc *sc, int reg) 133 { 134 uint8_t cmd = reg; 135 uint8_t val; 136 int error; 137 138 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 139 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 140 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 141 iic_release_bus(sc->sc_tag, I2C_F_POLL); 142 143 if (error) { 144 printf("error %d\n", error); 145 printf("%s: can't read register 0x%02x\n", 146 sc->sc_dev.dv_xname, reg); 147 val = 0xff; 148 } 149 150 return val; 151 } 152 153 void 154 sypwr_write(struct sypwr_softc *sc, int reg, uint8_t val) 155 { 156 uint8_t cmd = reg; 157 int error; 158 159 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 160 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 161 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 162 iic_release_bus(sc->sc_tag, I2C_F_POLL); 163 164 if (error) { 165 printf("%s: can't write register 0x%02x\n", 166 sc->sc_dev.dv_xname, reg); 167 } 168 } 169 170 uint32_t 171 sypwr_get_voltage(void *cookie) 172 { 173 struct sypwr_softc *sc = cookie; 174 uint8_t value; 175 176 value = sypwr_read(sc, SY8106A_VOUT1_SEL); 177 if (value & SY8106A_VOUT1_SEL_I2C) 178 return 680000 + (value & SY8106A_VOUT1_SEL_MASK) * 10000; 179 else 180 return sc->sc_fixed_microvolt; 181 } 182 183 int 184 sypwr_set_voltage(void *cookie, uint32_t voltage) 185 { 186 struct sypwr_softc *sc = cookie; 187 uint8_t value; 188 189 if (voltage < 680000 || voltage > 1950000) 190 return EINVAL; 191 192 value = (voltage - 680000) / 10000; 193 sypwr_write(sc, SY8106A_VOUT1_SEL, value | SY8106A_VOUT1_SEL_I2C); 194 return 0; 195 } 196