1*37c734d3Snaddy /* $OpenBSD: bd718x7.c,v 1.5 2022/06/28 23:43:12 naddy Exp $ */
23349995dSpatrick /*
33349995dSpatrick * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
43349995dSpatrick * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
53349995dSpatrick *
63349995dSpatrick * Permission to use, copy, modify, and distribute this software for any
73349995dSpatrick * purpose with or without fee is hereby granted, provided that the above
83349995dSpatrick * copyright notice and this permission notice appear in all copies.
93349995dSpatrick *
103349995dSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113349995dSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123349995dSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133349995dSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143349995dSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153349995dSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163349995dSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173349995dSpatrick */
183349995dSpatrick
193349995dSpatrick #include <sys/param.h>
203349995dSpatrick #include <sys/systm.h>
213349995dSpatrick #include <sys/device.h>
223349995dSpatrick #include <sys/malloc.h>
233349995dSpatrick
243349995dSpatrick #include <dev/ofw/openfirm.h>
253349995dSpatrick #include <dev/ofw/ofw_regulator.h>
263349995dSpatrick #include <dev/ofw/fdt.h>
273349995dSpatrick
283349995dSpatrick #include <dev/i2c/i2cvar.h>
293349995dSpatrick
303349995dSpatrick #define BDPMIC_REGLOCK 0x2f
313349995dSpatrick #define BDPMIC_REGLOCK_PWRSEQ (1 << 0)
323349995dSpatrick #define BDPMIC_REGLOCK_VREG (1 << 4)
333349995dSpatrick
343349995dSpatrick struct bdpmic_regdata {
353349995dSpatrick const char *name;
363349995dSpatrick uint8_t reg, mask;
373349995dSpatrick uint32_t base, delta;
383349995dSpatrick };
393349995dSpatrick
40*37c734d3Snaddy const struct bdpmic_regdata bd71837_regdata[] = {
41ab120fc2Spatrick { "BUCK2", 0x10, 0x3f, 700000, 10000 },
423349995dSpatrick { }
433349995dSpatrick };
443349995dSpatrick
45*37c734d3Snaddy const struct bdpmic_regdata bd71847_regdata[] = {
463349995dSpatrick { "BUCK2", 0x10, 0x3f, 700000, 10000 },
473349995dSpatrick { }
483349995dSpatrick };
493349995dSpatrick
503349995dSpatrick struct bdpmic_softc {
513349995dSpatrick struct device sc_dev;
523349995dSpatrick i2c_tag_t sc_tag;
533349995dSpatrick i2c_addr_t sc_addr;
543349995dSpatrick
55*37c734d3Snaddy const struct bdpmic_regdata *sc_regdata;
563349995dSpatrick };
573349995dSpatrick
583349995dSpatrick int bdpmic_match(struct device *, void *, void *);
593349995dSpatrick void bdpmic_attach(struct device *, struct device *, void *);
603349995dSpatrick
613349995dSpatrick void bdpmic_attach_regulator(struct bdpmic_softc *, int);
623349995dSpatrick uint8_t bdpmic_reg_read(struct bdpmic_softc *, int);
633349995dSpatrick void bdpmic_reg_write(struct bdpmic_softc *, int, uint8_t);
643349995dSpatrick
65471aeecfSnaddy const struct cfattach bdpmic_ca = {
663349995dSpatrick sizeof(struct bdpmic_softc), bdpmic_match, bdpmic_attach
673349995dSpatrick };
683349995dSpatrick
693349995dSpatrick struct cfdriver bdpmic_cd = {
703349995dSpatrick NULL, "bdpmic", DV_DULL
713349995dSpatrick };
723349995dSpatrick
733349995dSpatrick int
bdpmic_match(struct device * parent,void * match,void * aux)743349995dSpatrick bdpmic_match(struct device *parent, void *match, void *aux)
753349995dSpatrick {
763349995dSpatrick struct i2c_attach_args *ia = aux;
773349995dSpatrick
781ac62f4eSpatrick return (strcmp(ia->ia_name, "rohm,bd71837") == 0 ||
791ac62f4eSpatrick strcmp(ia->ia_name, "rohm,bd71847") == 0);
803349995dSpatrick }
813349995dSpatrick
823349995dSpatrick void
bdpmic_attach(struct device * parent,struct device * self,void * aux)833349995dSpatrick bdpmic_attach(struct device *parent, struct device *self, void *aux)
843349995dSpatrick {
853349995dSpatrick struct bdpmic_softc *sc = (struct bdpmic_softc *)self;
863349995dSpatrick struct i2c_attach_args *ia = aux;
873349995dSpatrick int node = *(int *)ia->ia_cookie;
883349995dSpatrick const char *chip;
893349995dSpatrick
903349995dSpatrick sc->sc_tag = ia->ia_tag;
913349995dSpatrick sc->sc_addr = ia->ia_addr;
923349995dSpatrick
933349995dSpatrick if (OF_is_compatible(node, "rohm,bd71837")) {
943349995dSpatrick chip = "BD71837";
953349995dSpatrick sc->sc_regdata = bd71837_regdata;
963349995dSpatrick } else {
973349995dSpatrick chip = "BD71847";
983349995dSpatrick sc->sc_regdata = bd71847_regdata;
993349995dSpatrick }
1003349995dSpatrick printf(": %s\n", chip);
1013349995dSpatrick
1023349995dSpatrick node = OF_getnodebyname(node, "regulators");
1033349995dSpatrick if (node == 0)
1043349995dSpatrick return;
1053349995dSpatrick for (node = OF_child(node); node; node = OF_peer(node))
1063349995dSpatrick bdpmic_attach_regulator(sc, node);
1073349995dSpatrick }
1083349995dSpatrick
1093349995dSpatrick struct bdpmic_regulator {
1103349995dSpatrick struct bdpmic_softc *bd_sc;
1113349995dSpatrick
1123349995dSpatrick uint8_t bd_reg, bd_mask;
1133349995dSpatrick uint32_t bd_base, bd_delta;
1143349995dSpatrick
1153349995dSpatrick struct regulator_device bd_rd;
1163349995dSpatrick };
1173349995dSpatrick
1183349995dSpatrick uint32_t bdpmic_get_voltage(void *);
1193349995dSpatrick int bdpmic_set_voltage(void *, uint32_t);
1203349995dSpatrick
1213349995dSpatrick void
bdpmic_attach_regulator(struct bdpmic_softc * sc,int node)1223349995dSpatrick bdpmic_attach_regulator(struct bdpmic_softc *sc, int node)
1233349995dSpatrick {
1243349995dSpatrick struct bdpmic_regulator *bd;
1253349995dSpatrick char name[32];
1263349995dSpatrick int i;
1273349995dSpatrick
1283349995dSpatrick name[0] = 0;
1293349995dSpatrick OF_getprop(node, "name", name, sizeof(name));
1303349995dSpatrick name[sizeof(name) - 1] = 0;
1313349995dSpatrick for (i = 0; sc->sc_regdata[i].name; i++) {
1323349995dSpatrick if (strcmp(sc->sc_regdata[i].name, name) == 0)
1333349995dSpatrick break;
1343349995dSpatrick }
1353349995dSpatrick if (sc->sc_regdata[i].name == NULL)
1363349995dSpatrick return;
1373349995dSpatrick
1383349995dSpatrick bd = malloc(sizeof(*bd), M_DEVBUF, M_WAITOK | M_ZERO);
1393349995dSpatrick bd->bd_sc = sc;
1403349995dSpatrick
1413349995dSpatrick bd->bd_reg = sc->sc_regdata[i].reg;
1423349995dSpatrick bd->bd_mask = sc->sc_regdata[i].mask;
1433349995dSpatrick bd->bd_base = sc->sc_regdata[i].base;
1443349995dSpatrick bd->bd_delta = sc->sc_regdata[i].delta;
1453349995dSpatrick
1463349995dSpatrick bd->bd_rd.rd_node = node;
1473349995dSpatrick bd->bd_rd.rd_cookie = bd;
1483349995dSpatrick bd->bd_rd.rd_get_voltage = bdpmic_get_voltage;
1493349995dSpatrick bd->bd_rd.rd_set_voltage = bdpmic_set_voltage;
1503349995dSpatrick regulator_register(&bd->bd_rd);
1513349995dSpatrick }
1523349995dSpatrick
1533349995dSpatrick uint32_t
bdpmic_get_voltage(void * cookie)1543349995dSpatrick bdpmic_get_voltage(void *cookie)
1553349995dSpatrick {
1563349995dSpatrick struct bdpmic_regulator *bd = cookie;
1573349995dSpatrick uint8_t vsel;
1583349995dSpatrick
1593349995dSpatrick vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg);
1603349995dSpatrick return bd->bd_base + (vsel & bd->bd_mask) * bd->bd_delta;
1613349995dSpatrick }
1623349995dSpatrick
1633349995dSpatrick int
bdpmic_set_voltage(void * cookie,uint32_t voltage)1643349995dSpatrick bdpmic_set_voltage(void *cookie, uint32_t voltage)
1653349995dSpatrick {
1663349995dSpatrick struct bdpmic_regulator *bd = cookie;
1673349995dSpatrick uint32_t vmin = bd->bd_base;
1683349995dSpatrick uint32_t vmax = vmin + bd->bd_mask * bd->bd_delta;
1693349995dSpatrick uint8_t vsel;
1703349995dSpatrick
1713349995dSpatrick if (voltage < vmin || voltage > vmax)
1723349995dSpatrick return EINVAL;
1733349995dSpatrick
1743349995dSpatrick /* Unlock */
1753349995dSpatrick bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK,
1763349995dSpatrick BDPMIC_REGLOCK_PWRSEQ);
1773349995dSpatrick
1783349995dSpatrick vsel = bdpmic_reg_read(bd->bd_sc, bd->bd_reg);
1793349995dSpatrick vsel &= ~bd->bd_mask;
1803349995dSpatrick vsel |= (voltage - bd->bd_base) / bd->bd_delta;
1813349995dSpatrick bdpmic_reg_write(bd->bd_sc, bd->bd_reg, vsel);
1823349995dSpatrick
1833349995dSpatrick /* Lock */
1843349995dSpatrick bdpmic_reg_write(bd->bd_sc, BDPMIC_REGLOCK,
1853349995dSpatrick BDPMIC_REGLOCK_PWRSEQ | BDPMIC_REGLOCK_VREG);
1863349995dSpatrick
1873349995dSpatrick return 0;
1883349995dSpatrick }
1893349995dSpatrick
1903349995dSpatrick uint8_t
bdpmic_reg_read(struct bdpmic_softc * sc,int reg)1913349995dSpatrick bdpmic_reg_read(struct bdpmic_softc *sc, int reg)
1923349995dSpatrick {
1933349995dSpatrick uint8_t cmd = reg;
1943349995dSpatrick uint8_t val;
1953349995dSpatrick int error;
1963349995dSpatrick
1973349995dSpatrick iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
1983349995dSpatrick error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
1993349995dSpatrick &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
2003349995dSpatrick iic_release_bus(sc->sc_tag, I2C_F_POLL);
2013349995dSpatrick
2023349995dSpatrick if (error) {
2033349995dSpatrick printf("%s: can't read register 0x%02x\n",
2043349995dSpatrick sc->sc_dev.dv_xname, reg);
2053349995dSpatrick val = 0xff;
2063349995dSpatrick }
2073349995dSpatrick
2083349995dSpatrick return val;
2093349995dSpatrick }
2103349995dSpatrick
2113349995dSpatrick void
bdpmic_reg_write(struct bdpmic_softc * sc,int reg,uint8_t val)2123349995dSpatrick bdpmic_reg_write(struct bdpmic_softc *sc, int reg, uint8_t val)
2133349995dSpatrick {
2143349995dSpatrick uint8_t cmd = reg;
2153349995dSpatrick int error;
2163349995dSpatrick
2173349995dSpatrick iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
2183349995dSpatrick error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
2193349995dSpatrick &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
2203349995dSpatrick iic_release_bus(sc->sc_tag, I2C_F_POLL);
2213349995dSpatrick
2223349995dSpatrick if (error) {
2233349995dSpatrick printf("%s: can't write register 0x%02x\n",
2243349995dSpatrick sc->sc_dev.dv_xname, reg);
2253349995dSpatrick }
2263349995dSpatrick }
227