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/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/rman.h> 34 #include <sys/kernel.h> 35 #include <sys/reboot.h> 36 #include <sys/module.h> 37 38 #include <dev/iicbus/iicbus.h> 39 #include <dev/iicbus/iiconf.h> 40 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <dev/extres/regulator/regulator.h> 45 46 #include "iicbus_if.h" 47 #include "regdev_if.h" 48 49 #define VOUT1_SEL 0x01 50 #define SEL_GO (1 << 7) 51 #define SEL_VOLTAGE_MASK 0x7f 52 #define SEL_VOLTAGE_BASE 680000 /* uV */ 53 #define SEL_VOLTAGE_STEP 10000 /* uV */ 54 #define VOUT_COM 0x02 55 #define COM_DISABLE (1 << 0) 56 #define SYS_STATUS 0x06 57 58 static struct ofw_compat_data compat_data[] = { 59 { "silergy,sy8106a", 1 }, 60 { NULL, 0 } 61 }; 62 63 struct sy8106a_reg_sc { 64 struct regnode *regnode; 65 device_t base_dev; 66 phandle_t xref; 67 struct regnode_std_param *param; 68 }; 69 70 struct sy8106a_softc { 71 uint16_t addr; 72 73 /* Regulator */ 74 struct sy8106a_reg_sc *reg; 75 }; 76 77 static int 78 sy8106a_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) 79 { 80 struct sy8106a_softc *sc; 81 struct iic_msg msg[2]; 82 83 sc = device_get_softc(dev); 84 85 msg[0].slave = sc->addr; 86 msg[0].flags = IIC_M_WR; 87 msg[0].len = 1; 88 msg[0].buf = ® 89 90 msg[1].slave = sc->addr; 91 msg[1].flags = IIC_M_RD; 92 msg[1].len = size; 93 msg[1].buf = data; 94 95 return (iicbus_transfer(dev, msg, 2)); 96 } 97 98 static int 99 sy8106a_write(device_t dev, uint8_t reg, uint8_t val) 100 { 101 struct sy8106a_softc *sc; 102 struct iic_msg msg; 103 uint8_t buffer[2]; 104 105 sc = device_get_softc(dev); 106 107 buffer[0] = reg; 108 buffer[1] = val; 109 110 msg.slave = sc->addr; 111 msg.flags = IIC_M_WR; 112 msg.len = 2; 113 msg.buf = buffer; 114 115 return (iicbus_transfer(dev, &msg, 1)); 116 } 117 118 static int 119 sy8106a_regnode_init(struct regnode *regnode) 120 { 121 return (0); 122 } 123 124 static int 125 sy8106a_regnode_enable(struct regnode *regnode, bool enable, int *udelay) 126 { 127 struct sy8106a_reg_sc *sc; 128 uint8_t val; 129 130 sc = regnode_get_softc(regnode); 131 132 sy8106a_read(sc->base_dev, VOUT_COM, &val, 1); 133 if (enable) 134 val &= ~COM_DISABLE; 135 else 136 val |= COM_DISABLE; 137 sy8106a_write(sc->base_dev, VOUT_COM, val); 138 139 *udelay = sc->param->ramp_delay; 140 141 return (0); 142 } 143 144 static int 145 sy8106a_regnode_set_voltage(struct regnode *regnode, int min_uvolt, 146 int max_uvolt, int *udelay) 147 { 148 struct sy8106a_reg_sc *sc; 149 int cur_uvolt; 150 uint8_t val, oval; 151 152 sc = regnode_get_softc(regnode); 153 154 /* Get current voltage */ 155 sy8106a_read(sc->base_dev, VOUT1_SEL, &oval, 1); 156 cur_uvolt = (oval & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + 157 SEL_VOLTAGE_BASE; 158 159 /* Set new voltage */ 160 val = SEL_GO | ((min_uvolt - SEL_VOLTAGE_BASE) / SEL_VOLTAGE_STEP); 161 sy8106a_write(sc->base_dev, VOUT1_SEL, val); 162 163 /* Time to delay is based on the number of voltage steps */ 164 *udelay = sc->param->ramp_delay * 165 (abs(cur_uvolt - min_uvolt) / SEL_VOLTAGE_STEP); 166 167 return (0); 168 } 169 170 static int 171 sy8106a_regnode_get_voltage(struct regnode *regnode, int *uvolt) 172 { 173 struct sy8106a_reg_sc *sc; 174 uint8_t val; 175 176 sc = regnode_get_softc(regnode); 177 178 sy8106a_read(sc->base_dev, VOUT1_SEL, &val, 1); 179 *uvolt = (val & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + 180 SEL_VOLTAGE_BASE; 181 182 return (0); 183 } 184 185 static regnode_method_t sy8106a_regnode_methods[] = { 186 /* Regulator interface */ 187 REGNODEMETHOD(regnode_init, sy8106a_regnode_init), 188 REGNODEMETHOD(regnode_enable, sy8106a_regnode_enable), 189 REGNODEMETHOD(regnode_set_voltage, sy8106a_regnode_set_voltage), 190 REGNODEMETHOD(regnode_get_voltage, sy8106a_regnode_get_voltage), 191 REGNODEMETHOD_END 192 }; 193 DEFINE_CLASS_1(sy8106a_regnode, sy8106a_regnode_class, sy8106a_regnode_methods, 194 sizeof(struct sy8106a_reg_sc), regnode_class); 195 196 static struct sy8106a_reg_sc * 197 sy8106a_reg_attach(device_t dev, phandle_t node) 198 { 199 struct sy8106a_reg_sc *reg_sc; 200 struct regnode_init_def initdef; 201 struct regnode *regnode; 202 203 memset(&initdef, 0, sizeof(initdef)); 204 regulator_parse_ofw_stdparam(dev, node, &initdef); 205 initdef.id = 0; 206 initdef.ofw_node = node; 207 regnode = regnode_create(dev, &sy8106a_regnode_class, &initdef); 208 if (regnode == NULL) { 209 device_printf(dev, "cannot create regulator\n"); 210 return (NULL); 211 } 212 213 reg_sc = regnode_get_softc(regnode); 214 reg_sc->regnode = regnode; 215 reg_sc->base_dev = dev; 216 reg_sc->xref = OF_xref_from_node(node); 217 reg_sc->param = regnode_get_stdparam(regnode); 218 219 regnode_register(regnode); 220 221 return (reg_sc); 222 } 223 224 static int 225 sy8106a_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, 226 intptr_t *num) 227 { 228 struct sy8106a_softc *sc; 229 230 sc = device_get_softc(dev); 231 232 if (sc->reg->xref != xref) 233 return (ENXIO); 234 235 *num = 0; 236 237 return (0); 238 } 239 240 static int 241 sy8106a_probe(device_t dev) 242 { 243 if (!ofw_bus_status_okay(dev)) 244 return (ENXIO); 245 246 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 247 return (ENXIO); 248 249 device_set_desc(dev, "Silergy SY8106A regulator"); 250 251 return (BUS_PROBE_DEFAULT); 252 } 253 254 static int 255 sy8106a_attach(device_t dev) 256 { 257 struct sy8106a_softc *sc; 258 phandle_t node; 259 260 sc = device_get_softc(dev); 261 node = ofw_bus_get_node(dev); 262 263 sc->addr = iicbus_get_addr(dev); 264 265 sc->reg = sy8106a_reg_attach(dev, node); 266 if (sc->reg == NULL) { 267 device_printf(dev, "cannot attach regulator\n"); 268 return (ENXIO); 269 } 270 271 return (0); 272 } 273 274 static device_method_t sy8106a_methods[] = { 275 /* Device interface */ 276 DEVMETHOD(device_probe, sy8106a_probe), 277 DEVMETHOD(device_attach, sy8106a_attach), 278 279 /* Regdev interface */ 280 DEVMETHOD(regdev_map, sy8106a_regdev_map), 281 282 DEVMETHOD_END 283 }; 284 285 static driver_t sy8106a_driver = { 286 "sy8106a", 287 sy8106a_methods, 288 sizeof(struct sy8106a_softc), 289 }; 290 291 EARLY_DRIVER_MODULE(sy8106a, iicbus, sy8106a_driver, 0, 0, BUS_PASS_RESOURCE); 292 MODULE_VERSION(sy8106a, 1); 293 MODULE_DEPEND(sy8106a, iicbus, 1, 1, 1); 294 IICBUS_FDT_PNP_INFO(compat_data); 295