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