as3722_regulators.c revision 296936
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: head/sys/arm/nvidia/as3722_regulators.c 296936 2016-03-16 13:01:48Z 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) (((n) + (d) - 1) / (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
74struct 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. */
83struct 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
97struct 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
118static struct regulator_range as3722_sd016_ranges[] = {
119	RANGE_INIT(0x00, 0x00,       0,     0),
120	RANGE_INIT(0x01, 0x5A,  610000, 10000),
121};
122
123static struct regulator_range as3722_sd0_lv_ranges[] = {
124	RANGE_INIT(0x00, 0x00,       0,     0),
125	RANGE_INIT(0x01, 0x6E,  410000, 10000),
126};
127
128static 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
135static struct regulator_range as3722_ldo3_ranges[] = {
136	RANGE_INIT(0x00, 0x00,       0,     0),
137	RANGE_INIT(0x01, 0x2D,  620000, 20000),
138};
139
140static 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
146static 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
381struct as3722_regnode_init_def {
382	struct regnode_init_def	reg_init_def;
383	int 			ext_control;
384	int	 		enable_tracking;
385};
386
387static int as3722_regnode_init(struct regnode *regnode);
388static int as3722_regnode_enable(struct regnode *regnode, bool enable,
389    int *udelay);
390static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
391    int max_uvolt, int *udelay);
392static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
393static 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};
401DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
402   sizeof(struct as3722_reg_sc), regnode_class);
403
404static int
405regulator_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
431static int
432regulator_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
485static int
486as3722_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
498static int
499as3722_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
513static bool
514as3722_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
525static int
526as3722_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
537static int
538as3722_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
547static int
548as3722_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
557static int
558as3722_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
604static void
605as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
606struct 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
647static struct as3722_reg_sc *
648as3722_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
701int
702as3722_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
740int
741as3722_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
759static int
760as3722_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
775static int
776as3722_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
794static int
795as3722_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