xref: /openbsd/sys/dev/fdt/bd718x7.c (revision 37c734d3)
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