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