fan53555.c revision 1.6 1 /* $NetBSD: fan53555.c,v 1.6 2021/01/17 21:56:20 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: fan53555.c,v 1.6 2021/01/17 21:56:20 thorpej Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/conf.h>
37 #include <sys/bus.h>
38 #include <sys/kmem.h>
39
40 #include <dev/i2c/i2cvar.h>
41
42 #include <dev/fdt/fdtvar.h>
43
44 #define VSEL0_REG 0x00
45 #define VSEL1_REG 0x01
46 #define VSEL_BUCK_EN __BIT(7)
47 #define VSEL_MODE __BIT(6)
48 #define VSEL_NSEL __BITS(5,0)
49 #define CONTROL_REG 0x02
50 #define CONTROL_OUTPUT_DISCHARGE __BIT(7)
51 #define CONTROL_SLEW __BITS(6,4)
52 #define CONTROL_RESET __BIT(2)
53 #define ID1_REG 0x03
54 #define ID1_VENDOR __BITS(7,5)
55 #define ID1_DIE_ID __BITS(3,0)
56 #define SILERGY_DIE_ID_SYR82X 8
57 #define SILERGY_DIE_ID_SYR83X 9
58 #define ID2_REG 0x04
59 #define ID2_DIE_REV __BITS(3,0)
60 #define MONITOR_REG 0x05
61 #define MONITOR_PGOOD __BIT(7)
62
63 struct fan53555_softc {
64 device_t sc_dev;
65 i2c_tag_t sc_i2c;
66 i2c_addr_t sc_addr;
67 int sc_phandle;
68
69 u_int sc_suspend_reg;
70 u_int sc_runtime_reg;
71
72 u_int sc_base;
73 u_int sc_step;
74
75 u_int sc_ramp_delay;
76 u_int sc_suspend_voltage_selector;
77 };
78
79 enum fan53555_vendor {
80 FAN_VENDOR_FAIRCHILD = 1,
81 FAN_VENDOR_SILERGY,
82 };
83
84 /*
85 * Transition slew rates.
86 * Array index is reg value, value is slew rate in uV / us
87 */
88 static const int fan53555_slew_rates[] = {
89 64000, 32000, 16000, 8000, 4000, 2000, 1000, 500
90 };
91
92 static const struct device_compatible_entry compat_data[] = {
93 { .compat = "silergy,syr827", .value = FAN_VENDOR_SILERGY },
94 { .compat = "silergy,syr828", .value = FAN_VENDOR_SILERGY },
95
96 { 0 }
97 };
98
99 static uint8_t
100 fan53555_read(struct fan53555_softc *sc, uint8_t reg, int flags)
101 {
102 uint8_t val = 0;
103 int error;
104
105 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr,
106 ®, 1, &val, 1, flags);
107 if (error != 0)
108 aprint_error_dev(sc->sc_dev, "error reading reg %#x: %d\n", reg, error);
109
110 return val;
111 }
112
113 static void
114 fan53555_write(struct fan53555_softc *sc, uint8_t reg, uint8_t val, int flags)
115 {
116 uint8_t buf[2];
117 int error;
118
119 buf[0] = reg;
120 buf[1] = val;
121
122 error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
123 NULL, 0, buf, 2, flags);
124 if (error != 0)
125 aprint_error_dev(sc->sc_dev, "error writing reg %#x: %d\n", reg, error);
126 }
127
128 #define I2C_READ(sc, reg) fan53555_read((sc), (reg), 0)
129 #define I2C_WRITE(sc, reg, val) fan53555_write((sc), (reg), (val), 0)
130 #define I2C_LOCK(sc) iic_acquire_bus((sc)->sc_i2c, 0)
131 #define I2C_UNLOCK(sc) iic_release_bus((sc)->sc_i2c, 0)
132
133 static int
134 fan53555_acquire(device_t dev)
135 {
136 return 0;
137 }
138
139 static void
140 fan53555_release(device_t dev)
141 {
142 }
143
144 static int
145 fan53555_enable(device_t dev, bool enable)
146 {
147 struct fan53555_softc * const sc = device_private(dev);
148 uint8_t val;
149
150 I2C_LOCK(sc);
151 val = I2C_READ(sc, sc->sc_runtime_reg);
152 if (enable)
153 val |= VSEL_BUCK_EN;
154 else
155 val &= ~VSEL_BUCK_EN;
156 I2C_WRITE(sc, sc->sc_runtime_reg, val);
157 I2C_UNLOCK(sc);
158
159 if (sc->sc_ramp_delay)
160 delay(sc->sc_ramp_delay);
161
162 return 0;
163 }
164
165 static int
166 fan53555_set_voltage(device_t dev, u_int min_uvol, u_int max_uvol)
167 {
168 struct fan53555_softc * const sc = device_private(dev);
169 uint8_t val, oval;
170 u_int cur_uvol;
171
172 I2C_LOCK(sc);
173
174 /* Get current voltage */
175 oval = I2C_READ(sc, sc->sc_runtime_reg);
176 cur_uvol = __SHIFTOUT(oval, VSEL_NSEL) * sc->sc_step + sc->sc_base;
177
178 /* Set new voltage */
179 val = oval & ~VSEL_NSEL;
180 val |= __SHIFTIN((min_uvol - sc->sc_base) / sc->sc_step, VSEL_NSEL);
181 I2C_WRITE(sc, sc->sc_runtime_reg, val);
182
183 I2C_UNLOCK(sc);
184
185 /* Time to delay is based on the number of voltage steps */
186 if (sc->sc_ramp_delay)
187 delay((abs(cur_uvol - min_uvol) / sc->sc_step) * sc->sc_ramp_delay);
188
189 return 0;
190 }
191
192 static int
193 fan53555_get_voltage(device_t dev, u_int *puvol)
194 {
195 struct fan53555_softc * const sc = device_private(dev);
196 uint8_t val;
197
198 I2C_LOCK(sc);
199 val = I2C_READ(sc, sc->sc_runtime_reg);
200 I2C_UNLOCK(sc);
201
202 *puvol = __SHIFTOUT(val, VSEL_NSEL) * sc->sc_step + sc->sc_base;
203
204 return 0;
205 }
206
207 static struct fdtbus_regulator_controller_func fan53555_funcs = {
208 .acquire = fan53555_acquire,
209 .release = fan53555_release,
210 .enable = fan53555_enable,
211 .set_voltage = fan53555_set_voltage,
212 .get_voltage = fan53555_get_voltage,
213 };
214
215 static int
216 fan53555_init(struct fan53555_softc *sc, enum fan53555_vendor vendor)
217 {
218 uint8_t id1, control;
219 int n;
220
221 I2C_LOCK(sc);
222 id1 = I2C_READ(sc, ID1_REG);
223 I2C_UNLOCK(sc);
224
225 const u_int die_id = __SHIFTOUT(id1, ID1_DIE_ID);
226
227 aprint_naive("\n");
228 switch (vendor) {
229 case FAN_VENDOR_SILERGY:
230 switch (die_id) {
231 case SILERGY_DIE_ID_SYR82X:
232 aprint_normal(": Silergy SYR82X\n");
233 sc->sc_base = 712500;
234 sc->sc_step = 12500;
235 break;
236 case SILERGY_DIE_ID_SYR83X:
237 aprint_normal(": Silergy SYR83X\n");
238 sc->sc_base = 712500;
239 sc->sc_step = 12500;
240 break;
241 default:
242 aprint_error(": Unsupported Silergy chip (0x%x)\n", die_id);
243 return ENXIO;
244 }
245 break;
246 default:
247 aprint_error(": Unsupported vendor (%d)\n", vendor);
248 return ENXIO;
249 }
250
251 sc->sc_suspend_voltage_selector = -1;
252 of_getprop_uint32(sc->sc_phandle, "fcs,suspend-voltage-selector",
253 &sc->sc_suspend_voltage_selector);
254 switch (sc->sc_suspend_voltage_selector) {
255 case 0:
256 sc->sc_suspend_reg = VSEL0_REG;
257 sc->sc_runtime_reg = VSEL1_REG;
258 break;
259 case 1:
260 sc->sc_suspend_reg = VSEL1_REG;
261 sc->sc_runtime_reg = VSEL0_REG;
262 break;
263 default:
264 aprint_error_dev(sc->sc_dev, "unsupported 'fcs,suspend-voltage-selector' value %u\n", sc->sc_suspend_voltage_selector);
265 return EINVAL;
266 }
267
268 of_getprop_uint32(sc->sc_phandle, "regulator-ramp-delay",
269 &sc->sc_ramp_delay);
270 if (sc->sc_ramp_delay) {
271 for (n = 0; n < __arraycount(fan53555_slew_rates); n++)
272 if (sc->sc_ramp_delay > fan53555_slew_rates[n])
273 break;
274 if (n == __arraycount(fan53555_slew_rates)) {
275 aprint_error_dev(sc->sc_dev, "unsupported 'regulator-ramp-delay' value %u\n", sc->sc_ramp_delay);
276 return EINVAL;
277 }
278 I2C_LOCK(sc);
279 control = I2C_READ(sc, CONTROL_REG);
280 control &= ~CONTROL_SLEW;
281 control |= __SHIFTIN(fan53555_slew_rates[n], CONTROL_SLEW);
282 I2C_WRITE(sc, CONTROL_REG, control);
283 I2C_UNLOCK(sc);
284 }
285
286 return 0;
287 }
288
289 static int
290 fan53555_match(device_t parent, cfdata_t match, void *aux)
291 {
292 struct i2c_attach_args *ia = aux;
293 int match_result;
294
295 if (iic_use_direct_match(ia, match, compat_data, &match_result))
296 return match_result;
297
298 return 0;
299 }
300
301 static void
302 fan53555_attach(device_t parent, device_t self, void *aux)
303 {
304 struct fan53555_softc * const sc = device_private(self);
305 const struct device_compatible_entry *compat;
306 struct i2c_attach_args *ia = aux;
307
308 sc->sc_dev = self;
309 sc->sc_i2c = ia->ia_tag;
310 sc->sc_addr = ia->ia_addr;
311 sc->sc_phandle = ia->ia_cookie;
312
313 iic_compatible_match(ia, compat_data, &compat);
314 KASSERT(compat != NULL);
315
316 if (fan53555_init(sc, compat->value) != 0)
317 return;
318
319 fdtbus_register_regulator_controller(self, sc->sc_phandle,
320 &fan53555_funcs);
321 }
322
323 CFATTACH_DECL_NEW(fan53555reg, sizeof(struct fan53555_softc),
324 fan53555_match, fan53555_attach, NULL, NULL);
325