1296936Smmel/*-
2296936Smmel * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_regulators.c 308328 2016-11-05 04:40:58Z mmel $");
29296936Smmel
30296936Smmel#include <sys/param.h>
31296936Smmel#include <sys/systm.h>
32296936Smmel#include <sys/bus.h>
33296936Smmel#include <sys/gpio.h>
34296936Smmel#include <sys/kernel.h>
35296936Smmel#include <sys/module.h>
36296936Smmel#include <sys/malloc.h>
37296936Smmel#include <sys/rman.h>
38296936Smmel#include <sys/sx.h>
39296936Smmel
40296936Smmel#include <machine/bus.h>
41296936Smmel
42296936Smmel#include <dev/extres/regulator/regulator.h>
43296936Smmel#include <dev/gpio/gpiobusvar.h>
44296936Smmel
45296936Smmel#include <gnu/dts/include/dt-bindings/mfd/as3722.h>
46296936Smmel
47296936Smmel#include "as3722.h"
48296936Smmel
49296936SmmelMALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator");
50296936Smmel
51298643Spfg#define	DIV_ROUND_UP(n,d) howmany(n, d)
52296936Smmel
53296936Smmelenum as3722_reg_id {
54296936Smmel	AS3722_REG_ID_SD0,
55296936Smmel	AS3722_REG_ID_SD1,
56296936Smmel	AS3722_REG_ID_SD2,
57296936Smmel	AS3722_REG_ID_SD3,
58296936Smmel	AS3722_REG_ID_SD4,
59296936Smmel	AS3722_REG_ID_SD5,
60296936Smmel	AS3722_REG_ID_SD6,
61296936Smmel	AS3722_REG_ID_LDO0,
62296936Smmel	AS3722_REG_ID_LDO1,
63296936Smmel	AS3722_REG_ID_LDO2,
64296936Smmel	AS3722_REG_ID_LDO3,
65296936Smmel	AS3722_REG_ID_LDO4,
66296936Smmel	AS3722_REG_ID_LDO5,
67296936Smmel	AS3722_REG_ID_LDO6,
68296936Smmel	AS3722_REG_ID_LDO7,
69296936Smmel	AS3722_REG_ID_LDO9,
70296936Smmel	AS3722_REG_ID_LDO10,
71296936Smmel	AS3722_REG_ID_LDO11,
72296936Smmel};
73296936Smmel
74296936Smmel
75296936Smmel/* Regulator HW definition. */
76296936Smmelstruct reg_def {
77296936Smmel	intptr_t		id;		/* ID */
78296936Smmel	char			*name;		/* Regulator name */
79296936Smmel	char			*supply_name;	/* Source property name */
80296936Smmel	uint8_t			volt_reg;
81296936Smmel	uint8_t			volt_vsel_mask;
82296936Smmel	uint8_t			enable_reg;
83296936Smmel	uint8_t			enable_mask;
84296936Smmel	uint8_t			ext_enable_reg;
85296936Smmel	uint8_t			ext_enable_mask;
86296936Smmel	struct regulator_range	*ranges;
87296936Smmel	int			nranges;
88296936Smmel};
89296936Smmel
90296936Smmelstruct as3722_reg_sc {
91296936Smmel	struct regnode		*regnode;
92296936Smmel	struct as3722_softc	*base_sc;
93296936Smmel	struct reg_def		*def;
94296936Smmel	phandle_t		xref;
95296936Smmel
96296936Smmel	struct regnode_std_param *param;
97296936Smmel	int 			ext_control;
98296936Smmel	int	 		enable_tracking;
99296936Smmel
100296936Smmel	int			enable_usec;
101296936Smmel};
102296936Smmel
103296936Smmelstatic struct regulator_range as3722_sd016_ranges[] = {
104308328Smmel	REG_RANGE_INIT(0x00, 0x00,       0,     0),
105308328Smmel	REG_RANGE_INIT(0x01, 0x5A,  610000, 10000),
106296936Smmel};
107296936Smmel
108296936Smmelstatic struct regulator_range as3722_sd0_lv_ranges[] = {
109308328Smmel	REG_RANGE_INIT(0x00, 0x00,       0,     0),
110308328Smmel	REG_RANGE_INIT(0x01, 0x6E,  410000, 10000),
111296936Smmel};
112296936Smmel
113296936Smmelstatic struct regulator_range as3722_sd_ranges[] = {
114308328Smmel	REG_RANGE_INIT(0x00, 0x00,       0,     0),
115308328Smmel	REG_RANGE_INIT(0x01, 0x40,  612500, 12500),
116308328Smmel	REG_RANGE_INIT(0x41, 0x70, 1425000, 25000),
117308328Smmel	REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000),
118296936Smmel};
119296936Smmel
120296936Smmelstatic struct regulator_range as3722_ldo3_ranges[] = {
121308328Smmel	REG_RANGE_INIT(0x00, 0x00,       0,     0),
122308328Smmel	REG_RANGE_INIT(0x01, 0x2D,  620000, 20000),
123296936Smmel};
124296936Smmel
125296936Smmelstatic struct regulator_range as3722_ldo_ranges[] = {
126308328Smmel	REG_RANGE_INIT(0x00, 0x00,       0,     0),
127308328Smmel	REG_RANGE_INIT(0x01, 0x24,  825000, 25000),
128308328Smmel	REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000),
129296936Smmel};
130296936Smmel
131296936Smmelstatic struct reg_def as3722s_def[] = {
132296936Smmel	{
133296936Smmel		.id = AS3722_REG_ID_SD0,
134296936Smmel		.name = "sd0",
135296936Smmel		.volt_reg = AS3722_SD0_VOLTAGE,
136296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
137296936Smmel		.enable_reg = AS3722_SD_CONTROL,
138296936Smmel		.enable_mask = AS3722_SDN_CTRL(0),
139296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL1,
140296936Smmel		.ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK,
141296936Smmel		.ranges = as3722_sd016_ranges,
142296936Smmel		.nranges = nitems(as3722_sd016_ranges),
143296936Smmel	},
144296936Smmel	{
145296936Smmel		.id = AS3722_REG_ID_SD1,
146296936Smmel		.name = "sd1",
147296936Smmel		.volt_reg = AS3722_SD1_VOLTAGE,
148296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
149296936Smmel		.enable_reg = AS3722_SD_CONTROL,
150296936Smmel		.enable_mask = AS3722_SDN_CTRL(1),
151296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL1,
152296936Smmel		.ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK,
153296936Smmel		.ranges = as3722_sd_ranges,
154296936Smmel		.nranges = nitems(as3722_sd_ranges),
155296936Smmel	},
156296936Smmel	{
157296936Smmel		.id = AS3722_REG_ID_SD2,
158296936Smmel		.name = "sd2",
159296936Smmel		.supply_name = "vsup-sd2",
160296936Smmel		.volt_reg = AS3722_SD2_VOLTAGE,
161296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
162296936Smmel		.enable_reg = AS3722_SD_CONTROL,
163296936Smmel		.enable_mask = AS3722_SDN_CTRL(2),
164296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL1,
165296936Smmel		.ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK,
166296936Smmel		.ranges = as3722_sd_ranges,
167296936Smmel		.nranges = nitems(as3722_sd_ranges),
168296936Smmel	},
169296936Smmel	{
170296936Smmel		.id = AS3722_REG_ID_SD3,
171296936Smmel		.name = "sd3",
172296936Smmel		.supply_name = "vsup-sd3",
173296936Smmel		.volt_reg = AS3722_SD3_VOLTAGE,
174296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
175296936Smmel		.enable_reg = AS3722_SD_CONTROL,
176296936Smmel		.enable_mask = AS3722_SDN_CTRL(3),
177296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL1,
178296936Smmel		.ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK,
179296936Smmel		.ranges = as3722_sd_ranges,
180296936Smmel		.nranges = nitems(as3722_sd_ranges),
181296936Smmel	},
182296936Smmel	{
183296936Smmel		.id = AS3722_REG_ID_SD4,
184296936Smmel		.name = "sd4",
185296936Smmel		.supply_name = "vsup-sd4",
186296936Smmel		.volt_reg = AS3722_SD4_VOLTAGE,
187296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
188296936Smmel		.enable_reg = AS3722_SD_CONTROL,
189296936Smmel		.enable_mask = AS3722_SDN_CTRL(4),
190296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL2,
191296936Smmel		.ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK,
192296936Smmel		.ranges = as3722_sd_ranges,
193296936Smmel		.nranges = nitems(as3722_sd_ranges),
194296936Smmel	},
195296936Smmel	{
196296936Smmel		.id = AS3722_REG_ID_SD5,
197296936Smmel		.name = "sd5",
198296936Smmel		.supply_name = "vsup-sd5",
199296936Smmel		.volt_reg = AS3722_SD5_VOLTAGE,
200296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
201296936Smmel		.enable_reg = AS3722_SD_CONTROL,
202296936Smmel		.enable_mask = AS3722_SDN_CTRL(5),
203296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL2,
204296936Smmel		.ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK,
205296936Smmel		.ranges = as3722_sd_ranges,
206296936Smmel		.nranges = nitems(as3722_sd_ranges),
207296936Smmel	},
208296936Smmel	{
209296936Smmel		.id = AS3722_REG_ID_SD6,
210296936Smmel		.name = "sd6",
211296936Smmel		.volt_reg = AS3722_SD6_VOLTAGE,
212296936Smmel		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
213296936Smmel		.enable_reg = AS3722_SD_CONTROL,
214296936Smmel		.enable_mask = AS3722_SDN_CTRL(6),
215296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL2,
216296936Smmel		.ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK,
217296936Smmel		.ranges = as3722_sd016_ranges,
218296936Smmel		.nranges = nitems(as3722_sd016_ranges),
219296936Smmel	},
220296936Smmel	{
221296936Smmel		.id = AS3722_REG_ID_LDO0,
222296936Smmel		.name = "ldo0",
223296936Smmel		.supply_name = "vin-ldo0",
224296936Smmel		.volt_reg = AS3722_LDO0_VOLTAGE,
225296936Smmel		.volt_vsel_mask = AS3722_LDO0_VSEL_MASK,
226296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
227296936Smmel		.enable_mask = AS3722_LDO0_CTRL,
228296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL3,
229296936Smmel		.ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK,
230296936Smmel		.ranges = as3722_ldo_ranges,
231296936Smmel		.nranges = nitems(as3722_ldo_ranges),
232296936Smmel	},
233296936Smmel	{
234296936Smmel		.id = AS3722_REG_ID_LDO1,
235296936Smmel		.name = "ldo1",
236296936Smmel		.supply_name = "vin-ldo1-6",
237296936Smmel		.volt_reg = AS3722_LDO1_VOLTAGE,
238296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
239296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
240296936Smmel		.enable_mask = AS3722_LDO1_CTRL,
241296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL3,
242296936Smmel		.ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK,
243296936Smmel		.ranges = as3722_ldo_ranges,
244296936Smmel		.nranges = nitems(as3722_ldo_ranges),
245296936Smmel	},
246296936Smmel	{
247296936Smmel		.id = AS3722_REG_ID_LDO2,
248296936Smmel		.name = "ldo2",
249296936Smmel		.supply_name = "vin-ldo2-5-7",
250296936Smmel		.volt_reg = AS3722_LDO2_VOLTAGE,
251296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
252296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
253296936Smmel		.enable_mask = AS3722_LDO2_CTRL,
254296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL3,
255296936Smmel		.ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK,
256296936Smmel		.ranges = as3722_ldo_ranges,
257296936Smmel		.nranges = nitems(as3722_ldo_ranges),
258296936Smmel	},
259296936Smmel	{
260296936Smmel		.id = AS3722_REG_ID_LDO3,
261296936Smmel		.name = "ldo3",
262296936Smmel		.supply_name = "vin-ldo3-4",
263296936Smmel		.volt_reg = AS3722_LDO3_VOLTAGE,
264296936Smmel		.volt_vsel_mask = AS3722_LDO3_VSEL_MASK,
265296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
266296936Smmel		.enable_mask = AS3722_LDO3_CTRL,
267296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL3,
268296936Smmel		.ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK,
269296936Smmel		.ranges = as3722_ldo3_ranges,
270296936Smmel		.nranges = nitems(as3722_ldo3_ranges),
271296936Smmel	},
272296936Smmel	{
273296936Smmel		.id = AS3722_REG_ID_LDO4,
274296936Smmel		.name = "ldo4",
275296936Smmel		.supply_name = "vin-ldo3-4",
276296936Smmel		.volt_reg = AS3722_LDO4_VOLTAGE,
277296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
278296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
279296936Smmel		.enable_mask = AS3722_LDO4_CTRL,
280296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL4,
281296936Smmel		.ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK,
282296936Smmel		.ranges = as3722_ldo_ranges,
283296936Smmel		.nranges = nitems(as3722_ldo_ranges),
284296936Smmel	},
285296936Smmel	{
286296936Smmel		.id = AS3722_REG_ID_LDO5,
287296936Smmel		.name = "ldo5",
288296936Smmel		.supply_name = "vin-ldo2-5-7",
289296936Smmel		.volt_reg = AS3722_LDO5_VOLTAGE,
290296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
291296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
292296936Smmel		.enable_mask = AS3722_LDO5_CTRL,
293296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL4,
294296936Smmel		.ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK,
295296936Smmel		.ranges = as3722_ldo_ranges,
296296936Smmel		.nranges = nitems(as3722_ldo_ranges),
297296936Smmel	},
298296936Smmel	{
299296936Smmel		.id = AS3722_REG_ID_LDO6,
300296936Smmel		.name = "ldo6",
301296936Smmel		.supply_name = "vin-ldo1-6",
302296936Smmel		.volt_reg = AS3722_LDO6_VOLTAGE,
303296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
304296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
305296936Smmel		.enable_mask = AS3722_LDO6_CTRL,
306296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL4,
307296936Smmel		.ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK,
308296936Smmel		.ranges = as3722_ldo_ranges,
309296936Smmel		.nranges = nitems(as3722_ldo_ranges),
310296936Smmel	},
311296936Smmel	{
312296936Smmel		.id = AS3722_REG_ID_LDO7,
313296936Smmel		.name = "ldo7",
314296936Smmel		.supply_name = "vin-ldo2-5-7",
315296936Smmel		.volt_reg = AS3722_LDO7_VOLTAGE,
316296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
317296936Smmel		.enable_reg = AS3722_LDO_CONTROL0,
318296936Smmel		.enable_mask = AS3722_LDO7_CTRL,
319296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL4,
320296936Smmel		.ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK,
321296936Smmel		.ranges = as3722_ldo_ranges,
322296936Smmel		.nranges = nitems(as3722_ldo_ranges),
323296936Smmel	},
324296936Smmel	{
325296936Smmel		.id = AS3722_REG_ID_LDO9,
326296936Smmel		.name = "ldo9",
327296936Smmel		.supply_name = "vin-ldo9-10",
328296936Smmel		.volt_reg = AS3722_LDO9_VOLTAGE,
329296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
330296936Smmel		.enable_reg = AS3722_LDO_CONTROL1,
331296936Smmel		.enable_mask = AS3722_LDO9_CTRL,
332296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL5,
333296936Smmel		.ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK,
334296936Smmel		.ranges = as3722_ldo_ranges,
335296936Smmel		.nranges = nitems(as3722_ldo_ranges),
336296936Smmel	},
337296936Smmel	{
338296936Smmel		.id = AS3722_REG_ID_LDO10,
339296936Smmel		.name = "ldo10",
340296936Smmel		.supply_name = "vin-ldo9-10",
341296936Smmel		.volt_reg = AS3722_LDO10_VOLTAGE,
342296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
343296936Smmel		.enable_reg = AS3722_LDO_CONTROL1,
344296936Smmel		.enable_mask = AS3722_LDO10_CTRL,
345296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL5,
346296936Smmel		.ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK,
347296936Smmel		.ranges = as3722_ldo_ranges,
348296936Smmel		.nranges = nitems(as3722_ldo_ranges),
349296936Smmel	},
350296936Smmel	{
351296936Smmel		.id = AS3722_REG_ID_LDO11,
352296936Smmel		.name = "ldo11",
353296936Smmel		.supply_name = "vin-ldo11",
354296936Smmel		.volt_reg = AS3722_LDO11_VOLTAGE,
355296936Smmel		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
356296936Smmel		.enable_reg = AS3722_LDO_CONTROL1,
357296936Smmel		.enable_mask = AS3722_LDO11_CTRL,
358296936Smmel		.ext_enable_reg = AS3722_ENABLE_CTRL5,
359296936Smmel		.ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK,
360296936Smmel		.ranges = as3722_ldo_ranges,
361296936Smmel		.nranges = nitems(as3722_ldo_ranges),
362296936Smmel	},
363296936Smmel};
364296936Smmel
365296936Smmel
366296936Smmelstruct as3722_regnode_init_def {
367296936Smmel	struct regnode_init_def	reg_init_def;
368296936Smmel	int 			ext_control;
369296936Smmel	int	 		enable_tracking;
370296936Smmel};
371296936Smmel
372296936Smmelstatic int as3722_regnode_init(struct regnode *regnode);
373296936Smmelstatic int as3722_regnode_enable(struct regnode *regnode, bool enable,
374296936Smmel    int *udelay);
375296936Smmelstatic int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
376296936Smmel    int max_uvolt, int *udelay);
377296936Smmelstatic int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
378296936Smmelstatic regnode_method_t as3722_regnode_methods[] = {
379296936Smmel	/* Regulator interface */
380296936Smmel	REGNODEMETHOD(regnode_init,		as3722_regnode_init),
381296936Smmel	REGNODEMETHOD(regnode_enable,		as3722_regnode_enable),
382296936Smmel	REGNODEMETHOD(regnode_set_voltage,	as3722_regnode_set_volt),
383296936Smmel	REGNODEMETHOD(regnode_get_voltage,	as3722_regnode_get_volt),
384296936Smmel	REGNODEMETHOD_END
385296936Smmel};
386296936SmmelDEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
387296936Smmel   sizeof(struct as3722_reg_sc), regnode_class);
388296936Smmel
389296936Smmelstatic int
390296936Smmelas3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel)
391296936Smmel{
392296936Smmel	int rv;
393296936Smmel
394296936Smmel	rv = RD1(sc->base_sc, sc->def->volt_reg, sel);
395296936Smmel	if (rv != 0)
396296936Smmel		return (rv);
397296936Smmel	*sel &= sc->def->volt_vsel_mask;
398296936Smmel	*sel >>= ffs(sc->def->volt_vsel_mask) - 1;
399296936Smmel	return (0);
400296936Smmel}
401296936Smmel
402296936Smmelstatic int
403296936Smmelas3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel)
404296936Smmel{
405296936Smmel	int rv;
406296936Smmel
407296936Smmel	sel <<= ffs(sc->def->volt_vsel_mask) - 1;
408296936Smmel	sel &= sc->def->volt_vsel_mask;
409296936Smmel
410296936Smmel	rv = RM1(sc->base_sc, sc->def->volt_reg,
411296936Smmel	    sc->def->volt_vsel_mask, sel);
412296936Smmel	if (rv != 0)
413296936Smmel		return (rv);
414296936Smmel	return (rv);
415296936Smmel}
416296936Smmel
417296936Smmelstatic bool
418296936Smmelas3722_sd0_is_low_voltage(struct as3722_reg_sc *sc)
419296936Smmel{
420296936Smmel	uint8_t val;
421296936Smmel	int rv;
422296936Smmel
423296936Smmel	rv = RD1(sc->base_sc, AS3722_FUSE7, &val);
424296936Smmel	if (rv != 0)
425296936Smmel		return (rv);
426296936Smmel	return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false);
427296936Smmel}
428296936Smmel
429296936Smmelstatic int
430296936Smmelas3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl)
431296936Smmel{
432296936Smmel	uint8_t val;
433296936Smmel	int rv;
434296936Smmel
435296936Smmel	val =  ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1);
436296936Smmel	rv = RM1(sc->base_sc, sc->def->ext_enable_reg,
437296936Smmel	    sc->def->ext_enable_mask, val);
438296936Smmel	return (rv);
439296936Smmel}
440296936Smmel
441296936Smmelstatic int
442296936Smmelas3722_reg_enable(struct as3722_reg_sc *sc)
443296936Smmel{
444296936Smmel	int rv;
445296936Smmel
446296936Smmel	rv = RM1(sc->base_sc, sc->def->enable_reg,
447296936Smmel	    sc->def->enable_mask, sc->def->enable_mask);
448296936Smmel	return (rv);
449296936Smmel}
450296936Smmel
451296936Smmelstatic int
452296936Smmelas3722_reg_disable(struct as3722_reg_sc *sc)
453296936Smmel{
454296936Smmel	int rv;
455296936Smmel
456296936Smmel	rv = RM1(sc->base_sc, sc->def->enable_reg,
457296936Smmel	    sc->def->enable_mask, 0);
458296936Smmel	return (rv);
459296936Smmel}
460296936Smmel
461296936Smmelstatic int
462296936Smmelas3722_regnode_init(struct regnode *regnode)
463296936Smmel{
464296936Smmel	struct as3722_reg_sc *sc;
465296936Smmel	int rv;
466296936Smmel
467296936Smmel	sc = regnode_get_softc(regnode);
468296936Smmel
469296936Smmel	sc->enable_usec = 500;
470296936Smmel	if (sc->def->id == AS3722_REG_ID_SD0) {
471296936Smmel		if (as3722_sd0_is_low_voltage(sc)) {
472296936Smmel			sc->def->ranges = as3722_sd0_lv_ranges;
473296936Smmel			sc->def->nranges = nitems(as3722_sd0_lv_ranges);
474296936Smmel		}
475296936Smmel		sc->enable_usec = 600;
476296936Smmel	} else if (sc->def->id == AS3722_REG_ID_LDO3) {
477296936Smmel		if (sc->enable_tracking) {
478296936Smmel			rv = RM1(sc->base_sc, sc->def->volt_reg,
479296936Smmel			    AS3722_LDO3_MODE_MASK,
480296936Smmel			    AS3722_LDO3_MODE_PMOS_TRACKING);
481296936Smmel			if (rv < 0) {
482296936Smmel				device_printf(sc->base_sc->dev,
483296936Smmel					"LDO3 tracking failed: %d\n", rv);
484296936Smmel				return (rv);
485296936Smmel			}
486296936Smmel		}
487296936Smmel	}
488296936Smmel
489296936Smmel	if (sc->ext_control) {
490296936Smmel
491296936Smmel		rv = as3722_reg_enable(sc);
492296936Smmel		if (rv < 0) {
493296936Smmel			device_printf(sc->base_sc->dev,
494296936Smmel				"Failed to enable %s regulator: %d\n",
495296936Smmel				sc->def->name, rv);
496296936Smmel			return (rv);
497296936Smmel		}
498296936Smmel		rv = as3722_reg_extreg_setup(sc, sc->ext_control);
499296936Smmel		if (rv < 0) {
500296936Smmel			device_printf(sc->base_sc->dev,
501296936Smmel				"%s ext control failed: %d", sc->def->name, rv);
502296936Smmel			return (rv);
503296936Smmel		}
504296936Smmel	}
505296936Smmel	return (0);
506296936Smmel}
507296936Smmel
508296936Smmelstatic void
509296936Smmelas3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
510296936Smmelstruct as3722_regnode_init_def *init_def)
511296936Smmel{
512296936Smmel	int rv;
513296936Smmel	phandle_t parent, supply_node;
514296936Smmel	char prop_name[64]; /* Maximum OFW property name length. */
515296936Smmel
516296936Smmel	rv = regulator_parse_ofw_stdparam(sc->dev, node,
517296936Smmel	    &init_def->reg_init_def);
518296936Smmel
519296936Smmel	rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control,
520296936Smmel	    sizeof(init_def->ext_control));
521296936Smmel	if (rv <= 0)
522296936Smmel		init_def->ext_control = 0;
523296936Smmel	if (init_def->ext_control > 3) {
524296936Smmel		device_printf(sc->dev,
525296936Smmel		    "Invalid value for ams,ext-control property: %d\n",
526296936Smmel		    init_def->ext_control);
527296936Smmel		init_def->ext_control = 0;
528296936Smmel	}
529296936Smmel	if (OF_hasprop(node, "ams,enable-tracking"))
530296936Smmel		init_def->enable_tracking = 1;
531296936Smmel
532296936Smmel
533296936Smmel	/* Get parent supply. */
534296936Smmel	if (def->supply_name == NULL)
535296936Smmel		 return;
536296936Smmel
537296936Smmel	parent = OF_parent(node);
538296936Smmel	snprintf(prop_name, sizeof(prop_name), "%s-supply",
539296936Smmel	    def->supply_name);
540296936Smmel	rv = OF_getencprop(parent, prop_name, &supply_node,
541296936Smmel	    sizeof(supply_node));
542296936Smmel	if (rv <= 0)
543296936Smmel		return;
544296936Smmel	supply_node = OF_node_from_xref(supply_node);
545296936Smmel	rv = OF_getprop_alloc(supply_node, "regulator-name", 1,
546296936Smmel	    (void **)&init_def->reg_init_def.parent_name);
547296936Smmel	if (rv <= 0)
548296936Smmel		init_def->reg_init_def.parent_name = NULL;
549296936Smmel}
550296936Smmel
551296936Smmelstatic struct as3722_reg_sc *
552296936Smmelas3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def)
553296936Smmel{
554296936Smmel	struct as3722_reg_sc *reg_sc;
555296936Smmel	struct as3722_regnode_init_def init_def;
556296936Smmel	struct regnode *regnode;
557296936Smmel
558296936Smmel	bzero(&init_def, sizeof(init_def));
559296936Smmel
560296936Smmel	as3722_fdt_parse(sc, node, def, &init_def);
561296936Smmel	init_def.reg_init_def.id = def->id;
562296936Smmel	init_def.reg_init_def.ofw_node = node;
563296936Smmel	regnode = regnode_create(sc->dev, &as3722_regnode_class,
564296936Smmel	    &init_def.reg_init_def);
565296936Smmel	if (regnode == NULL) {
566296936Smmel		device_printf(sc->dev, "Cannot create regulator.\n");
567296936Smmel		return (NULL);
568296936Smmel	}
569296936Smmel	reg_sc = regnode_get_softc(regnode);
570296936Smmel
571296936Smmel	/* Init regulator softc. */
572296936Smmel	reg_sc->regnode = regnode;
573296936Smmel	reg_sc->base_sc = sc;
574296936Smmel	reg_sc->def = def;
575296936Smmel	reg_sc->xref = OF_xref_from_node(node);
576296936Smmel
577296936Smmel	reg_sc->param = regnode_get_stdparam(regnode);
578296936Smmel	reg_sc->ext_control = init_def.ext_control;
579296936Smmel	reg_sc->enable_tracking = init_def.enable_tracking;
580296936Smmel
581296936Smmel	regnode_register(regnode);
582296936Smmel	if (bootverbose) {
583296936Smmel		int volt, rv;
584296936Smmel		regnode_topo_slock();
585296936Smmel		rv = regnode_get_voltage(regnode, &volt);
586296936Smmel		if (rv == ENODEV) {
587296936Smmel			device_printf(sc->dev,
588296936Smmel			   " Regulator %s: parent doesn't exist yet.\n",
589296936Smmel			   regnode_get_name(regnode));
590296936Smmel		} else if (rv != 0) {
591296936Smmel			device_printf(sc->dev,
592296936Smmel			   " Regulator %s: voltage: INVALID!!!\n",
593296936Smmel			   regnode_get_name(regnode));
594296936Smmel		} else {
595296936Smmel			device_printf(sc->dev,
596296936Smmel			    " Regulator %s: voltage: %d uV\n",
597296936Smmel			    regnode_get_name(regnode), volt);
598296936Smmel		}
599296936Smmel		regnode_topo_unlock();
600296936Smmel	}
601296936Smmel
602296936Smmel	return (reg_sc);
603296936Smmel}
604296936Smmel
605296936Smmelint
606296936Smmelas3722_regulator_attach(struct as3722_softc *sc, phandle_t node)
607296936Smmel{
608296936Smmel	struct as3722_reg_sc *reg;
609296936Smmel	phandle_t child, rnode;
610296936Smmel	int i;
611296936Smmel
612296936Smmel	rnode = ofw_bus_find_child(node, "regulators");
613296936Smmel	if (rnode <= 0) {
614296936Smmel		device_printf(sc->dev, " Cannot find regulators subnode\n");
615296936Smmel		return (ENXIO);
616296936Smmel	}
617296936Smmel
618296936Smmel	sc->nregs = nitems(as3722s_def);
619296936Smmel	sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs,
620296936Smmel	    M_AS3722_REG, M_WAITOK | M_ZERO);
621296936Smmel
622296936Smmel
623296936Smmel	/* Attach all known regulators if exist in DT. */
624296936Smmel	for (i = 0; i < sc->nregs; i++) {
625296936Smmel		child = ofw_bus_find_child(rnode, as3722s_def[i].name);
626296936Smmel		if (child == 0) {
627296936Smmel			if (bootverbose)
628296936Smmel				device_printf(sc->dev,
629296936Smmel				    "Regulator %s missing in DT\n",
630296936Smmel				    as3722s_def[i].name);
631296936Smmel			continue;
632296936Smmel		}
633296936Smmel		reg = as3722_attach(sc, child, as3722s_def + i);
634296936Smmel		if (reg == NULL) {
635296936Smmel			device_printf(sc->dev, "Cannot attach regulator: %s\n",
636296936Smmel			    as3722s_def[i].name);
637296936Smmel			return (ENXIO);
638296936Smmel		}
639296936Smmel		sc->regs[i] = reg;
640296936Smmel	}
641296936Smmel	return (0);
642296936Smmel}
643296936Smmel
644296936Smmelint
645296936Smmelas3722_regulator_map(device_t dev, phandle_t xref, int ncells,
646296936Smmel    pcell_t *cells, int *num)
647296936Smmel{
648296936Smmel	struct as3722_softc *sc;
649296936Smmel	int i;
650296936Smmel
651296936Smmel	sc = device_get_softc(dev);
652296936Smmel	for (i = 0; i < sc->nregs; i++) {
653296936Smmel		if (sc->regs[i] == NULL)
654296936Smmel			continue;
655296936Smmel		if (sc->regs[i]->xref == xref) {
656296936Smmel			*num = sc->regs[i]->def->id;
657296936Smmel			return (0);
658296936Smmel		}
659296936Smmel	}
660296936Smmel	return (ENXIO);
661296936Smmel}
662296936Smmel
663296936Smmelstatic int
664296936Smmelas3722_regnode_enable(struct regnode *regnode, bool val, int *udelay)
665296936Smmel{
666296936Smmel	struct as3722_reg_sc *sc;
667296936Smmel	int rv;
668296936Smmel
669296936Smmel	sc = regnode_get_softc(regnode);
670296936Smmel
671296936Smmel	if (val)
672296936Smmel		rv = as3722_reg_enable(sc);
673296936Smmel	else
674296936Smmel		rv = as3722_reg_disable(sc);
675296936Smmel	*udelay = sc->enable_usec;
676296936Smmel	return (rv);
677296936Smmel}
678296936Smmel
679296936Smmelstatic int
680296936Smmelas3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt,
681296936Smmel    int *udelay)
682296936Smmel{
683296936Smmel	struct as3722_reg_sc *sc;
684296936Smmel	uint8_t sel;
685296936Smmel	int rv;
686296936Smmel
687296936Smmel	sc = regnode_get_softc(regnode);
688296936Smmel
689296936Smmel	*udelay = 0;
690308328Smmel	rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges,
691308328Smmel	    min_uvolt, max_uvolt, &sel);
692296936Smmel	if (rv != 0)
693296936Smmel		return (rv);
694296936Smmel	rv = as3722_write_sel(sc, sel);
695296936Smmel	return (rv);
696296936Smmel
697296936Smmel}
698296936Smmel
699296936Smmelstatic int
700296936Smmelas3722_regnode_get_volt(struct regnode *regnode, int *uvolt)
701296936Smmel{
702296936Smmel	struct as3722_reg_sc *sc;
703296936Smmel	uint8_t sel;
704296936Smmel	int rv;
705296936Smmel
706296936Smmel	sc = regnode_get_softc(regnode);
707296936Smmel	rv = as3722_read_sel(sc, &sel);
708296936Smmel	if (rv != 0)
709296936Smmel		return (rv);
710296936Smmel
711296936Smmel	/* LDO6 have bypass. */
712296936Smmel	if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS)
713296936Smmel		return (ENOENT);
714308328Smmel	rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges,
715308328Smmel	    sel, uvolt);
716296936Smmel	return (rv);
717296936Smmel}
718