xref: /openbsd/sys/dev/fdt/fanpwr.c (revision 097a140d)
1 /*	$OpenBSD: fanpwr.c,v 1.4 2020/11/12 10:47:07 patrick 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 
81 	return (strcmp(ia->ia_name, "fcs,fan53555") == 0 ||
82 	    strcmp(ia->ia_name, "silergy,syr827") == 0 ||
83 	    strcmp(ia->ia_name, "silergy,syr828") == 0);
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 	pinctrl_byname(node, "default");
96 
97 	sc->sc_tag = ia->ia_tag;
98 	sc->sc_addr = ia->ia_addr;
99 
100 	if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
101 		sc->sc_vsel = FAN53555_VSEL0;
102 	else
103 		sc->sc_vsel = FAN53555_VSEL1;
104 
105 	if (OF_is_compatible(node, "silergy,syr827")) {
106 		printf(": SYR827");
107 		sc->sc_id = FANPWR_SYR827;
108 	} else if (OF_is_compatible(node, "silergy,syr828")) {
109 		printf(": SYR828");
110 		sc->sc_id = FANPWR_SYR828;
111 	} else {
112 		printf(": FAN53555");
113 		sc->sc_id = FANPWR_FAN53555;
114 	}
115 
116 	id1 = fanpwr_read(sc, FAN53555_ID1);
117 	id2 = fanpwr_read(sc, FAN53555_ID2);
118 
119 	switch (sc->sc_id) {
120 	case FANPWR_FAN53555:
121 		switch (id1 << 8 | id2) {
122 		case 0x8003:	/* 00 Option */
123 		case 0x8103:	/* 01 Option */
124 		case 0x8303:	/* 03 Option */
125 		case 0x8503:	/* 05 Option */
126 		case 0x8801:	/* 08, 18 Options */
127 		case 0x880f:	/* BUC08, BUC18 Options */
128 		case 0x8108:	/* 79 Option */
129 			sc->sc_vbase = 600000;
130 			sc->sc_vstep = 10000;
131 			break;
132 		case 0x840f:	/* 04 Option */
133 		case 0x8c0f:	/* 09 Option */
134 			sc->sc_vbase = 603000;
135 			sc->sc_vstep = 12826;
136 			break;
137 		case 0x800f:	/* 13 Option */
138 			sc->sc_vbase = 800000;
139 			sc->sc_vstep = 10000;
140 			break;
141 		case 0x800c:	/* 23 Option */
142 			sc->sc_vbase = 600000;
143 			sc->sc_vstep = 12500;
144 			break;
145 		case 0x8004:	/* 24 Option */
146 			sc->sc_vbase = 603000;
147 			sc->sc_vstep = 12967;
148 			break;
149 		default:
150 			printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2);
151 			return;
152 		}
153 		break;
154 	case FANPWR_SYR827:
155 	case FANPWR_SYR828:
156 		sc->sc_vbase = 712500;
157 		sc->sc_vstep = 12500;
158 		break;
159 	}
160 
161 	voltage = fanpwr_get_voltage(sc);
162 	printf(", %d.%02d VDC", voltage / 1000000,
163 		    (voltage % 1000000) / 10000);
164 
165 	ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0);
166 	if (ramp_delay > 0) {
167 		uint8_t ctrl, slew;
168 
169 		for (slew = 7; slew > 0; slew--)
170 			if ((64000 >> slew) >= ramp_delay)
171 				break;
172 		ctrl = fanpwr_read(sc, FAN53555_CONTROL);
173 		ctrl &= ~FAN53555_CONTROL_SLEW_MASK;
174 		ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT;
175 		fanpwr_write(sc, FAN53555_CONTROL, ctrl);
176 	}
177 
178 	sc->sc_rd.rd_node = node;
179 	sc->sc_rd.rd_cookie = sc;
180 	sc->sc_rd.rd_get_voltage = fanpwr_get_voltage;
181 	sc->sc_rd.rd_set_voltage = fanpwr_set_voltage;
182 	regulator_register(&sc->sc_rd);
183 
184 	printf("\n");
185 }
186 
187 uint8_t
188 fanpwr_read(struct fanpwr_softc *sc, int reg)
189 {
190 	uint8_t cmd = reg;
191 	uint8_t val;
192 	int error;
193 
194 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
195 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
196 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
197 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
198 
199 	if (error) {
200 		printf("error %d\n", error);
201 		printf("%s: can't read register 0x%02x\n",
202 		    sc->sc_dev.dv_xname, reg);
203 		val = 0xff;
204 	}
205 
206 	return val;
207 }
208 
209 void
210 fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val)
211 {
212 	uint8_t cmd = reg;
213 	int error;
214 
215 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
216 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
217 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
218 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
219 
220 	if (error) {
221 		printf("%s: can't write register 0x%02x\n",
222 		    sc->sc_dev.dv_xname, reg);
223 	}
224 }
225 
226 uint32_t
227 fanpwr_get_voltage(void *cookie)
228 {
229 	struct fanpwr_softc *sc = cookie;
230 	uint8_t vsel;
231 
232 	vsel = fanpwr_read(sc, sc->sc_vsel);
233 	return sc->sc_vbase + (vsel & FAN53555_VSEL_NSEL_MASK) * sc->sc_vstep;
234 }
235 
236 int
237 fanpwr_set_voltage(void *cookie, uint32_t voltage)
238 {
239 	struct fanpwr_softc *sc = cookie;
240 	uint32_t vmin = sc->sc_vbase;
241 	uint32_t vmax = vmin + FAN53555_VSEL_NSEL_MASK * sc->sc_vstep;
242 	uint8_t vsel;
243 
244 	if (voltage < vmin || voltage > vmax)
245 		return EINVAL;
246 
247 	vsel = fanpwr_read(sc, sc->sc_vsel);
248 	vsel &= ~FAN53555_VSEL_NSEL_MASK;
249 	vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep;
250 	fanpwr_write(sc, sc->sc_vsel, vsel);
251 
252 	return 0;
253 }
254