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