1 /*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 /* 27 * Silergy Corp. SY8106A buck regulator 28 */ 29 30 #include <sys/cdefs.h> 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/rman.h> 35 #include <sys/kernel.h> 36 #include <sys/reboot.h> 37 #include <sys/module.h> 38 39 #include <dev/iicbus/iicbus.h> 40 #include <dev/iicbus/iiconf.h> 41 42 #include <dev/ofw/ofw_bus.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 45 #include <dev/extres/regulator/regulator.h> 46 47 #include "iicbus_if.h" 48 #include "regdev_if.h" 49 50 #define VOUT1_SEL 0x01 51 #define SEL_GO (1 << 7) 52 #define SEL_VOLTAGE_MASK 0x7f 53 #define SEL_VOLTAGE_BASE 680000 /* uV */ 54 #define SEL_VOLTAGE_STEP 10000 /* uV */ 55 #define VOUT_COM 0x02 56 #define COM_DISABLE (1 << 0) 57 #define SYS_STATUS 0x06 58 59 static struct ofw_compat_data compat_data[] = { 60 { "silergy,sy8106a", 1 }, 61 { NULL, 0 } 62 }; 63 64 struct sy8106a_reg_sc { 65 struct regnode *regnode; 66 device_t base_dev; 67 phandle_t xref; 68 struct regnode_std_param *param; 69 }; 70 71 struct sy8106a_softc { 72 uint16_t addr; 73 74 /* Regulator */ 75 struct sy8106a_reg_sc *reg; 76 }; 77 78 static int 79 sy8106a_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) 80 { 81 struct sy8106a_softc *sc; 82 struct iic_msg msg[2]; 83 84 sc = device_get_softc(dev); 85 86 msg[0].slave = sc->addr; 87 msg[0].flags = IIC_M_WR; 88 msg[0].len = 1; 89 msg[0].buf = ® 90 91 msg[1].slave = sc->addr; 92 msg[1].flags = IIC_M_RD; 93 msg[1].len = size; 94 msg[1].buf = data; 95 96 return (iicbus_transfer(dev, msg, 2)); 97 } 98 99 static int 100 sy8106a_write(device_t dev, uint8_t reg, uint8_t val) 101 { 102 struct sy8106a_softc *sc; 103 struct iic_msg msg; 104 uint8_t buffer[2]; 105 106 sc = device_get_softc(dev); 107 108 buffer[0] = reg; 109 buffer[1] = val; 110 111 msg.slave = sc->addr; 112 msg.flags = IIC_M_WR; 113 msg.len = 2; 114 msg.buf = buffer; 115 116 return (iicbus_transfer(dev, &msg, 1)); 117 } 118 119 static int 120 sy8106a_regnode_init(struct regnode *regnode) 121 { 122 return (0); 123 } 124 125 static int 126 sy8106a_regnode_enable(struct regnode *regnode, bool enable, int *udelay) 127 { 128 struct sy8106a_reg_sc *sc; 129 uint8_t val; 130 131 sc = regnode_get_softc(regnode); 132 133 sy8106a_read(sc->base_dev, VOUT_COM, &val, 1); 134 if (enable) 135 val &= ~COM_DISABLE; 136 else 137 val |= COM_DISABLE; 138 sy8106a_write(sc->base_dev, VOUT_COM, val); 139 140 *udelay = sc->param->ramp_delay; 141 142 return (0); 143 } 144 145 static int 146 sy8106a_regnode_set_voltage(struct regnode *regnode, int min_uvolt, 147 int max_uvolt, int *udelay) 148 { 149 struct sy8106a_reg_sc *sc; 150 int cur_uvolt; 151 uint8_t val, oval; 152 153 sc = regnode_get_softc(regnode); 154 155 /* Get current voltage */ 156 sy8106a_read(sc->base_dev, VOUT1_SEL, &oval, 1); 157 cur_uvolt = (oval & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + 158 SEL_VOLTAGE_BASE; 159 160 /* Set new voltage */ 161 val = SEL_GO | ((min_uvolt - SEL_VOLTAGE_BASE) / SEL_VOLTAGE_STEP); 162 sy8106a_write(sc->base_dev, VOUT1_SEL, val); 163 164 /* Time to delay is based on the number of voltage steps */ 165 *udelay = sc->param->ramp_delay * 166 (abs(cur_uvolt - min_uvolt) / SEL_VOLTAGE_STEP); 167 168 return (0); 169 } 170 171 static int 172 sy8106a_regnode_get_voltage(struct regnode *regnode, int *uvolt) 173 { 174 struct sy8106a_reg_sc *sc; 175 uint8_t val; 176 177 sc = regnode_get_softc(regnode); 178 179 sy8106a_read(sc->base_dev, VOUT1_SEL, &val, 1); 180 *uvolt = (val & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + 181 SEL_VOLTAGE_BASE; 182 183 return (0); 184 } 185 186 static regnode_method_t sy8106a_regnode_methods[] = { 187 /* Regulator interface */ 188 REGNODEMETHOD(regnode_init, sy8106a_regnode_init), 189 REGNODEMETHOD(regnode_enable, sy8106a_regnode_enable), 190 REGNODEMETHOD(regnode_set_voltage, sy8106a_regnode_set_voltage), 191 REGNODEMETHOD(regnode_get_voltage, sy8106a_regnode_get_voltage), 192 REGNODEMETHOD_END 193 }; 194 DEFINE_CLASS_1(sy8106a_regnode, sy8106a_regnode_class, sy8106a_regnode_methods, 195 sizeof(struct sy8106a_reg_sc), regnode_class); 196 197 static struct sy8106a_reg_sc * 198 sy8106a_reg_attach(device_t dev, phandle_t node) 199 { 200 struct sy8106a_reg_sc *reg_sc; 201 struct regnode_init_def initdef; 202 struct regnode *regnode; 203 204 memset(&initdef, 0, sizeof(initdef)); 205 regulator_parse_ofw_stdparam(dev, node, &initdef); 206 initdef.id = 0; 207 initdef.ofw_node = node; 208 regnode = regnode_create(dev, &sy8106a_regnode_class, &initdef); 209 if (regnode == NULL) { 210 device_printf(dev, "cannot create regulator\n"); 211 return (NULL); 212 } 213 214 reg_sc = regnode_get_softc(regnode); 215 reg_sc->regnode = regnode; 216 reg_sc->base_dev = dev; 217 reg_sc->xref = OF_xref_from_node(node); 218 reg_sc->param = regnode_get_stdparam(regnode); 219 220 regnode_register(regnode); 221 222 return (reg_sc); 223 } 224 225 static int 226 sy8106a_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, 227 intptr_t *num) 228 { 229 struct sy8106a_softc *sc; 230 231 sc = device_get_softc(dev); 232 233 if (sc->reg->xref != xref) 234 return (ENXIO); 235 236 *num = 0; 237 238 return (0); 239 } 240 241 static int 242 sy8106a_probe(device_t dev) 243 { 244 if (!ofw_bus_status_okay(dev)) 245 return (ENXIO); 246 247 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 248 return (ENXIO); 249 250 device_set_desc(dev, "Silergy SY8106A regulator"); 251 252 return (BUS_PROBE_DEFAULT); 253 } 254 255 static int 256 sy8106a_attach(device_t dev) 257 { 258 struct sy8106a_softc *sc; 259 phandle_t node; 260 261 sc = device_get_softc(dev); 262 node = ofw_bus_get_node(dev); 263 264 sc->addr = iicbus_get_addr(dev); 265 266 sc->reg = sy8106a_reg_attach(dev, node); 267 if (sc->reg == NULL) { 268 device_printf(dev, "cannot attach regulator\n"); 269 return (ENXIO); 270 } 271 272 return (0); 273 } 274 275 static device_method_t sy8106a_methods[] = { 276 /* Device interface */ 277 DEVMETHOD(device_probe, sy8106a_probe), 278 DEVMETHOD(device_attach, sy8106a_attach), 279 280 /* Regdev interface */ 281 DEVMETHOD(regdev_map, sy8106a_regdev_map), 282 283 DEVMETHOD_END 284 }; 285 286 static driver_t sy8106a_driver = { 287 "sy8106a", 288 sy8106a_methods, 289 sizeof(struct sy8106a_softc), 290 }; 291 292 EARLY_DRIVER_MODULE(sy8106a, iicbus, sy8106a_driver, 0, 0, BUS_PASS_RESOURCE); 293 MODULE_VERSION(sy8106a, 1); 294 MODULE_DEPEND(sy8106a, iicbus, 1, 1, 1); 295 IICBUS_FDT_PNP_INFO(compat_data); 296