xref: /openbsd-src/sys/dev/fdt/sypwr.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1*9fdf0c62Smpi /*	$OpenBSD: sypwr.c,v 1.5 2021/10/24 17:52:27 mpi Exp $	*/
2de30f4ccSkettenis /*
3de30f4ccSkettenis  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4de30f4ccSkettenis  *
5de30f4ccSkettenis  * Permission to use, copy, modify, and distribute this software for any
6de30f4ccSkettenis  * purpose with or without fee is hereby granted, provided that the above
7de30f4ccSkettenis  * copyright notice and this permission notice appear in all copies.
8de30f4ccSkettenis  *
9de30f4ccSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10de30f4ccSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11de30f4ccSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12de30f4ccSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13de30f4ccSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14de30f4ccSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15de30f4ccSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16de30f4ccSkettenis  */
17de30f4ccSkettenis 
18de30f4ccSkettenis #include <sys/param.h>
19de30f4ccSkettenis #include <sys/systm.h>
20de30f4ccSkettenis #include <sys/device.h>
21de30f4ccSkettenis #include <sys/malloc.h>
22de30f4ccSkettenis 
23de30f4ccSkettenis #include <dev/ofw/openfirm.h>
24de30f4ccSkettenis #include <dev/ofw/ofw_regulator.h>
25de30f4ccSkettenis #include <dev/ofw/fdt.h>
26de30f4ccSkettenis 
27de30f4ccSkettenis #include <dev/i2c/i2cvar.h>
28de30f4ccSkettenis 
29de30f4ccSkettenis #define SY8106A_VOUT1_SEL		0x01
30de30f4ccSkettenis #define  SY8106A_VOUT1_SEL_I2C		(1 << 7)
31de30f4ccSkettenis #define  SY8106A_VOUT1_SEL_MASK 	0x7f
32de30f4ccSkettenis 
33de30f4ccSkettenis struct sypwr_softc {
34de30f4ccSkettenis 	struct device	sc_dev;
35de30f4ccSkettenis 	i2c_tag_t	sc_tag;
36de30f4ccSkettenis 	i2c_addr_t	sc_addr;
37de30f4ccSkettenis 
38fffe0f8eSkettenis 	uint32_t	sc_fixed_microvolt;
39fffe0f8eSkettenis 
40de30f4ccSkettenis 	struct regulator_device sc_rd;
41de30f4ccSkettenis };
42de30f4ccSkettenis 
43de30f4ccSkettenis int	sypwr_match(struct device *, void *, void *);
44de30f4ccSkettenis void	sypwr_attach(struct device *, struct device *, void *);
45fffe0f8eSkettenis int	sypwr_activate(struct device *, int);
46de30f4ccSkettenis 
47*9fdf0c62Smpi const struct cfattach sypwr_ca = {
48fffe0f8eSkettenis 	sizeof(struct sypwr_softc), sypwr_match, sypwr_attach,
49fffe0f8eSkettenis 	NULL, sypwr_activate
50de30f4ccSkettenis };
51de30f4ccSkettenis 
52de30f4ccSkettenis struct cfdriver sypwr_cd = {
53de30f4ccSkettenis 	NULL, "sypwr", DV_DULL
54de30f4ccSkettenis };
55de30f4ccSkettenis 
56de30f4ccSkettenis uint8_t	sypwr_read(struct sypwr_softc *, int);
57de30f4ccSkettenis void	sypwr_write(struct sypwr_softc *, int, uint8_t);
58de30f4ccSkettenis uint32_t sypwr_get_voltage(void *);
598f4013c9Skettenis int	sypwr_set_voltage(void *, uint32_t);
60de30f4ccSkettenis 
61de30f4ccSkettenis int
sypwr_match(struct device * parent,void * match,void * aux)62de30f4ccSkettenis sypwr_match(struct device *parent, void *match, void *aux)
63de30f4ccSkettenis {
64de30f4ccSkettenis 	struct i2c_attach_args *ia = aux;
65de30f4ccSkettenis 
661ac62f4eSpatrick 	return (strcmp(ia->ia_name, "silergy,sy8106a") == 0);
67de30f4ccSkettenis }
68de30f4ccSkettenis 
69de30f4ccSkettenis void
sypwr_attach(struct device * parent,struct device * self,void * aux)70de30f4ccSkettenis sypwr_attach(struct device *parent, struct device *self, void *aux)
71de30f4ccSkettenis {
72de30f4ccSkettenis 	struct sypwr_softc *sc = (struct sypwr_softc *)self;
73de30f4ccSkettenis 	struct i2c_attach_args *ia = aux;
74de30f4ccSkettenis 	int node = *(int *)ia->ia_cookie;
75de30f4ccSkettenis 	uint8_t reg;
76de30f4ccSkettenis 
77de30f4ccSkettenis 	sc->sc_tag = ia->ia_tag;
78de30f4ccSkettenis 	sc->sc_addr = ia->ia_addr;
79de30f4ccSkettenis 
80fffe0f8eSkettenis 	sc->sc_fixed_microvolt =
81fffe0f8eSkettenis 	    OF_getpropint(node, "silergy,fixed-microvolt", 0);
82fffe0f8eSkettenis 
83de30f4ccSkettenis 	/*
84de30f4ccSkettenis 	 * Only register the regulator if it is under I2C control
85fffe0f8eSkettenis 	 * (i.e. initialized by the firmware) or if the device tree
86fffe0f8eSkettenis 	 * specifies its fixed voltage.  Otherwise we have no idea
87fffe0f8eSkettenis 	 * what the current output voltage is, which will confuse the
88fffe0f8eSkettenis 	 * regulator framework.
89de30f4ccSkettenis 	 */
90de30f4ccSkettenis 	reg = sypwr_read(sc, SY8106A_VOUT1_SEL);
91fffe0f8eSkettenis 	if (reg & SY8106A_VOUT1_SEL_I2C || sc->sc_fixed_microvolt != 0) {
92de30f4ccSkettenis 		uint32_t voltage;
93de30f4ccSkettenis 
94de30f4ccSkettenis 		voltage = sypwr_get_voltage(sc);
95de30f4ccSkettenis 		printf(": %d.%02d VDC", voltage / 1000000,
96de30f4ccSkettenis 		    (voltage % 1000000) / 10000);
97de30f4ccSkettenis 
98de30f4ccSkettenis 		sc->sc_rd.rd_node = node;
99de30f4ccSkettenis 		sc->sc_rd.rd_cookie = sc;
100de30f4ccSkettenis 		sc->sc_rd.rd_get_voltage = sypwr_get_voltage;
101de30f4ccSkettenis 		sc->sc_rd.rd_set_voltage = sypwr_set_voltage;
102de30f4ccSkettenis 		regulator_register(&sc->sc_rd);
103de30f4ccSkettenis 	}
104de30f4ccSkettenis 
105de30f4ccSkettenis 	printf("\n");
106de30f4ccSkettenis }
107de30f4ccSkettenis 
108fffe0f8eSkettenis int
sypwr_activate(struct device * self,int act)109fffe0f8eSkettenis sypwr_activate(struct device *self, int act)
110fffe0f8eSkettenis {
111fffe0f8eSkettenis 	struct sypwr_softc *sc = (struct sypwr_softc *)self;
112fffe0f8eSkettenis 	uint8_t reg;
113fffe0f8eSkettenis 
114fffe0f8eSkettenis 	switch (act) {
115fffe0f8eSkettenis 	case DVACT_POWERDOWN:
116fffe0f8eSkettenis 		/*
117fffe0f8eSkettenis 		 * Restore fixed voltage otherwise we might hang after
118fffe0f8eSkettenis 		 * a warm reset.
119fffe0f8eSkettenis 		 */
120fffe0f8eSkettenis 		if (sc->sc_fixed_microvolt != 0) {
121fffe0f8eSkettenis 			reg = sypwr_read(sc, SY8106A_VOUT1_SEL);
122fffe0f8eSkettenis 			reg &= ~SY8106A_VOUT1_SEL_I2C;
123fffe0f8eSkettenis 			sypwr_write(sc, SY8106A_VOUT1_SEL, reg);
124fffe0f8eSkettenis 		}
125fffe0f8eSkettenis 		break;
126fffe0f8eSkettenis 	}
127fffe0f8eSkettenis 
128fffe0f8eSkettenis 	return 0;
129fffe0f8eSkettenis }
130fffe0f8eSkettenis 
131de30f4ccSkettenis uint8_t
sypwr_read(struct sypwr_softc * sc,int reg)132de30f4ccSkettenis sypwr_read(struct sypwr_softc *sc, int reg)
133de30f4ccSkettenis {
134de30f4ccSkettenis 	uint8_t cmd = reg;
135de30f4ccSkettenis 	uint8_t val;
136de30f4ccSkettenis 	int error;
137de30f4ccSkettenis 
138de30f4ccSkettenis 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
139de30f4ccSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
140de30f4ccSkettenis 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
141de30f4ccSkettenis 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
142de30f4ccSkettenis 
143de30f4ccSkettenis 	if (error) {
144de30f4ccSkettenis 		printf("error %d\n", error);
145de30f4ccSkettenis 		printf("%s: can't read register 0x%02x\n",
146de30f4ccSkettenis 		    sc->sc_dev.dv_xname, reg);
147de30f4ccSkettenis 		val = 0xff;
148de30f4ccSkettenis 	}
149de30f4ccSkettenis 
150de30f4ccSkettenis 	return val;
151de30f4ccSkettenis }
152de30f4ccSkettenis 
153de30f4ccSkettenis void
sypwr_write(struct sypwr_softc * sc,int reg,uint8_t val)154de30f4ccSkettenis sypwr_write(struct sypwr_softc *sc, int reg, uint8_t val)
155de30f4ccSkettenis {
156de30f4ccSkettenis 	uint8_t cmd = reg;
157de30f4ccSkettenis 	int error;
158de30f4ccSkettenis 
159de30f4ccSkettenis 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
160de30f4ccSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
161de30f4ccSkettenis 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
162de30f4ccSkettenis 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
163de30f4ccSkettenis 
164de30f4ccSkettenis 	if (error) {
165de30f4ccSkettenis 		printf("%s: can't write register 0x%02x\n",
166de30f4ccSkettenis 		    sc->sc_dev.dv_xname, reg);
167de30f4ccSkettenis 	}
168de30f4ccSkettenis }
169de30f4ccSkettenis 
170de30f4ccSkettenis uint32_t
sypwr_get_voltage(void * cookie)171de30f4ccSkettenis sypwr_get_voltage(void *cookie)
172de30f4ccSkettenis {
173de30f4ccSkettenis 	struct sypwr_softc *sc = cookie;
174de30f4ccSkettenis 	uint8_t value;
175de30f4ccSkettenis 
176de30f4ccSkettenis 	value = sypwr_read(sc, SY8106A_VOUT1_SEL);
177fffe0f8eSkettenis 	if (value & SY8106A_VOUT1_SEL_I2C)
178de30f4ccSkettenis 		return 680000 + (value & SY8106A_VOUT1_SEL_MASK) * 10000;
179fffe0f8eSkettenis 	else
180fffe0f8eSkettenis 		return sc->sc_fixed_microvolt;
181de30f4ccSkettenis }
182de30f4ccSkettenis 
183de30f4ccSkettenis int
sypwr_set_voltage(void * cookie,uint32_t voltage)184de30f4ccSkettenis sypwr_set_voltage(void *cookie, uint32_t voltage)
185de30f4ccSkettenis {
186de30f4ccSkettenis 	struct sypwr_softc *sc = cookie;
187de30f4ccSkettenis 	uint8_t value;
188de30f4ccSkettenis 
189de30f4ccSkettenis 	if (voltage < 680000 || voltage > 1950000)
190de30f4ccSkettenis 		return EINVAL;
191de30f4ccSkettenis 
192de30f4ccSkettenis 	value = (voltage - 680000) / 10000;
193de30f4ccSkettenis 	sypwr_write(sc, SY8106A_VOUT1_SEL, value | SY8106A_VOUT1_SEL_I2C);
194de30f4ccSkettenis 	return 0;
195de30f4ccSkettenis }
196