xref: /freebsd/sys/arm/nvidia/as3722_regulators.c (revision fd029318)
1 /*-
2  * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/gpio.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/malloc.h>
37 #include <sys/rman.h>
38 #include <sys/sx.h>
39 
40 #include <machine/bus.h>
41 
42 #include <dev/extres/regulator/regulator.h>
43 #include <dev/gpio/gpiobusvar.h>
44 
45 #include <gnu/dts/include/dt-bindings/mfd/as3722.h>
46 
47 #include "as3722.h"
48 
49 MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator");
50 
51 #define	DIV_ROUND_UP(n,d) howmany(n, d)
52 
53 enum as3722_reg_id {
54 	AS3722_REG_ID_SD0,
55 	AS3722_REG_ID_SD1,
56 	AS3722_REG_ID_SD2,
57 	AS3722_REG_ID_SD3,
58 	AS3722_REG_ID_SD4,
59 	AS3722_REG_ID_SD5,
60 	AS3722_REG_ID_SD6,
61 	AS3722_REG_ID_LDO0,
62 	AS3722_REG_ID_LDO1,
63 	AS3722_REG_ID_LDO2,
64 	AS3722_REG_ID_LDO3,
65 	AS3722_REG_ID_LDO4,
66 	AS3722_REG_ID_LDO5,
67 	AS3722_REG_ID_LDO6,
68 	AS3722_REG_ID_LDO7,
69 	AS3722_REG_ID_LDO9,
70 	AS3722_REG_ID_LDO10,
71 	AS3722_REG_ID_LDO11,
72 };
73 
74 
75 /* Regulator HW definition. */
76 struct reg_def {
77 	intptr_t		id;		/* ID */
78 	char			*name;		/* Regulator name */
79 	char			*supply_name;	/* Source property name */
80 	uint8_t			volt_reg;
81 	uint8_t			volt_vsel_mask;
82 	uint8_t			enable_reg;
83 	uint8_t			enable_mask;
84 	uint8_t			ext_enable_reg;
85 	uint8_t			ext_enable_mask;
86 	struct regulator_range	*ranges;
87 	int			nranges;
88 };
89 
90 struct as3722_reg_sc {
91 	struct regnode		*regnode;
92 	struct as3722_softc	*base_sc;
93 	struct reg_def		*def;
94 	phandle_t		xref;
95 
96 	struct regnode_std_param *param;
97 	int 			ext_control;
98 	int	 		enable_tracking;
99 
100 	int			enable_usec;
101 };
102 
103 static struct regulator_range as3722_sd016_ranges[] = {
104 	REG_RANGE_INIT(0x00, 0x00,       0,     0),
105 	REG_RANGE_INIT(0x01, 0x5A,  610000, 10000),
106 };
107 
108 static struct regulator_range as3722_sd0_lv_ranges[] = {
109 	REG_RANGE_INIT(0x00, 0x00,       0,     0),
110 	REG_RANGE_INIT(0x01, 0x6E,  410000, 10000),
111 };
112 
113 static struct regulator_range as3722_sd_ranges[] = {
114 	REG_RANGE_INIT(0x00, 0x00,       0,     0),
115 	REG_RANGE_INIT(0x01, 0x40,  612500, 12500),
116 	REG_RANGE_INIT(0x41, 0x70, 1425000, 25000),
117 	REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000),
118 };
119 
120 static struct regulator_range as3722_ldo3_ranges[] = {
121 	REG_RANGE_INIT(0x00, 0x00,       0,     0),
122 	REG_RANGE_INIT(0x01, 0x2D,  620000, 20000),
123 };
124 
125 static struct regulator_range as3722_ldo_ranges[] = {
126 	REG_RANGE_INIT(0x00, 0x00,       0,     0),
127 	REG_RANGE_INIT(0x01, 0x24,  825000, 25000),
128 	REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000),
129 };
130 
131 static struct reg_def as3722s_def[] = {
132 	{
133 		.id = AS3722_REG_ID_SD0,
134 		.name = "sd0",
135 		.volt_reg = AS3722_SD0_VOLTAGE,
136 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
137 		.enable_reg = AS3722_SD_CONTROL,
138 		.enable_mask = AS3722_SDN_CTRL(0),
139 		.ext_enable_reg = AS3722_ENABLE_CTRL1,
140 		.ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK,
141 		.ranges = as3722_sd016_ranges,
142 		.nranges = nitems(as3722_sd016_ranges),
143 	},
144 	{
145 		.id = AS3722_REG_ID_SD1,
146 		.name = "sd1",
147 		.volt_reg = AS3722_SD1_VOLTAGE,
148 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
149 		.enable_reg = AS3722_SD_CONTROL,
150 		.enable_mask = AS3722_SDN_CTRL(1),
151 		.ext_enable_reg = AS3722_ENABLE_CTRL1,
152 		.ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK,
153 		.ranges = as3722_sd_ranges,
154 		.nranges = nitems(as3722_sd_ranges),
155 	},
156 	{
157 		.id = AS3722_REG_ID_SD2,
158 		.name = "sd2",
159 		.supply_name = "vsup-sd2",
160 		.volt_reg = AS3722_SD2_VOLTAGE,
161 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
162 		.enable_reg = AS3722_SD_CONTROL,
163 		.enable_mask = AS3722_SDN_CTRL(2),
164 		.ext_enable_reg = AS3722_ENABLE_CTRL1,
165 		.ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK,
166 		.ranges = as3722_sd_ranges,
167 		.nranges = nitems(as3722_sd_ranges),
168 	},
169 	{
170 		.id = AS3722_REG_ID_SD3,
171 		.name = "sd3",
172 		.supply_name = "vsup-sd3",
173 		.volt_reg = AS3722_SD3_VOLTAGE,
174 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
175 		.enable_reg = AS3722_SD_CONTROL,
176 		.enable_mask = AS3722_SDN_CTRL(3),
177 		.ext_enable_reg = AS3722_ENABLE_CTRL1,
178 		.ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK,
179 		.ranges = as3722_sd_ranges,
180 		.nranges = nitems(as3722_sd_ranges),
181 	},
182 	{
183 		.id = AS3722_REG_ID_SD4,
184 		.name = "sd4",
185 		.supply_name = "vsup-sd4",
186 		.volt_reg = AS3722_SD4_VOLTAGE,
187 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
188 		.enable_reg = AS3722_SD_CONTROL,
189 		.enable_mask = AS3722_SDN_CTRL(4),
190 		.ext_enable_reg = AS3722_ENABLE_CTRL2,
191 		.ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK,
192 		.ranges = as3722_sd_ranges,
193 		.nranges = nitems(as3722_sd_ranges),
194 	},
195 	{
196 		.id = AS3722_REG_ID_SD5,
197 		.name = "sd5",
198 		.supply_name = "vsup-sd5",
199 		.volt_reg = AS3722_SD5_VOLTAGE,
200 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
201 		.enable_reg = AS3722_SD_CONTROL,
202 		.enable_mask = AS3722_SDN_CTRL(5),
203 		.ext_enable_reg = AS3722_ENABLE_CTRL2,
204 		.ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK,
205 		.ranges = as3722_sd_ranges,
206 		.nranges = nitems(as3722_sd_ranges),
207 	},
208 	{
209 		.id = AS3722_REG_ID_SD6,
210 		.name = "sd6",
211 		.volt_reg = AS3722_SD6_VOLTAGE,
212 		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
213 		.enable_reg = AS3722_SD_CONTROL,
214 		.enable_mask = AS3722_SDN_CTRL(6),
215 		.ext_enable_reg = AS3722_ENABLE_CTRL2,
216 		.ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK,
217 		.ranges = as3722_sd016_ranges,
218 		.nranges = nitems(as3722_sd016_ranges),
219 	},
220 	{
221 		.id = AS3722_REG_ID_LDO0,
222 		.name = "ldo0",
223 		.supply_name = "vin-ldo0",
224 		.volt_reg = AS3722_LDO0_VOLTAGE,
225 		.volt_vsel_mask = AS3722_LDO0_VSEL_MASK,
226 		.enable_reg = AS3722_LDO_CONTROL0,
227 		.enable_mask = AS3722_LDO0_CTRL,
228 		.ext_enable_reg = AS3722_ENABLE_CTRL3,
229 		.ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK,
230 		.ranges = as3722_ldo_ranges,
231 		.nranges = nitems(as3722_ldo_ranges),
232 	},
233 	{
234 		.id = AS3722_REG_ID_LDO1,
235 		.name = "ldo1",
236 		.supply_name = "vin-ldo1-6",
237 		.volt_reg = AS3722_LDO1_VOLTAGE,
238 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
239 		.enable_reg = AS3722_LDO_CONTROL0,
240 		.enable_mask = AS3722_LDO1_CTRL,
241 		.ext_enable_reg = AS3722_ENABLE_CTRL3,
242 		.ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK,
243 		.ranges = as3722_ldo_ranges,
244 		.nranges = nitems(as3722_ldo_ranges),
245 	},
246 	{
247 		.id = AS3722_REG_ID_LDO2,
248 		.name = "ldo2",
249 		.supply_name = "vin-ldo2-5-7",
250 		.volt_reg = AS3722_LDO2_VOLTAGE,
251 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
252 		.enable_reg = AS3722_LDO_CONTROL0,
253 		.enable_mask = AS3722_LDO2_CTRL,
254 		.ext_enable_reg = AS3722_ENABLE_CTRL3,
255 		.ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK,
256 		.ranges = as3722_ldo_ranges,
257 		.nranges = nitems(as3722_ldo_ranges),
258 	},
259 	{
260 		.id = AS3722_REG_ID_LDO3,
261 		.name = "ldo3",
262 		.supply_name = "vin-ldo3-4",
263 		.volt_reg = AS3722_LDO3_VOLTAGE,
264 		.volt_vsel_mask = AS3722_LDO3_VSEL_MASK,
265 		.enable_reg = AS3722_LDO_CONTROL0,
266 		.enable_mask = AS3722_LDO3_CTRL,
267 		.ext_enable_reg = AS3722_ENABLE_CTRL3,
268 		.ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK,
269 		.ranges = as3722_ldo3_ranges,
270 		.nranges = nitems(as3722_ldo3_ranges),
271 	},
272 	{
273 		.id = AS3722_REG_ID_LDO4,
274 		.name = "ldo4",
275 		.supply_name = "vin-ldo3-4",
276 		.volt_reg = AS3722_LDO4_VOLTAGE,
277 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
278 		.enable_reg = AS3722_LDO_CONTROL0,
279 		.enable_mask = AS3722_LDO4_CTRL,
280 		.ext_enable_reg = AS3722_ENABLE_CTRL4,
281 		.ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK,
282 		.ranges = as3722_ldo_ranges,
283 		.nranges = nitems(as3722_ldo_ranges),
284 	},
285 	{
286 		.id = AS3722_REG_ID_LDO5,
287 		.name = "ldo5",
288 		.supply_name = "vin-ldo2-5-7",
289 		.volt_reg = AS3722_LDO5_VOLTAGE,
290 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
291 		.enable_reg = AS3722_LDO_CONTROL0,
292 		.enable_mask = AS3722_LDO5_CTRL,
293 		.ext_enable_reg = AS3722_ENABLE_CTRL4,
294 		.ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK,
295 		.ranges = as3722_ldo_ranges,
296 		.nranges = nitems(as3722_ldo_ranges),
297 	},
298 	{
299 		.id = AS3722_REG_ID_LDO6,
300 		.name = "ldo6",
301 		.supply_name = "vin-ldo1-6",
302 		.volt_reg = AS3722_LDO6_VOLTAGE,
303 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
304 		.enable_reg = AS3722_LDO_CONTROL0,
305 		.enable_mask = AS3722_LDO6_CTRL,
306 		.ext_enable_reg = AS3722_ENABLE_CTRL4,
307 		.ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK,
308 		.ranges = as3722_ldo_ranges,
309 		.nranges = nitems(as3722_ldo_ranges),
310 	},
311 	{
312 		.id = AS3722_REG_ID_LDO7,
313 		.name = "ldo7",
314 		.supply_name = "vin-ldo2-5-7",
315 		.volt_reg = AS3722_LDO7_VOLTAGE,
316 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
317 		.enable_reg = AS3722_LDO_CONTROL0,
318 		.enable_mask = AS3722_LDO7_CTRL,
319 		.ext_enable_reg = AS3722_ENABLE_CTRL4,
320 		.ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK,
321 		.ranges = as3722_ldo_ranges,
322 		.nranges = nitems(as3722_ldo_ranges),
323 	},
324 	{
325 		.id = AS3722_REG_ID_LDO9,
326 		.name = "ldo9",
327 		.supply_name = "vin-ldo9-10",
328 		.volt_reg = AS3722_LDO9_VOLTAGE,
329 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
330 		.enable_reg = AS3722_LDO_CONTROL1,
331 		.enable_mask = AS3722_LDO9_CTRL,
332 		.ext_enable_reg = AS3722_ENABLE_CTRL5,
333 		.ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK,
334 		.ranges = as3722_ldo_ranges,
335 		.nranges = nitems(as3722_ldo_ranges),
336 	},
337 	{
338 		.id = AS3722_REG_ID_LDO10,
339 		.name = "ldo10",
340 		.supply_name = "vin-ldo9-10",
341 		.volt_reg = AS3722_LDO10_VOLTAGE,
342 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
343 		.enable_reg = AS3722_LDO_CONTROL1,
344 		.enable_mask = AS3722_LDO10_CTRL,
345 		.ext_enable_reg = AS3722_ENABLE_CTRL5,
346 		.ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK,
347 		.ranges = as3722_ldo_ranges,
348 		.nranges = nitems(as3722_ldo_ranges),
349 	},
350 	{
351 		.id = AS3722_REG_ID_LDO11,
352 		.name = "ldo11",
353 		.supply_name = "vin-ldo11",
354 		.volt_reg = AS3722_LDO11_VOLTAGE,
355 		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
356 		.enable_reg = AS3722_LDO_CONTROL1,
357 		.enable_mask = AS3722_LDO11_CTRL,
358 		.ext_enable_reg = AS3722_ENABLE_CTRL5,
359 		.ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK,
360 		.ranges = as3722_ldo_ranges,
361 		.nranges = nitems(as3722_ldo_ranges),
362 	},
363 };
364 
365 
366 struct as3722_regnode_init_def {
367 	struct regnode_init_def	reg_init_def;
368 	int 			ext_control;
369 	int	 		enable_tracking;
370 };
371 
372 static int as3722_regnode_init(struct regnode *regnode);
373 static int as3722_regnode_enable(struct regnode *regnode, bool enable,
374     int *udelay);
375 static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
376     int max_uvolt, int *udelay);
377 static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
378 static regnode_method_t as3722_regnode_methods[] = {
379 	/* Regulator interface */
380 	REGNODEMETHOD(regnode_init,		as3722_regnode_init),
381 	REGNODEMETHOD(regnode_enable,		as3722_regnode_enable),
382 	REGNODEMETHOD(regnode_set_voltage,	as3722_regnode_set_volt),
383 	REGNODEMETHOD(regnode_get_voltage,	as3722_regnode_get_volt),
384 	REGNODEMETHOD_END
385 };
386 DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
387    sizeof(struct as3722_reg_sc), regnode_class);
388 
389 static int
390 as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel)
391 {
392 	int rv;
393 
394 	rv = RD1(sc->base_sc, sc->def->volt_reg, sel);
395 	if (rv != 0)
396 		return (rv);
397 	*sel &= sc->def->volt_vsel_mask;
398 	*sel >>= ffs(sc->def->volt_vsel_mask) - 1;
399 	return (0);
400 }
401 
402 static int
403 as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel)
404 {
405 	int rv;
406 
407 	sel <<= ffs(sc->def->volt_vsel_mask) - 1;
408 	sel &= sc->def->volt_vsel_mask;
409 
410 	rv = RM1(sc->base_sc, sc->def->volt_reg,
411 	    sc->def->volt_vsel_mask, sel);
412 	if (rv != 0)
413 		return (rv);
414 	return (rv);
415 }
416 
417 static bool
418 as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc)
419 {
420 	uint8_t val;
421 	int rv;
422 
423 	rv = RD1(sc->base_sc, AS3722_FUSE7, &val);
424 	if (rv != 0)
425 		return (rv);
426 	return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false);
427 }
428 
429 static int
430 as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl)
431 {
432 	uint8_t val;
433 	int rv;
434 
435 	val =  ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1);
436 	rv = RM1(sc->base_sc, sc->def->ext_enable_reg,
437 	    sc->def->ext_enable_mask, val);
438 	return (rv);
439 }
440 
441 static int
442 as3722_reg_enable(struct as3722_reg_sc *sc)
443 {
444 	int rv;
445 
446 	rv = RM1(sc->base_sc, sc->def->enable_reg,
447 	    sc->def->enable_mask, sc->def->enable_mask);
448 	return (rv);
449 }
450 
451 static int
452 as3722_reg_disable(struct as3722_reg_sc *sc)
453 {
454 	int rv;
455 
456 	rv = RM1(sc->base_sc, sc->def->enable_reg,
457 	    sc->def->enable_mask, 0);
458 	return (rv);
459 }
460 
461 static int
462 as3722_regnode_init(struct regnode *regnode)
463 {
464 	struct as3722_reg_sc *sc;
465 	int rv;
466 
467 	sc = regnode_get_softc(regnode);
468 
469 	sc->enable_usec = 500;
470 	if (sc->def->id == AS3722_REG_ID_SD0) {
471 		if (as3722_sd0_is_low_voltage(sc)) {
472 			sc->def->ranges = as3722_sd0_lv_ranges;
473 			sc->def->nranges = nitems(as3722_sd0_lv_ranges);
474 		}
475 		sc->enable_usec = 600;
476 	} else if (sc->def->id == AS3722_REG_ID_LDO3) {
477 		if (sc->enable_tracking) {
478 			rv = RM1(sc->base_sc, sc->def->volt_reg,
479 			    AS3722_LDO3_MODE_MASK,
480 			    AS3722_LDO3_MODE_PMOS_TRACKING);
481 			if (rv < 0) {
482 				device_printf(sc->base_sc->dev,
483 					"LDO3 tracking failed: %d\n", rv);
484 				return (rv);
485 			}
486 		}
487 	}
488 
489 	if (sc->ext_control) {
490 
491 		rv = as3722_reg_enable(sc);
492 		if (rv < 0) {
493 			device_printf(sc->base_sc->dev,
494 				"Failed to enable %s regulator: %d\n",
495 				sc->def->name, rv);
496 			return (rv);
497 		}
498 		rv = as3722_reg_extreg_setup(sc, sc->ext_control);
499 		if (rv < 0) {
500 			device_printf(sc->base_sc->dev,
501 				"%s ext control failed: %d", sc->def->name, rv);
502 			return (rv);
503 		}
504 	}
505 	return (0);
506 }
507 
508 static void
509 as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
510 struct as3722_regnode_init_def *init_def)
511 {
512 	int rv;
513 	phandle_t parent, supply_node;
514 	char prop_name[64]; /* Maximum OFW property name length. */
515 
516 	rv = regulator_parse_ofw_stdparam(sc->dev, node,
517 	    &init_def->reg_init_def);
518 
519 	rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control,
520 	    sizeof(init_def->ext_control));
521 	if (rv <= 0)
522 		init_def->ext_control = 0;
523 	if (init_def->ext_control > 3) {
524 		device_printf(sc->dev,
525 		    "Invalid value for ams,ext-control property: %d\n",
526 		    init_def->ext_control);
527 		init_def->ext_control = 0;
528 	}
529 	if (OF_hasprop(node, "ams,enable-tracking"))
530 		init_def->enable_tracking = 1;
531 
532 
533 	/* Get parent supply. */
534 	if (def->supply_name == NULL)
535 		 return;
536 
537 	parent = OF_parent(node);
538 	snprintf(prop_name, sizeof(prop_name), "%s-supply",
539 	    def->supply_name);
540 	rv = OF_getencprop(parent, prop_name, &supply_node,
541 	    sizeof(supply_node));
542 	if (rv <= 0)
543 		return;
544 	supply_node = OF_node_from_xref(supply_node);
545 	rv = OF_getprop_alloc(supply_node, "regulator-name", 1,
546 	    (void **)&init_def->reg_init_def.parent_name);
547 	if (rv <= 0)
548 		init_def->reg_init_def.parent_name = NULL;
549 }
550 
551 static struct as3722_reg_sc *
552 as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def)
553 {
554 	struct as3722_reg_sc *reg_sc;
555 	struct as3722_regnode_init_def init_def;
556 	struct regnode *regnode;
557 
558 	bzero(&init_def, sizeof(init_def));
559 
560 	as3722_fdt_parse(sc, node, def, &init_def);
561 	init_def.reg_init_def.id = def->id;
562 	init_def.reg_init_def.ofw_node = node;
563 	regnode = regnode_create(sc->dev, &as3722_regnode_class,
564 	    &init_def.reg_init_def);
565 	if (regnode == NULL) {
566 		device_printf(sc->dev, "Cannot create regulator.\n");
567 		return (NULL);
568 	}
569 	reg_sc = regnode_get_softc(regnode);
570 
571 	/* Init regulator softc. */
572 	reg_sc->regnode = regnode;
573 	reg_sc->base_sc = sc;
574 	reg_sc->def = def;
575 	reg_sc->xref = OF_xref_from_node(node);
576 
577 	reg_sc->param = regnode_get_stdparam(regnode);
578 	reg_sc->ext_control = init_def.ext_control;
579 	reg_sc->enable_tracking = init_def.enable_tracking;
580 
581 	regnode_register(regnode);
582 	if (bootverbose) {
583 		int volt, rv;
584 		regnode_topo_slock();
585 		rv = regnode_get_voltage(regnode, &volt);
586 		if (rv == ENODEV) {
587 			device_printf(sc->dev,
588 			   " Regulator %s: parent doesn't exist yet.\n",
589 			   regnode_get_name(regnode));
590 		} else if (rv != 0) {
591 			device_printf(sc->dev,
592 			   " Regulator %s: voltage: INVALID!!!\n",
593 			   regnode_get_name(regnode));
594 		} else {
595 			device_printf(sc->dev,
596 			    " Regulator %s: voltage: %d uV\n",
597 			    regnode_get_name(regnode), volt);
598 		}
599 		regnode_topo_unlock();
600 	}
601 
602 	return (reg_sc);
603 }
604 
605 int
606 as3722_regulator_attach(struct as3722_softc *sc, phandle_t node)
607 {
608 	struct as3722_reg_sc *reg;
609 	phandle_t child, rnode;
610 	int i;
611 
612 	rnode = ofw_bus_find_child(node, "regulators");
613 	if (rnode <= 0) {
614 		device_printf(sc->dev, " Cannot find regulators subnode\n");
615 		return (ENXIO);
616 	}
617 
618 	sc->nregs = nitems(as3722s_def);
619 	sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs,
620 	    M_AS3722_REG, M_WAITOK | M_ZERO);
621 
622 
623 	/* Attach all known regulators if exist in DT. */
624 	for (i = 0; i < sc->nregs; i++) {
625 		child = ofw_bus_find_child(rnode, as3722s_def[i].name);
626 		if (child == 0) {
627 			if (bootverbose)
628 				device_printf(sc->dev,
629 				    "Regulator %s missing in DT\n",
630 				    as3722s_def[i].name);
631 			continue;
632 		}
633 		reg = as3722_attach(sc, child, as3722s_def + i);
634 		if (reg == NULL) {
635 			device_printf(sc->dev, "Cannot attach regulator: %s\n",
636 			    as3722s_def[i].name);
637 			return (ENXIO);
638 		}
639 		sc->regs[i] = reg;
640 	}
641 	return (0);
642 }
643 
644 int
645 as3722_regulator_map(device_t dev, phandle_t xref, int ncells,
646     pcell_t *cells, int *num)
647 {
648 	struct as3722_softc *sc;
649 	int i;
650 
651 	sc = device_get_softc(dev);
652 	for (i = 0; i < sc->nregs; i++) {
653 		if (sc->regs[i] == NULL)
654 			continue;
655 		if (sc->regs[i]->xref == xref) {
656 			*num = sc->regs[i]->def->id;
657 			return (0);
658 		}
659 	}
660 	return (ENXIO);
661 }
662 
663 static int
664 as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay)
665 {
666 	struct as3722_reg_sc *sc;
667 	int rv;
668 
669 	sc = regnode_get_softc(regnode);
670 
671 	if (val)
672 		rv = as3722_reg_enable(sc);
673 	else
674 		rv = as3722_reg_disable(sc);
675 	*udelay = sc->enable_usec;
676 	return (rv);
677 }
678 
679 static int
680 as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt,
681     int *udelay)
682 {
683 	struct as3722_reg_sc *sc;
684 	uint8_t sel;
685 	int rv;
686 
687 	sc = regnode_get_softc(regnode);
688 
689 	*udelay = 0;
690 	rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges,
691 	    min_uvolt, max_uvolt, &sel);
692 	if (rv != 0)
693 		return (rv);
694 	rv = as3722_write_sel(sc, sel);
695 	return (rv);
696 
697 }
698 
699 static int
700 as3722_regnode_get_volt(struct regnode *regnode, int *uvolt)
701 {
702 	struct as3722_reg_sc *sc;
703 	uint8_t sel;
704 	int rv;
705 
706 	sc = regnode_get_softc(regnode);
707 	rv = as3722_read_sel(sc, &sel);
708 	if (rv != 0)
709 		return (rv);
710 
711 	/* LDO6 have bypass. */
712 	if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS)
713 		return (ENOENT);
714 	rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges,
715 	    sel, uvolt);
716 	return (rv);
717 }
718