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