xref: /openbsd-src/sys/dev/fdt/fanpwr.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /*	$OpenBSD: fanpwr.c,v 1.2 2018/07/31 10:07:13 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_regulator.h>
25 #include <dev/ofw/fdt.h>
26 
27 #include <dev/i2c/i2cvar.h>
28 
29 /* Registers */
30 #define FAN53555_VSEL0			0x00
31 #define FAN53555_VSEL1			0x01
32 #define  FAN53555_VSEL_NSEL_MASK	0x3f
33 #define FAN53555_CONTROL		0x02
34 #define  FAN53555_CONTROL_SLEW_MASK	(0x7 << 4)
35 #define  FAN53555_CONTROL_SLEW_SHIFT	4
36 #define FAN53555_ID1			0x03
37 #define FAN53555_ID2			0x04
38 
39 /* Distinguish between Failrchild original and Silergy clones. */
40 enum fanpwr_id {
41 	FANPWR_FAN53555,	/* Fairchild FAN53555 */
42 	FANPWR_SYR827,		/* Silergy SYR827 */
43 	FANPWR_SYR828		/* Silergy SYR828 */
44 };
45 
46 struct fanpwr_softc {
47 	struct device	sc_dev;
48 	i2c_tag_t	sc_tag;
49 	i2c_addr_t	sc_addr;
50 
51 	enum fanpwr_id	sc_id;
52 	uint8_t		sc_vsel;
53 
54 	struct regulator_device sc_rd;
55 	uint32_t	sc_vbase;
56 	uint32_t	sc_vstep;
57 };
58 
59 int	fanpwr_match(struct device *, void *, void *);
60 void	fanpwr_attach(struct device *, struct device *, void *);
61 
62 struct cfattach fanpwr_ca = {
63 	sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach
64 };
65 
66 struct cfdriver fanpwr_cd = {
67 	NULL, "fanpwr", DV_DULL
68 };
69 
70 uint8_t	fanpwr_read(struct fanpwr_softc *, int);
71 void	fanpwr_write(struct fanpwr_softc *, int, uint8_t);
72 uint32_t fanpwr_get_voltage(void *);
73 int	fanpwr_set_voltage(void *, uint32_t);
74 
75 int
76 fanpwr_match(struct device *parent, void *match, void *aux)
77 {
78 	struct i2c_attach_args *ia = aux;
79 	int node = *(int *)ia->ia_cookie;
80 
81 	return (OF_is_compatible(node, "fcs,fan53555") ||
82 	    OF_is_compatible(node, "silergy,syr827") ||
83 	    OF_is_compatible(node, "silergy,syr828"));
84 }
85 
86 void
87 fanpwr_attach(struct device *parent, struct device *self, void *aux)
88 {
89 	struct fanpwr_softc *sc = (struct fanpwr_softc *)self;
90 	struct i2c_attach_args *ia = aux;
91 	int node = *(int *)ia->ia_cookie;
92 	uint32_t voltage, ramp_delay;
93 	uint8_t id1, id2;
94 
95 	sc->sc_tag = ia->ia_tag;
96 	sc->sc_addr = ia->ia_addr;
97 
98 	if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
99 		sc->sc_vsel = FAN53555_VSEL0;
100 	else
101 		sc->sc_vsel = FAN53555_VSEL1;
102 
103 	if (OF_is_compatible(node, "silergy,syr827")) {
104 		printf(": SYR827");
105 		sc->sc_id = FANPWR_SYR827;
106 	} else if (OF_is_compatible(node, "silergy,syr828")) {
107 		printf(": SYR828");
108 		sc->sc_id = FANPWR_SYR828;
109 	} else {
110 		printf(": FAN53555");
111 		sc->sc_id = FANPWR_FAN53555;
112 	}
113 
114 	id1 = fanpwr_read(sc, FAN53555_ID1);
115 	id2 = fanpwr_read(sc, FAN53555_ID2);
116 
117 	switch (sc->sc_id) {
118 	case FANPWR_FAN53555:
119 		switch (id1 << 8 | id2) {
120 		case 0x8003:	/* 00 Option */
121 		case 0x8103:	/* 01 Option */
122 		case 0x8303:	/* 03 Option */
123 		case 0x8503:	/* 05 Option */
124 		case 0x8801:	/* 08, 18 Options */
125 		case 0x880f:	/* BUC08, BUC18 Options */
126 		case 0x8108:	/* 79 Option */
127 			sc->sc_vbase = 600000;
128 			sc->sc_vstep = 10000;
129 			break;
130 		case 0x840f:	/* 04 Option */
131 		case 0x8c0f:	/* 09 Option */
132 			sc->sc_vbase = 603000;
133 			sc->sc_vstep = 12826;
134 			break;
135 		case 0x800f:	/* 13 Option */
136 			sc->sc_vbase = 800000;
137 			sc->sc_vstep = 10000;
138 			break;
139 		case 0x800c:	/* 23 Option */
140 			sc->sc_vbase = 600000;
141 			sc->sc_vstep = 12500;
142 			break;
143 		case 0x8004:	/* 24 Option */
144 			sc->sc_vbase = 603000;
145 			sc->sc_vstep = 12967;
146 			break;
147 		default:
148 			printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2);
149 			return;
150 		}
151 		break;
152 	case FANPWR_SYR827:
153 	case FANPWR_SYR828:
154 		sc->sc_vbase = 712500;
155 		sc->sc_vstep = 12500;
156 		break;
157 	}
158 
159 	voltage = fanpwr_get_voltage(sc);
160 	printf(", %d.%02d VDC", voltage / 1000000,
161 		    (voltage % 1000000) / 10000);
162 
163 	ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0);
164 	if (ramp_delay > 0) {
165 		uint8_t ctrl, slew;
166 
167 		for (slew = 7; slew > 0; slew--)
168 			if ((64000 >> slew) >= ramp_delay)
169 				break;
170 		ctrl = fanpwr_read(sc, FAN53555_CONTROL);
171 		ctrl &= ~FAN53555_CONTROL_SLEW_MASK;
172 		ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT;
173 		fanpwr_write(sc, FAN53555_CONTROL, ctrl);
174 	}
175 
176 	sc->sc_rd.rd_node = node;
177 	sc->sc_rd.rd_cookie = sc;
178 	sc->sc_rd.rd_get_voltage = fanpwr_get_voltage;
179 	sc->sc_rd.rd_set_voltage = fanpwr_set_voltage;
180 	regulator_register(&sc->sc_rd);
181 
182 	printf("\n");
183 }
184 
185 uint8_t
186 fanpwr_read(struct fanpwr_softc *sc, int reg)
187 {
188 	uint8_t cmd = reg;
189 	uint8_t val;
190 	int error;
191 
192 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
193 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
194 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
195 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
196 
197 	if (error) {
198 		printf("error %d\n", error);
199 		printf("%s: can't read register 0x%02x\n",
200 		    sc->sc_dev.dv_xname, reg);
201 		val = 0xff;
202 	}
203 
204 	return val;
205 }
206 
207 void
208 fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val)
209 {
210 	uint8_t cmd = reg;
211 	int error;
212 
213 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
214 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
215 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
216 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
217 
218 	if (error) {
219 		printf("%s: can't write register 0x%02x\n",
220 		    sc->sc_dev.dv_xname, reg);
221 	}
222 }
223 
224 uint32_t
225 fanpwr_get_voltage(void *cookie)
226 {
227 	struct fanpwr_softc *sc = cookie;
228 	uint8_t vsel;
229 
230 	vsel = fanpwr_read(sc, sc->sc_vsel);
231 	return sc->sc_vbase + (vsel & FAN53555_VSEL_NSEL_MASK) * sc->sc_vstep;
232 }
233 
234 int
235 fanpwr_set_voltage(void *cookie, uint32_t voltage)
236 {
237 	struct fanpwr_softc *sc = cookie;
238 	uint32_t vmin = sc->sc_vbase;
239 	uint32_t vmax = vmin + FAN53555_VSEL_NSEL_MASK * sc->sc_vstep;
240 	uint8_t vsel;
241 
242 	if (voltage < vmin || voltage > vmax)
243 		return EINVAL;
244 
245 	vsel = fanpwr_read(sc, sc->sc_vsel);
246 	vsel &= ~FAN53555_VSEL_NSEL_MASK;
247 	vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep;
248 	fanpwr_write(sc, sc->sc_vsel, vsel);
249 
250 	return 0;
251 }
252