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