1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 5 * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/rman.h> 36#include <sys/kernel.h> 37#include <sys/reboot.h> 38#include <sys/module.h> 39 40#include <dev/iicbus/iicbus.h> 41#include <dev/iicbus/iiconf.h> 42 43#include <dev/ofw/ofw_bus.h> 44#include <dev/ofw/ofw_bus_subr.h> 45 46#include <dev/extres/regulator/regulator.h> 47 48#include "iicbus_if.h" 49#include "regdev_if.h" 50 51#define VSEL0 0x00 52#define VSEL1 0x01 53 54#define VSEL_BUCK_EN (1 << 7) 55#define VSEL_NSEL_MASK 0x3F 56#define VSEL_VOLTAGE_BASE 712500 /* uV */ 57#define VSEL_VOLTAGE_STEP 12500 /* uV */ 58 59#define ID1 0x03 60#define ID1_VENDOR_MASK 0xE0 61#define ID1_VENDOR_SHIFT 5 62#define ID1_DIE_MASK 0xF 63 64#define ID2 0x4 65#define ID2_DIE_REV_MASK 0xF 66 67static struct ofw_compat_data compat_data[] = { 68 { "silergy,syr827", 1 }, 69 { NULL, 0 } 70}; 71 72struct syr827_reg_sc { 73 struct regnode *regnode; 74 device_t base_dev; 75 phandle_t xref; 76 struct regnode_std_param *param; 77 78 int volt_reg; 79 int suspend_reg; 80}; 81 82struct syr827_softc { 83 uint16_t addr; 84 struct intr_config_hook intr_hook; 85 86 /* Regulator */ 87 struct syr827_reg_sc *reg; 88}; 89 90static int 91syr827_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) 92{ 93 return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT)); 94} 95 96static int 97syr827_write(device_t dev, uint8_t reg, uint8_t val) 98{ 99 return (iicdev_writeto(dev, reg, &val, 1, IIC_INTRWAIT)); 100} 101 102static int 103syr827_regnode_init(struct regnode *regnode) 104{ 105 return (0); 106} 107 108static int 109syr827_regnode_enable(struct regnode *regnode, bool enable, int *udelay) 110{ 111 struct syr827_reg_sc *sc; 112 uint8_t val; 113 114 sc = regnode_get_softc(regnode); 115 116 syr827_read(sc->base_dev, sc->volt_reg, &val, 1); 117 if (enable) 118 val &= ~VSEL_BUCK_EN; 119 else 120 val |= VSEL_BUCK_EN; 121 syr827_write(sc->base_dev, sc->volt_reg, val); 122 123 *udelay = sc->param->ramp_delay; 124 125 return (0); 126} 127 128static int 129syr827_regnode_set_voltage(struct regnode *regnode, int min_uvolt, 130 int max_uvolt, int *udelay) 131{ 132 struct syr827_reg_sc *sc; 133 int cur_uvolt; 134 uint8_t val; 135 136 sc = regnode_get_softc(regnode); 137 138 /* Get current voltage */ 139 syr827_read(sc->base_dev, sc->volt_reg, &val, 1); 140 cur_uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP + 141 VSEL_VOLTAGE_BASE; 142 143 /* Set new voltage */ 144 val &= ~VSEL_NSEL_MASK; 145 val |= ((min_uvolt - VSEL_VOLTAGE_BASE) / VSEL_VOLTAGE_STEP); 146 syr827_write(sc->base_dev, sc->volt_reg, val); 147 148 /* Time to delay is based on the number of voltage steps */ 149 *udelay = sc->param->ramp_delay * 150 (abs(cur_uvolt - min_uvolt) / VSEL_VOLTAGE_STEP); 151 152 return (0); 153} 154 155static int 156syr827_regnode_get_voltage(struct regnode *regnode, int *uvolt) 157{ 158 struct syr827_reg_sc *sc; 159 uint8_t val; 160 161 sc = regnode_get_softc(regnode); 162 163 syr827_read(sc->base_dev, sc->volt_reg, &val, 1); 164 *uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP + 165 VSEL_VOLTAGE_BASE; 166 167 return (0); 168} 169 170static regnode_method_t syr827_regnode_methods[] = { 171 /* Regulator interface */ 172 REGNODEMETHOD(regnode_init, syr827_regnode_init), 173 REGNODEMETHOD(regnode_enable, syr827_regnode_enable), 174 REGNODEMETHOD(regnode_set_voltage, syr827_regnode_set_voltage), 175 REGNODEMETHOD(regnode_get_voltage, syr827_regnode_get_voltage), 176 REGNODEMETHOD_END 177}; 178DEFINE_CLASS_1(syr827_regnode, syr827_regnode_class, syr827_regnode_methods, 179 sizeof(struct syr827_reg_sc), regnode_class); 180 181static struct syr827_reg_sc * 182syr827_reg_attach(device_t dev, phandle_t node) 183{ 184 struct syr827_reg_sc *reg_sc; 185 struct regnode_init_def initdef; 186 struct regnode *regnode; 187 int suspend_reg; 188 189 memset(&initdef, 0, sizeof(initdef)); 190 regulator_parse_ofw_stdparam(dev, node, &initdef); 191 initdef.id = 0; 192 initdef.ofw_node = node; 193 regnode = regnode_create(dev, &syr827_regnode_class, &initdef); 194 if (regnode == NULL) { 195 device_printf(dev, "cannot create regulator\n"); 196 return (NULL); 197 } 198 199 reg_sc = regnode_get_softc(regnode); 200 reg_sc->regnode = regnode; 201 reg_sc->base_dev = dev; 202 reg_sc->xref = OF_xref_from_node(node); 203 reg_sc->param = regnode_get_stdparam(regnode); 204 205 if (OF_getencprop(node, "fcs,suspend-voltage-selector", &suspend_reg, 206 sizeof(uint32_t)) <= 0) 207 suspend_reg = 0; 208 209 switch (suspend_reg) { 210 case 0: 211 reg_sc->suspend_reg = VSEL0; 212 reg_sc->volt_reg = VSEL1; 213 break; 214 case 1: 215 reg_sc->suspend_reg = VSEL1; 216 reg_sc->volt_reg = VSEL0; 217 break; 218 } 219 220 regnode_register(regnode); 221 222 return (reg_sc); 223} 224 225static int 226syr827_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, 227 intptr_t *num) 228{ 229 struct syr827_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 241static int 242syr827_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 SYR827 regulator"); 251 252 return (BUS_PROBE_DEFAULT); 253} 254 255static void 256syr827_start(void *pdev) 257{ 258 struct syr827_softc *sc; 259 device_t dev; 260 uint8_t val; 261 262 dev = pdev; 263 sc = device_get_softc(dev); 264 265 if (bootverbose) { 266 syr827_read(dev, ID1, &val, 1); 267 device_printf(dev, "Vendor ID: %x, DIE ID: %x\n", 268 (val & ID1_VENDOR_MASK) >> ID1_VENDOR_SHIFT, 269 val & ID1_DIE_MASK); 270 syr827_read(dev, ID2, &val, 1); 271 device_printf(dev, "DIE Rev: %x\n", val & ID2_DIE_REV_MASK); 272 } 273 274 config_intrhook_disestablish(&sc->intr_hook); 275} 276 277static int 278syr827_attach(device_t dev) 279{ 280 struct syr827_softc *sc; 281 phandle_t node; 282 283 sc = device_get_softc(dev); 284 node = ofw_bus_get_node(dev); 285 286 sc->addr = iicbus_get_addr(dev); 287 288 sc->intr_hook.ich_func = syr827_start; 289 sc->intr_hook.ich_arg = dev; 290 291 if (config_intrhook_establish(&sc->intr_hook) != 0) 292 return (ENOMEM); 293 294 sc->reg = syr827_reg_attach(dev, node); 295 if (sc->reg == NULL) { 296 device_printf(dev, "cannot attach regulator\n"); 297 return (ENXIO); 298 } 299 300 return (0); 301} 302 303static device_method_t syr827_methods[] = { 304 /* Device interface */ 305 DEVMETHOD(device_probe, syr827_probe), 306 DEVMETHOD(device_attach, syr827_attach), 307 308 /* Regdev interface */ 309 DEVMETHOD(regdev_map, syr827_regdev_map), 310 311 DEVMETHOD_END 312}; 313 314static driver_t syr827_driver = { 315 "syr827", 316 syr827_methods, 317 sizeof(struct syr827_softc), 318}; 319 320static devclass_t syr827_devclass; 321 322EARLY_DRIVER_MODULE(syr827, iicbus, syr827_driver, syr827_devclass, 0, 0, 323 BUS_PASS_RESOURCE); 324MODULE_VERSION(syr827, 1); 325MODULE_DEPEND(syr827, iicbus, 1, 1, 1); 326IICBUS_FDT_PNP_INFO(compat_data); 327