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: stable/11/sys/arm/nvidia/as3722_regulators.c 308328 2016-11-05 04:40:58Z mmel $");
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
49MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator");
50
51#define	DIV_ROUND_UP(n,d) howmany(n, d)
52
53enum 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. */
76struct 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
90struct 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
103static struct regulator_range as3722_sd016_ranges[] = {
104	REG_RANGE_INIT(0x00, 0x00,       0,     0),
105	REG_RANGE_INIT(0x01, 0x5A,  610000, 10000),
106};
107
108static 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
113static 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
120static struct regulator_range as3722_ldo3_ranges[] = {
121	REG_RANGE_INIT(0x00, 0x00,       0,     0),
122	REG_RANGE_INIT(0x01, 0x2D,  620000, 20000),
123};
124
125static 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
131static 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
366struct as3722_regnode_init_def {
367	struct regnode_init_def	reg_init_def;
368	int 			ext_control;
369	int	 		enable_tracking;
370};
371
372static int as3722_regnode_init(struct regnode *regnode);
373static int as3722_regnode_enable(struct regnode *regnode, bool enable,
374    int *udelay);
375static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
376    int max_uvolt, int *udelay);
377static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
378static 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};
386DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
387   sizeof(struct as3722_reg_sc), regnode_class);
388
389static int
390as3722_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
402static int
403as3722_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
417static bool
418as3722_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
429static int
430as3722_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
441static int
442as3722_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
451static int
452as3722_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
461static int
462as3722_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
508static void
509as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
510struct 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
551static struct as3722_reg_sc *
552as3722_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
605int
606as3722_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
644int
645as3722_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
663static int
664as3722_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
679static int
680as3722_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
699static int
700as3722_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