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