xref: /openbsd/sys/dev/fdt/fanpwr.c (revision f057eb46)
1 /*	$OpenBSD: fanpwr.c,v 1.10 2024/05/26 22:04:52 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2018 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_pinctrl.h>
25 #include <dev/ofw/ofw_regulator.h>
26 #include <dev/ofw/fdt.h>
27 
28 #include <dev/i2c/i2cvar.h>
29 
30 /* Registers */
31 #define FAN53555_VSEL0			0x00
32 #define FAN53555_VSEL1			0x01
33 #define  FAN53555_VSEL_NSEL_MASK	0x3f
34 #define FAN53555_CONTROL		0x02
35 #define  FAN53555_CONTROL_SLEW_MASK	(0x7 << 4)
36 #define  FAN53555_CONTROL_SLEW_SHIFT	4
37 #define FAN53555_ID1			0x03
38 #define FAN53555_ID2			0x04
39 
40 #define TCS4525_VSEL1			0x10
41 #define TCS4525_VSEL0			0x11
42 #define  TCS4525_VSEL_NSEL_MASK		0x7f
43 #define TCS4525_TIME			0x13
44 #define  TCS4525_TIME_SLEW_MASK		(0x3 << 3)
45 #define  TCS4525_TIME_SLEW_SHIFT	3
46 
47 #define RK8602_VSEL0			0x06
48 #define RK8602_VSEL1			0x07
49 #define  RK8602_VSEL_NSEL_MASK		0xff
50 
51 /* Distinguish between Fairchild original and Silergy clones. */
52 enum fanpwr_id {
53 	FANPWR_FAN53555,	/* Fairchild FAN53555 */
54 	FANPWR_RK8602,		/* Rockchip RK8602 */
55 	FANPWR_SYR827,		/* Silergy SYR827 */
56 	FANPWR_SYR828,		/* Silergy SYR828 */
57 	FANPWR_TCS4525,		/* TCS TCS4525 */
58 };
59 
60 struct fanpwr_softc {
61 	struct device	sc_dev;
62 	i2c_tag_t	sc_tag;
63 	i2c_addr_t	sc_addr;
64 
65 	enum fanpwr_id	sc_id;
66 	uint8_t		sc_vsel;
67 	uint8_t		sc_vsel_nsel_mask;
68 
69 	struct regulator_device sc_rd;
70 	uint32_t	sc_vbase;
71 	uint32_t	sc_vstep;
72 };
73 
74 int	fanpwr_match(struct device *, void *, void *);
75 void	fanpwr_attach(struct device *, struct device *, void *);
76 
77 const struct cfattach fanpwr_ca = {
78 	sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach
79 };
80 
81 struct cfdriver fanpwr_cd = {
82 	NULL, "fanpwr", DV_DULL
83 };
84 
85 uint8_t	fanpwr_read(struct fanpwr_softc *, int);
86 void	fanpwr_write(struct fanpwr_softc *, int, uint8_t);
87 uint32_t fanpwr_get_voltage(void *);
88 int	fanpwr_set_voltage(void *, uint32_t);
89 
90 int
fanpwr_match(struct device * parent,void * match,void * aux)91 fanpwr_match(struct device *parent, void *match, void *aux)
92 {
93 	struct i2c_attach_args *ia = aux;
94 
95 	return (strcmp(ia->ia_name, "fcs,fan53555") == 0 ||
96 	    strcmp(ia->ia_name, "rockchip,rk8602") == 0 ||
97 	    strcmp(ia->ia_name, "rockchip,rk8603") == 0 ||
98 	    strcmp(ia->ia_name, "silergy,syr827") == 0 ||
99 	    strcmp(ia->ia_name, "silergy,syr828") == 0 ||
100 	    strcmp(ia->ia_name, "tcs,tcs4525") == 0);
101 }
102 
103 void
fanpwr_attach(struct device * parent,struct device * self,void * aux)104 fanpwr_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	struct fanpwr_softc *sc = (struct fanpwr_softc *)self;
107 	struct i2c_attach_args *ia = aux;
108 	int node = *(int *)ia->ia_cookie;
109 	uint32_t voltage, ramp_delay;
110 	uint8_t id1, id2;
111 
112 	pinctrl_byname(node, "default");
113 
114 	sc->sc_tag = ia->ia_tag;
115 	sc->sc_addr = ia->ia_addr;
116 
117 	if (OF_is_compatible(node, "rockchip,rk8602") ||
118 	    OF_is_compatible(node, "rockchip,rk8603")) {
119 		printf(": RK8602");
120 		sc->sc_id = FANPWR_RK8602;
121 	} else if (OF_is_compatible(node, "silergy,syr827")) {
122 		printf(": SYR827");
123 		sc->sc_id = FANPWR_SYR827;
124 	} else if (OF_is_compatible(node, "silergy,syr828")) {
125 		printf(": SYR828");
126 		sc->sc_id = FANPWR_SYR828;
127 	} else if (OF_is_compatible(node, "tcs,tcs4525")) {
128 		printf(": TCS4525");
129 		sc->sc_id = FANPWR_TCS4525;
130 	} else {
131 		printf(": FAN53555");
132 		sc->sc_id = FANPWR_FAN53555;
133 	}
134 
135 	if (sc->sc_id == FANPWR_TCS4525) {
136 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
137 			sc->sc_vsel = TCS4525_VSEL0;
138 		else
139 			sc->sc_vsel = TCS4525_VSEL1;
140 		sc->sc_vsel_nsel_mask = TCS4525_VSEL_NSEL_MASK;
141 	} else if (sc->sc_id == FANPWR_RK8602) {
142 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
143 			sc->sc_vsel = RK8602_VSEL0;
144 		else
145 			sc->sc_vsel = RK8602_VSEL1;
146 		sc->sc_vsel_nsel_mask = RK8602_VSEL_NSEL_MASK;
147 	} else {
148 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
149 			sc->sc_vsel = FAN53555_VSEL0;
150 		else
151 			sc->sc_vsel = FAN53555_VSEL1;
152 		sc->sc_vsel_nsel_mask = FAN53555_VSEL_NSEL_MASK;
153 	}
154 
155 	id1 = fanpwr_read(sc, FAN53555_ID1);
156 	id2 = fanpwr_read(sc, FAN53555_ID2);
157 
158 	switch (sc->sc_id) {
159 	case FANPWR_FAN53555:
160 		switch (id1 << 8 | id2) {
161 		case 0x8003:	/* 00 Option */
162 		case 0x8103:	/* 01 Option */
163 		case 0x8303:	/* 03 Option */
164 		case 0x8503:	/* 05 Option */
165 		case 0x8801:	/* 08, 18 Options */
166 		case 0x880f:	/* BUC08, BUC18 Options */
167 		case 0x8108:	/* 79 Option */
168 			sc->sc_vbase = 600000;
169 			sc->sc_vstep = 10000;
170 			break;
171 		case 0x840f:	/* 04 Option */
172 		case 0x8c0f:	/* 09 Option */
173 			sc->sc_vbase = 603000;
174 			sc->sc_vstep = 12826;
175 			break;
176 		case 0x800f:	/* 13 Option */
177 			sc->sc_vbase = 800000;
178 			sc->sc_vstep = 10000;
179 			break;
180 		case 0x800c:	/* 23 Option */
181 			sc->sc_vbase = 600000;
182 			sc->sc_vstep = 12500;
183 			break;
184 		case 0x8004:	/* 24 Option */
185 			sc->sc_vbase = 603000;
186 			sc->sc_vstep = 12967;
187 			break;
188 		default:
189 			printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2);
190 			return;
191 		}
192 		break;
193 	case FANPWR_RK8602:
194 		sc->sc_vbase = 500000;
195 		sc->sc_vstep = 6250;
196 		break;
197 	case FANPWR_SYR827:
198 	case FANPWR_SYR828:
199 		sc->sc_vbase = 712500;
200 		sc->sc_vstep = 12500;
201 		break;
202 	case FANPWR_TCS4525:
203 		sc->sc_vbase = 600000;
204 		sc->sc_vstep = 6250;
205 		break;
206 	}
207 
208 	voltage = fanpwr_get_voltage(sc);
209 	printf(", %d.%02d VDC", voltage / 1000000,
210 		    (voltage % 1000000) / 10000);
211 
212 	ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0);
213 	if (ramp_delay > 0) {
214 		if (sc->sc_id == FANPWR_TCS4525) {
215 			uint8_t ctrl, slew;
216 
217 			if (ramp_delay >= 18700)
218 				slew = 0;
219 			else if (ramp_delay >= 9300)
220 				slew = 1;
221 			else if (ramp_delay >= 4600)
222 				slew = 2;
223 			else
224 				slew = 3;
225 			ctrl = fanpwr_read(sc, TCS4525_TIME);
226 			ctrl &= ~TCS4525_TIME_SLEW_MASK;
227 			ctrl |= slew << TCS4525_TIME_SLEW_SHIFT;
228 			fanpwr_write(sc, TCS4525_TIME, ctrl);
229 		} else {
230 			uint8_t ctrl, slew;
231 
232 			for (slew = 7; slew > 0; slew--)
233 				if ((64000 >> slew) >= ramp_delay)
234 					break;
235 			ctrl = fanpwr_read(sc, FAN53555_CONTROL);
236 			ctrl &= ~FAN53555_CONTROL_SLEW_MASK;
237 			ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT;
238 			fanpwr_write(sc, FAN53555_CONTROL, ctrl);
239 		}
240 	}
241 
242 	sc->sc_rd.rd_node = node;
243 	sc->sc_rd.rd_cookie = sc;
244 	sc->sc_rd.rd_get_voltage = fanpwr_get_voltage;
245 	sc->sc_rd.rd_set_voltage = fanpwr_set_voltage;
246 	regulator_register(&sc->sc_rd);
247 
248 	printf("\n");
249 }
250 
251 uint8_t
fanpwr_read(struct fanpwr_softc * sc,int reg)252 fanpwr_read(struct fanpwr_softc *sc, int reg)
253 {
254 	uint8_t cmd = reg;
255 	uint8_t val;
256 	int error;
257 
258 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
259 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
260 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
261 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
262 
263 	if (error) {
264 		printf("error %d\n", error);
265 		printf("%s: can't read register 0x%02x\n",
266 		    sc->sc_dev.dv_xname, reg);
267 		val = 0xff;
268 	}
269 
270 	return val;
271 }
272 
273 void
fanpwr_write(struct fanpwr_softc * sc,int reg,uint8_t val)274 fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val)
275 {
276 	uint8_t cmd = reg;
277 	int error;
278 
279 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
280 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
281 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
282 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
283 
284 	if (error) {
285 		printf("%s: can't write register 0x%02x\n",
286 		    sc->sc_dev.dv_xname, reg);
287 	}
288 }
289 
290 uint32_t
fanpwr_get_voltage(void * cookie)291 fanpwr_get_voltage(void *cookie)
292 {
293 	struct fanpwr_softc *sc = cookie;
294 	uint8_t vsel;
295 
296 	vsel = fanpwr_read(sc, sc->sc_vsel);
297 	return sc->sc_vbase + (vsel & sc->sc_vsel_nsel_mask) * sc->sc_vstep;
298 }
299 
300 int
fanpwr_set_voltage(void * cookie,uint32_t voltage)301 fanpwr_set_voltage(void *cookie, uint32_t voltage)
302 {
303 	struct fanpwr_softc *sc = cookie;
304 	uint32_t vmin = sc->sc_vbase;
305 	uint32_t vmax = vmin + sc->sc_vsel_nsel_mask * sc->sc_vstep;
306 	uint8_t vsel;
307 
308 	if (voltage < vmin || voltage > vmax)
309 		return EINVAL;
310 
311 	vsel = fanpwr_read(sc, sc->sc_vsel);
312 	vsel &= ~sc->sc_vsel_nsel_mask;
313 	vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep;
314 	fanpwr_write(sc, sc->sc_vsel, vsel);
315 
316 	return 0;
317 }
318