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