1 /* $OpenBSD: sypwr.c,v 1.1 2017/12/31 11:13:59 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/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 struct regulator_device sc_rd; 39 }; 40 41 int sypwr_match(struct device *, void *, void *); 42 void sypwr_attach(struct device *, struct device *, void *); 43 44 struct cfattach sypwr_ca = { 45 sizeof(struct sypwr_softc), sypwr_match, sypwr_attach 46 }; 47 48 struct cfdriver sypwr_cd = { 49 NULL, "sypwr", DV_DULL 50 }; 51 52 uint8_t sypwr_read(struct sypwr_softc *, int); 53 void sypwr_write(struct sypwr_softc *, int, uint8_t); 54 uint32_t sypwr_get_voltage(void *); 55 int sypwr_set_voltage(void *, uint32_t voltage); 56 57 int 58 sypwr_match(struct device *parent, void *match, void *aux) 59 { 60 struct i2c_attach_args *ia = aux; 61 int node = *(int *)ia->ia_cookie; 62 63 return (OF_is_compatible(node, "silergy,sy8106a")); 64 } 65 66 void 67 sypwr_attach(struct device *parent, struct device *self, void *aux) 68 { 69 struct sypwr_softc *sc = (struct sypwr_softc *)self; 70 struct i2c_attach_args *ia = aux; 71 int node = *(int *)ia->ia_cookie; 72 uint8_t reg; 73 74 sc->sc_tag = ia->ia_tag; 75 sc->sc_addr = ia->ia_addr; 76 77 /* 78 * Only register the regulator if it is under I2C control 79 * (i.e. initialized by the firmware). Otherwise we have no 80 * idea what the current output voltage is, which will confuse 81 * the regulator framework. 82 */ 83 reg = sypwr_read(sc, SY8106A_VOUT1_SEL); 84 if (reg & SY8106A_VOUT1_SEL_I2C) { 85 uint32_t voltage; 86 87 voltage = sypwr_get_voltage(sc); 88 printf(": %d.%02d VDC", voltage / 1000000, 89 (voltage % 1000000) / 10000); 90 91 sc->sc_rd.rd_node = node; 92 sc->sc_rd.rd_cookie = sc; 93 sc->sc_rd.rd_get_voltage = sypwr_get_voltage; 94 sc->sc_rd.rd_set_voltage = sypwr_set_voltage; 95 regulator_register(&sc->sc_rd); 96 } 97 98 printf("\n"); 99 } 100 101 uint8_t 102 sypwr_read(struct sypwr_softc *sc, int reg) 103 { 104 uint8_t cmd = reg; 105 uint8_t val; 106 int error; 107 108 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 109 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 110 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 111 iic_release_bus(sc->sc_tag, I2C_F_POLL); 112 113 if (error) { 114 printf("error %d\n", error); 115 printf("%s: can't read register 0x%02x\n", 116 sc->sc_dev.dv_xname, reg); 117 val = 0xff; 118 } 119 120 return val; 121 } 122 123 void 124 sypwr_write(struct sypwr_softc *sc, int reg, uint8_t val) 125 { 126 uint8_t cmd = reg; 127 int error; 128 129 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 130 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 131 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 132 iic_release_bus(sc->sc_tag, I2C_F_POLL); 133 134 if (error) { 135 printf("%s: can't write register 0x%02x\n", 136 sc->sc_dev.dv_xname, reg); 137 } 138 } 139 140 uint32_t 141 sypwr_get_voltage(void *cookie) 142 { 143 struct sypwr_softc *sc = cookie; 144 uint8_t value; 145 146 value = sypwr_read(sc, SY8106A_VOUT1_SEL); 147 return 680000 + (value & SY8106A_VOUT1_SEL_MASK) * 10000; 148 } 149 150 int 151 sypwr_set_voltage(void *cookie, uint32_t voltage) 152 { 153 struct sypwr_softc *sc = cookie; 154 uint8_t value; 155 156 if (voltage < 680000 || voltage > 1950000) 157 return EINVAL; 158 159 value = (voltage - 680000) / 10000; 160 sypwr_write(sc, SY8106A_VOUT1_SEL, value | SY8106A_VOUT1_SEL_I2C); 161 return 0; 162 } 163