xref: /freebsd/sys/arm/ti/twl/twl_vreg.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1e53470feSOleksandr Tymoshenko /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni  *
4e53470feSOleksandr Tymoshenko  * Copyright (c) 2011
5e53470feSOleksandr Tymoshenko  *	Ben Gray <ben.r.gray@gmail.com>.
6e53470feSOleksandr Tymoshenko  * All rights reserved.
7e53470feSOleksandr Tymoshenko  *
8e53470feSOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
9e53470feSOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
10e53470feSOleksandr Tymoshenko  * are met:
11e53470feSOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
12e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
13e53470feSOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
14e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
15e53470feSOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
16e53470feSOleksandr Tymoshenko  *
17e53470feSOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e53470feSOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e53470feSOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e53470feSOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21e53470feSOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e53470feSOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e53470feSOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e53470feSOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e53470feSOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e53470feSOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e53470feSOleksandr Tymoshenko  * SUCH DAMAGE.
28e53470feSOleksandr Tymoshenko  */
29e53470feSOleksandr Tymoshenko 
30e53470feSOleksandr Tymoshenko #include <sys/cdefs.h>
31e53470feSOleksandr Tymoshenko /*
32e53470feSOleksandr Tymoshenko  * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
33e53470feSOleksandr Tymoshenko  *
34e53470feSOleksandr Tymoshenko  * This driver covers the voltages regulators (LDO), allows for enabling &
35e53470feSOleksandr Tymoshenko  * disabling the voltage output and adjusting the voltage level.
36e53470feSOleksandr Tymoshenko  *
37e53470feSOleksandr Tymoshenko  * Voltage regulators can belong to different power groups, in this driver we
38e53470feSOleksandr Tymoshenko  * put the regulators under our control in the "Application power group".
39e53470feSOleksandr Tymoshenko  *
40e53470feSOleksandr Tymoshenko  *
41e53470feSOleksandr Tymoshenko  * FLATTENED DEVICE TREE (FDT)
42e53470feSOleksandr Tymoshenko  * Startup override settings can be specified in the FDT, if they are they
43e53470feSOleksandr Tymoshenko  * should be under the twl parent device and take the following form:
44e53470feSOleksandr Tymoshenko  *
45e53470feSOleksandr Tymoshenko  *    voltage-regulators = "name1", "millivolts1",
46e53470feSOleksandr Tymoshenko  *                         "name2", "millivolts2";
47e53470feSOleksandr Tymoshenko  *
48e53470feSOleksandr Tymoshenko  * Each override should be a pair, the first entry is the name of the regulator
49e53470feSOleksandr Tymoshenko  * the second is the voltage (in millivolts) to set for the given regulator.
50e53470feSOleksandr Tymoshenko  *
51e53470feSOleksandr Tymoshenko  */
52e53470feSOleksandr Tymoshenko 
53e53470feSOleksandr Tymoshenko #include <sys/param.h>
54e53470feSOleksandr Tymoshenko #include <sys/systm.h>
55e53470feSOleksandr Tymoshenko #include <sys/kernel.h>
56e53470feSOleksandr Tymoshenko #include <sys/lock.h>
57e53470feSOleksandr Tymoshenko #include <sys/module.h>
58e53470feSOleksandr Tymoshenko #include <sys/bus.h>
59e53470feSOleksandr Tymoshenko #include <sys/resource.h>
60e53470feSOleksandr Tymoshenko #include <sys/rman.h>
61e53470feSOleksandr Tymoshenko #include <sys/sysctl.h>
62e53470feSOleksandr Tymoshenko #include <sys/sx.h>
63e53470feSOleksandr Tymoshenko #include <sys/malloc.h>
64e53470feSOleksandr Tymoshenko 
65e53470feSOleksandr Tymoshenko #include <machine/bus.h>
66e53470feSOleksandr Tymoshenko #include <machine/resource.h>
67e53470feSOleksandr Tymoshenko #include <machine/intr.h>
68e53470feSOleksandr Tymoshenko 
69e53470feSOleksandr Tymoshenko #include <dev/ofw/openfirm.h>
70e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
71e53470feSOleksandr Tymoshenko 
72e53470feSOleksandr Tymoshenko #include "twl.h"
73e53470feSOleksandr Tymoshenko #include "twl_vreg.h"
74e53470feSOleksandr Tymoshenko 
75e53470feSOleksandr Tymoshenko static int twl_vreg_debug = 1;
76e53470feSOleksandr Tymoshenko 
77e53470feSOleksandr Tymoshenko /*
78e53470feSOleksandr Tymoshenko  * Power Groups bits for the 4030 and 6030 devices
79e53470feSOleksandr Tymoshenko  */
80e53470feSOleksandr Tymoshenko #define TWL4030_P3_GRP		0x80	/* Peripherals, power group */
81e53470feSOleksandr Tymoshenko #define TWL4030_P2_GRP		0x40	/* Modem power group */
82e53470feSOleksandr Tymoshenko #define TWL4030_P1_GRP		0x20	/* Application power group (FreeBSD control) */
83e53470feSOleksandr Tymoshenko 
84e53470feSOleksandr Tymoshenko #define TWL6030_P3_GRP		0x04	/* Modem power group */
85e53470feSOleksandr Tymoshenko #define TWL6030_P2_GRP		0x02	/* Connectivity power group */
86e53470feSOleksandr Tymoshenko #define TWL6030_P1_GRP		0x01	/* Application power group (FreeBSD control) */
87e53470feSOleksandr Tymoshenko 
88e53470feSOleksandr Tymoshenko /*
89e53470feSOleksandr Tymoshenko  * Register offsets within a LDO regulator register set
90e53470feSOleksandr Tymoshenko  */
91e53470feSOleksandr Tymoshenko #define TWL_VREG_GRP		0x00	/* Regulator GRP register */
92e53470feSOleksandr Tymoshenko #define TWL_VREG_STATE		0x02
93e53470feSOleksandr Tymoshenko #define TWL_VREG_VSEL		0x03	/* Voltage select register */
94e53470feSOleksandr Tymoshenko 
95e53470feSOleksandr Tymoshenko #define UNDF  0xFFFF
96e53470feSOleksandr Tymoshenko 
97e53470feSOleksandr Tymoshenko static const uint16_t twl6030_voltages[] = {
98e53470feSOleksandr Tymoshenko 	0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
99e53470feSOleksandr Tymoshenko 	1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400,
100e53470feSOleksandr Tymoshenko 	2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200,
101e53470feSOleksandr Tymoshenko 	3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750
102e53470feSOleksandr Tymoshenko };
103e53470feSOleksandr Tymoshenko 
104e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vaux1_voltages[] = {
105e53470feSOleksandr Tymoshenko 	1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
106e53470feSOleksandr Tymoshenko };
107e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vaux2_voltages[] = {
108e53470feSOleksandr Tymoshenko 	1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500,
109e53470feSOleksandr Tymoshenko 	2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400
110e53470feSOleksandr Tymoshenko };
111e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vaux3_voltages[] = {
112e53470feSOleksandr Tymoshenko 	1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
113e53470feSOleksandr Tymoshenko };
114e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vaux4_voltages[] = {
115e53470feSOleksandr Tymoshenko 	700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
116e53470feSOleksandr Tymoshenko 	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
117e53470feSOleksandr Tymoshenko };
118e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vmmc1_voltages[] = {
119e53470feSOleksandr Tymoshenko 	1850, 2850, 3000, 3150
120e53470feSOleksandr Tymoshenko };
121e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vmmc2_voltages[] = {
122e53470feSOleksandr Tymoshenko 	1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
123e53470feSOleksandr Tymoshenko 	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
124e53470feSOleksandr Tymoshenko };
125e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vpll1_voltages[] = {
126e53470feSOleksandr Tymoshenko 	1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
127e53470feSOleksandr Tymoshenko };
128e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vpll2_voltages[] = {
129e53470feSOleksandr Tymoshenko 	700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
130e53470feSOleksandr Tymoshenko 	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
131e53470feSOleksandr Tymoshenko };
132e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vsim_voltages[] = {
133e53470feSOleksandr Tymoshenko 	1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
134e53470feSOleksandr Tymoshenko };
135e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vdac_voltages[] = {
136e53470feSOleksandr Tymoshenko 	1200, 1300, 1800, 1800
137e53470feSOleksandr Tymoshenko };
138a75a785aSIan Lepore #if 0 /* vdd1, vdd2, vdio, not currently used. */
139e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vdd1_voltages[] = {
140e53470feSOleksandr Tymoshenko 	800, 1450
141e53470feSOleksandr Tymoshenko };
142e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vdd2_voltages[] = {
143e53470feSOleksandr Tymoshenko 	800, 1450, 1500
144e53470feSOleksandr Tymoshenko };
145e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vio_voltages[] = {
146e53470feSOleksandr Tymoshenko 	1800, 1850
147e53470feSOleksandr Tymoshenko };
148a75a785aSIan Lepore #endif
149e53470feSOleksandr Tymoshenko static const uint16_t twl4030_vintana2_voltages[] = {
150e53470feSOleksandr Tymoshenko 	2500, 2750
151e53470feSOleksandr Tymoshenko };
152e53470feSOleksandr Tymoshenko 
153e53470feSOleksandr Tymoshenko /**
154e53470feSOleksandr Tymoshenko  *  Support voltage regulators for the different IC's
155e53470feSOleksandr Tymoshenko  */
156e53470feSOleksandr Tymoshenko struct twl_regulator {
157e53470feSOleksandr Tymoshenko 	const char	*name;
158e53470feSOleksandr Tymoshenko 	uint8_t		subdev;
159e53470feSOleksandr Tymoshenko 	uint8_t		regbase;
160e53470feSOleksandr Tymoshenko 
161e53470feSOleksandr Tymoshenko 	uint16_t	fixedvoltage;
162e53470feSOleksandr Tymoshenko 
163e53470feSOleksandr Tymoshenko 	const uint16_t	*voltages;
164e53470feSOleksandr Tymoshenko 	uint32_t	num_voltages;
165e53470feSOleksandr Tymoshenko };
166e53470feSOleksandr Tymoshenko 
167e53470feSOleksandr Tymoshenko #define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \
168e53470feSOleksandr Tymoshenko 	{ name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) }
169e53470feSOleksandr Tymoshenko #define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \
170e53470feSOleksandr Tymoshenko 	{ name, subdev, reg, voltage, NULL, 0 }
171e53470feSOleksandr Tymoshenko 
172e53470feSOleksandr Tymoshenko static const struct twl_regulator twl4030_regulators[] = {
173e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux1",    0, 0x17, twl4030_vaux1_voltages),
174e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux2",    0, 0x1B, twl4030_vaux2_voltages),
175e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux3",    0, 0x1F, twl4030_vaux3_voltages),
176e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux4",    0, 0x23, twl4030_vaux4_voltages),
177e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vmmc1",    0, 0x27, twl4030_vmmc1_voltages),
178e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vmmc2",    0, 0x2B, twl4030_vmmc2_voltages),
179e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vpll1",    0, 0x2F, twl4030_vpll1_voltages),
180e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vpll2",    0, 0x33, twl4030_vpll2_voltages),
181e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vsim",     0, 0x37, twl4030_vsim_voltages),
182e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vdac",     0, 0x3B, twl4030_vdac_voltages),
183e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages),
184e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500),
185e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vintdig",  0, 0x47, 1500),
186e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vusb1v5",  0, 0x71, 1500),
187e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vusb1v8",  0, 0x74, 1800),
188e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vusb3v1",  0, 0x77, 3100),
189e53470feSOleksandr Tymoshenko 	{ NULL, 0, 0x00, 0, NULL, 0 }
190e53470feSOleksandr Tymoshenko };
191e53470feSOleksandr Tymoshenko 
192e53470feSOleksandr Tymoshenko static const struct twl_regulator twl6030_regulators[] = {
193e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages),
194e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages),
195e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages),
196e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vmmc",  0, 0x98, twl6030_voltages),
197e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vpp",   0, 0x9C, twl6030_voltages),
198e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages),
199e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vmem",  0, 0x64, 1800),
200e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vusb",  0, 0xA0, 3300),
201e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("v1v8",  0, 0x46, 1800),
202e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("v2v1",  0, 0x4C, 2100),
203e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290),
204e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800),
205e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vdac",  0, 0x94, 1800),
206e53470feSOleksandr Tymoshenko 	TWL_REGULATOR_FIXED("vana",  0, 0x80, 2100),
207e53470feSOleksandr Tymoshenko 	{ NULL, 0, 0x00, 0, NULL, 0 }
208e53470feSOleksandr Tymoshenko };
209e53470feSOleksandr Tymoshenko 
210e53470feSOleksandr Tymoshenko #define TWL_VREG_MAX_NAMELEN  32
211e53470feSOleksandr Tymoshenko 
212e53470feSOleksandr Tymoshenko struct twl_regulator_entry {
213e53470feSOleksandr Tymoshenko 	LIST_ENTRY(twl_regulator_entry) entries;
214e53470feSOleksandr Tymoshenko 	char                 name[TWL_VREG_MAX_NAMELEN];
215e53470feSOleksandr Tymoshenko 	struct sysctl_oid   *oid;
216e53470feSOleksandr Tymoshenko 	uint8_t          sub_dev;           /* TWL sub-device group */
217e53470feSOleksandr Tymoshenko 	uint8_t          reg_off;           /* base register offset for the LDO */
218e53470feSOleksandr Tymoshenko 	uint16_t         fixed_voltage;	    /* the (milli)voltage if LDO is fixed */
219e53470feSOleksandr Tymoshenko 	const uint16_t  *supp_voltages;     /* pointer to an array of possible voltages */
220e53470feSOleksandr Tymoshenko 	uint32_t         num_supp_voltages; /* the number of supplied voltages */
221e53470feSOleksandr Tymoshenko };
222e53470feSOleksandr Tymoshenko 
223e53470feSOleksandr Tymoshenko struct twl_vreg_softc {
224e53470feSOleksandr Tymoshenko 	device_t        sc_dev;
225e53470feSOleksandr Tymoshenko 	device_t        sc_pdev;
226e53470feSOleksandr Tymoshenko 	struct sx       sc_sx;
227e53470feSOleksandr Tymoshenko 
228e53470feSOleksandr Tymoshenko 	struct intr_config_hook sc_init_hook;
229e53470feSOleksandr Tymoshenko 	LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list;
230e53470feSOleksandr Tymoshenko };
231e53470feSOleksandr Tymoshenko 
232e53470feSOleksandr Tymoshenko #define TWL_VREG_XLOCK(_sc)			sx_xlock(&(_sc)->sc_sx)
233e53470feSOleksandr Tymoshenko #define	TWL_VREG_XUNLOCK(_sc)		sx_xunlock(&(_sc)->sc_sx)
234e53470feSOleksandr Tymoshenko #define TWL_VREG_SLOCK(_sc)			sx_slock(&(_sc)->sc_sx)
235e53470feSOleksandr Tymoshenko #define	TWL_VREG_SUNLOCK(_sc)		sx_sunlock(&(_sc)->sc_sx)
236e53470feSOleksandr Tymoshenko #define TWL_VREG_LOCK_INIT(_sc)		sx_init(&(_sc)->sc_sx, "twl_vreg")
237e53470feSOleksandr Tymoshenko #define TWL_VREG_LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->sc_sx);
238e53470feSOleksandr Tymoshenko 
239e53470feSOleksandr Tymoshenko #define TWL_VREG_ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->sc_sx, SA_LOCKED);
240e53470feSOleksandr Tymoshenko 
241e53470feSOleksandr Tymoshenko #define TWL_VREG_LOCK_UPGRADE(_sc)               \
242e53470feSOleksandr Tymoshenko 	do {                                         \
243e53470feSOleksandr Tymoshenko 		while (!sx_try_upgrade(&(_sc)->sc_sx))   \
244e53470feSOleksandr Tymoshenko 			pause("twl_vreg_ex", (hz / 100));    \
245e53470feSOleksandr Tymoshenko 	} while(0)
246e53470feSOleksandr Tymoshenko #define TWL_VREG_LOCK_DOWNGRADE(_sc)	sx_downgrade(&(_sc)->sc_sx);
247e53470feSOleksandr Tymoshenko 
248e53470feSOleksandr Tymoshenko /**
249e53470feSOleksandr Tymoshenko  *	twl_vreg_read_1 - read single register from the TWL device
250e53470feSOleksandr Tymoshenko  *	twl_vreg_write_1 - write a single register in the TWL device
251e53470feSOleksandr Tymoshenko  *	@sc: device context
252e53470feSOleksandr Tymoshenko  *	@clk: the clock device we're reading from / writing to
253e53470feSOleksandr Tymoshenko  *	@off: offset within the clock's register set
254e53470feSOleksandr Tymoshenko  *	@val: the value to write or a pointer to a variable to store the result
255e53470feSOleksandr Tymoshenko  *
256e53470feSOleksandr Tymoshenko  *	RETURNS:
257e53470feSOleksandr Tymoshenko  *	Zero on success or an error code on failure.
258e53470feSOleksandr Tymoshenko  */
259e53470feSOleksandr Tymoshenko static inline int
twl_vreg_read_1(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,uint8_t off,uint8_t * val)260e53470feSOleksandr Tymoshenko twl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
261e53470feSOleksandr Tymoshenko 	uint8_t off, uint8_t *val)
262e53470feSOleksandr Tymoshenko {
263e53470feSOleksandr Tymoshenko 	return (twl_read(sc->sc_pdev, regulator->sub_dev,
264e53470feSOleksandr Tymoshenko 	    regulator->reg_off + off, val, 1));
265e53470feSOleksandr Tymoshenko }
266e53470feSOleksandr Tymoshenko 
267e53470feSOleksandr Tymoshenko static inline int
twl_vreg_write_1(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,uint8_t off,uint8_t val)268e53470feSOleksandr Tymoshenko twl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
269e53470feSOleksandr Tymoshenko 	uint8_t off, uint8_t val)
270e53470feSOleksandr Tymoshenko {
271e53470feSOleksandr Tymoshenko 	return (twl_write(sc->sc_pdev, regulator->sub_dev,
272e53470feSOleksandr Tymoshenko 	    regulator->reg_off + off, &val, 1));
273e53470feSOleksandr Tymoshenko }
274e53470feSOleksandr Tymoshenko 
275e53470feSOleksandr Tymoshenko /**
276e53470feSOleksandr Tymoshenko  *	twl_millivolt_to_vsel - gets the vsel bit value to write into the register
277e53470feSOleksandr Tymoshenko  *	                        for a desired voltage and regulator
278e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
279e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator device
280e53470feSOleksandr Tymoshenko  *	@millivolts: the millivolts to find the bit value for
281e53470feSOleksandr Tymoshenko  *	@vsel: upon return will contain the corresponding register value
282e53470feSOleksandr Tymoshenko  *
283e53470feSOleksandr Tymoshenko  *	Accepts a (milli)voltage value and tries to find the closest match to the
284e53470feSOleksandr Tymoshenko  *	actual supported voltages for the given regulator.  If a match is found
285e53470feSOleksandr Tymoshenko  *	within 100mv of the target, @vsel is written with the match and 0 is
286e53470feSOleksandr Tymoshenko  *	returned. If no voltage match is found the function returns an non-zero
287e53470feSOleksandr Tymoshenko  *	value.
288e53470feSOleksandr Tymoshenko  *
289e53470feSOleksandr Tymoshenko  *	RETURNS:
290e53470feSOleksandr Tymoshenko  *	Zero on success or an error code on failure.
291e53470feSOleksandr Tymoshenko  */
292e53470feSOleksandr Tymoshenko static int
twl_vreg_millivolt_to_vsel(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,int millivolts,uint8_t * vsel)293e53470feSOleksandr Tymoshenko twl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc,
294e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel)
295e53470feSOleksandr Tymoshenko {
296e53470feSOleksandr Tymoshenko 	int delta, smallest_delta;
297e53470feSOleksandr Tymoshenko 	unsigned i, closest_idx;
298e53470feSOleksandr Tymoshenko 
299e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
300e53470feSOleksandr Tymoshenko 
301e53470feSOleksandr Tymoshenko 	if (regulator->supp_voltages == NULL)
302e53470feSOleksandr Tymoshenko 		return (EINVAL);
303e53470feSOleksandr Tymoshenko 
304e53470feSOleksandr Tymoshenko 	/* Loop over the support voltages and try and find the closest match */
305e53470feSOleksandr Tymoshenko 	closest_idx = 0;
306e53470feSOleksandr Tymoshenko 	smallest_delta = 0x7fffffff;
307e53470feSOleksandr Tymoshenko 	for (i = 0; i < regulator->num_supp_voltages; i++) {
308e53470feSOleksandr Tymoshenko 		/* Ignore undefined values */
309e53470feSOleksandr Tymoshenko 		if (regulator->supp_voltages[i] == UNDF)
310e53470feSOleksandr Tymoshenko 			continue;
311e53470feSOleksandr Tymoshenko 
312e53470feSOleksandr Tymoshenko 		/* Calculate the difference */
313e53470feSOleksandr Tymoshenko 		delta = millivolts - (int)regulator->supp_voltages[i];
314e53470feSOleksandr Tymoshenko 		if (abs(delta) < smallest_delta) {
315e53470feSOleksandr Tymoshenko 			smallest_delta = abs(delta);
316e53470feSOleksandr Tymoshenko 			closest_idx = i;
317e53470feSOleksandr Tymoshenko 		}
318e53470feSOleksandr Tymoshenko 	}
319e53470feSOleksandr Tymoshenko 
320e53470feSOleksandr Tymoshenko 	/* Check we got a voltage that was within 100mv of the actual target, this
321e53470feSOleksandr Tymoshenko 	 * is just a value I picked out of thin air.
322e53470feSOleksandr Tymoshenko 	 */
323e53470feSOleksandr Tymoshenko 	if ((smallest_delta > 100) && (closest_idx < 0x100))
324e53470feSOleksandr Tymoshenko 		return (EINVAL);
325e53470feSOleksandr Tymoshenko 
326e53470feSOleksandr Tymoshenko 	*vsel = closest_idx;
327e53470feSOleksandr Tymoshenko 	return (0);
328e53470feSOleksandr Tymoshenko }
329e53470feSOleksandr Tymoshenko 
330e53470feSOleksandr Tymoshenko /**
331e53470feSOleksandr Tymoshenko  *	twl_vreg_is_regulator_enabled - returns the enabled status of the regulator
332e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
333e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator device
334e53470feSOleksandr Tymoshenko  *	@enabled: stores the enabled status, zero disabled, non-zero enabled
335e53470feSOleksandr Tymoshenko  *
336e53470feSOleksandr Tymoshenko  *	LOCKING:
337e53470feSOleksandr Tymoshenko  *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
338e53470feSOleksandr Tymoshenko  *	exclusive if not already but, if so, it will be downgraded again before
339e53470feSOleksandr Tymoshenko  *	returning.
340e53470feSOleksandr Tymoshenko  *
341e53470feSOleksandr Tymoshenko  *	RETURNS:
342e53470feSOleksandr Tymoshenko  *	Zero on success or an error code on failure.
343e53470feSOleksandr Tymoshenko  */
344e53470feSOleksandr Tymoshenko static int
twl_vreg_is_regulator_enabled(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,int * enabled)345e53470feSOleksandr Tymoshenko twl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc,
346e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator, int *enabled)
347e53470feSOleksandr Tymoshenko {
348e53470feSOleksandr Tymoshenko 	int err;
349e53470feSOleksandr Tymoshenko 	uint8_t grp;
350e53470feSOleksandr Tymoshenko 	uint8_t state;
351e53470feSOleksandr Tymoshenko 	int xlocked;
352e53470feSOleksandr Tymoshenko 
353e53470feSOleksandr Tymoshenko 	if (enabled == NULL)
354e53470feSOleksandr Tymoshenko 		return (EINVAL);
355e53470feSOleksandr Tymoshenko 
356e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
357e53470feSOleksandr Tymoshenko 
358e53470feSOleksandr Tymoshenko 	xlocked = sx_xlocked(&sc->sc_sx);
359e53470feSOleksandr Tymoshenko 	if (!xlocked)
360e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_UPGRADE(sc);
361e53470feSOleksandr Tymoshenko 
362e53470feSOleksandr Tymoshenko 	/* The status reading is different for the different devices */
363e53470feSOleksandr Tymoshenko 	if (twl_is_4030(sc->sc_pdev)) {
364e53470feSOleksandr Tymoshenko 		err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state);
365e53470feSOleksandr Tymoshenko 		if (err)
366e53470feSOleksandr Tymoshenko 			goto done;
367e53470feSOleksandr Tymoshenko 
368e53470feSOleksandr Tymoshenko 		*enabled = (state & TWL4030_P1_GRP);
369e53470feSOleksandr Tymoshenko 
370e53470feSOleksandr Tymoshenko 	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
371e53470feSOleksandr Tymoshenko 		/* Check the regulator is in the application group */
372e53470feSOleksandr Tymoshenko 		if (twl_is_6030(sc->sc_pdev)) {
373e53470feSOleksandr Tymoshenko 			err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
374e53470feSOleksandr Tymoshenko 			if (err)
375e53470feSOleksandr Tymoshenko 				goto done;
376e53470feSOleksandr Tymoshenko 
377e53470feSOleksandr Tymoshenko 			if (!(grp & TWL6030_P1_GRP)) {
378e53470feSOleksandr Tymoshenko 				*enabled = 0; /* disabled */
379e53470feSOleksandr Tymoshenko 				goto done;
380e53470feSOleksandr Tymoshenko 			}
381e53470feSOleksandr Tymoshenko 		}
382e53470feSOleksandr Tymoshenko 
383e53470feSOleksandr Tymoshenko 		/* Read the application mode state and verify it's ON */
384e53470feSOleksandr Tymoshenko 		err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state);
385e53470feSOleksandr Tymoshenko 		if (err)
386e53470feSOleksandr Tymoshenko 			goto done;
387e53470feSOleksandr Tymoshenko 
388e53470feSOleksandr Tymoshenko 		*enabled = ((state & 0x0C) == 0x04);
389e53470feSOleksandr Tymoshenko 
390e53470feSOleksandr Tymoshenko 	} else {
391e53470feSOleksandr Tymoshenko 		err = EINVAL;
392e53470feSOleksandr Tymoshenko 	}
393e53470feSOleksandr Tymoshenko 
394e53470feSOleksandr Tymoshenko done:
395e53470feSOleksandr Tymoshenko 	if (!xlocked)
396e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_DOWNGRADE(sc);
397e53470feSOleksandr Tymoshenko 
398e53470feSOleksandr Tymoshenko 	return (err);
399e53470feSOleksandr Tymoshenko }
400e53470feSOleksandr Tymoshenko 
401e53470feSOleksandr Tymoshenko /**
402e53470feSOleksandr Tymoshenko  *	twl_vreg_disable_regulator - disables a voltage regulator
403e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
404e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator device
405e53470feSOleksandr Tymoshenko  *
406e53470feSOleksandr Tymoshenko  *	Disables the regulator which will stop the output drivers.
407e53470feSOleksandr Tymoshenko  *
408e53470feSOleksandr Tymoshenko  *	LOCKING:
409e53470feSOleksandr Tymoshenko  *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
410e53470feSOleksandr Tymoshenko  *	exclusive if not already but, if so, it will be downgraded again before
411e53470feSOleksandr Tymoshenko  *	returning.
412e53470feSOleksandr Tymoshenko  *
413e53470feSOleksandr Tymoshenko  *	RETURNS:
414e53470feSOleksandr Tymoshenko  *	Zero on success or a positive error code on failure.
415e53470feSOleksandr Tymoshenko  */
416e53470feSOleksandr Tymoshenko static int
twl_vreg_disable_regulator(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator)417e53470feSOleksandr Tymoshenko twl_vreg_disable_regulator(struct twl_vreg_softc *sc,
418e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator)
419e53470feSOleksandr Tymoshenko {
420e53470feSOleksandr Tymoshenko 	int err = 0;
421e53470feSOleksandr Tymoshenko 	uint8_t grp;
422e53470feSOleksandr Tymoshenko 	int xlocked;
423e53470feSOleksandr Tymoshenko 
424e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
425e53470feSOleksandr Tymoshenko 
426e53470feSOleksandr Tymoshenko 	xlocked = sx_xlocked(&sc->sc_sx);
427e53470feSOleksandr Tymoshenko 	if (!xlocked)
428e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_UPGRADE(sc);
429e53470feSOleksandr Tymoshenko 
430e53470feSOleksandr Tymoshenko 	if (twl_is_4030(sc->sc_pdev)) {
431e53470feSOleksandr Tymoshenko 		/* Read the regulator CFG_GRP register */
432e53470feSOleksandr Tymoshenko 		err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
433e53470feSOleksandr Tymoshenko 		if (err)
434e53470feSOleksandr Tymoshenko 			goto done;
435e53470feSOleksandr Tymoshenko 
436e53470feSOleksandr Tymoshenko 		/* On the TWL4030 we just need to remove the regulator from all the
437e53470feSOleksandr Tymoshenko 		 * power groups.
438e53470feSOleksandr Tymoshenko 		 */
439e53470feSOleksandr Tymoshenko 		grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
440e53470feSOleksandr Tymoshenko 		err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
441e53470feSOleksandr Tymoshenko 
442e53470feSOleksandr Tymoshenko 	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
443e53470feSOleksandr Tymoshenko 		/* On TWL6030 we need to make sure we disable power for all groups */
444e53470feSOleksandr Tymoshenko 		if (twl_is_6030(sc->sc_pdev))
445e53470feSOleksandr Tymoshenko 			grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
446e53470feSOleksandr Tymoshenko 		else
447e53470feSOleksandr Tymoshenko 			grp = 0x00;
448e53470feSOleksandr Tymoshenko 
449e53470feSOleksandr Tymoshenko 		/* Write the resource state to "OFF" */
450e53470feSOleksandr Tymoshenko 		err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5));
451e53470feSOleksandr Tymoshenko 	}
452e53470feSOleksandr Tymoshenko 
453e53470feSOleksandr Tymoshenko done:
454e53470feSOleksandr Tymoshenko 	if (!xlocked)
455e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_DOWNGRADE(sc);
456e53470feSOleksandr Tymoshenko 
457e53470feSOleksandr Tymoshenko 	return (err);
458e53470feSOleksandr Tymoshenko }
459e53470feSOleksandr Tymoshenko 
460e53470feSOleksandr Tymoshenko /**
461e53470feSOleksandr Tymoshenko  *	twl_vreg_enable_regulator - enables the voltage regulator
462e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
463e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator device
464e53470feSOleksandr Tymoshenko  *
465e53470feSOleksandr Tymoshenko  *	Enables the regulator which will enable the voltage out at the currently
466e53470feSOleksandr Tymoshenko  *	set voltage.  Set the voltage before calling this function to avoid
467e53470feSOleksandr Tymoshenko  *	driving the voltage too high/low by mistake.
468e53470feSOleksandr Tymoshenko  *
469e53470feSOleksandr Tymoshenko  *	LOCKING:
470e53470feSOleksandr Tymoshenko  *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
471e53470feSOleksandr Tymoshenko  *	exclusive if not already but, if so, it will be downgraded again before
472e53470feSOleksandr Tymoshenko  *	returning.
473e53470feSOleksandr Tymoshenko  *
474e53470feSOleksandr Tymoshenko  *	RETURNS:
475e53470feSOleksandr Tymoshenko  *	Zero on success or a positive error code on failure.
476e53470feSOleksandr Tymoshenko  */
477e53470feSOleksandr Tymoshenko static int
twl_vreg_enable_regulator(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator)478e53470feSOleksandr Tymoshenko twl_vreg_enable_regulator(struct twl_vreg_softc *sc,
479e53470feSOleksandr Tymoshenko     struct twl_regulator_entry *regulator)
480e53470feSOleksandr Tymoshenko {
481e53470feSOleksandr Tymoshenko 	int err;
482e53470feSOleksandr Tymoshenko 	uint8_t grp;
483e53470feSOleksandr Tymoshenko 	int xlocked;
484e53470feSOleksandr Tymoshenko 
485e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
486e53470feSOleksandr Tymoshenko 
487e53470feSOleksandr Tymoshenko 	xlocked = sx_xlocked(&sc->sc_sx);
488e53470feSOleksandr Tymoshenko 	if (!xlocked)
489e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_UPGRADE(sc);
490e53470feSOleksandr Tymoshenko 
491e53470feSOleksandr Tymoshenko 	err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
492e53470feSOleksandr Tymoshenko 	if (err)
493e53470feSOleksandr Tymoshenko 		goto done;
494e53470feSOleksandr Tymoshenko 
495e53470feSOleksandr Tymoshenko 	/* Enable the regulator by ensuring it's in the application power group
496e53470feSOleksandr Tymoshenko 	 * and is in the "on" state.
497e53470feSOleksandr Tymoshenko 	 */
498e53470feSOleksandr Tymoshenko 	if (twl_is_4030(sc->sc_pdev)) {
499e53470feSOleksandr Tymoshenko 		/* On the TWL4030 we just need to ensure the regulator is in the right
500e53470feSOleksandr Tymoshenko 		 * power domain, don't need to turn on explicitly like TWL6030.
501e53470feSOleksandr Tymoshenko 		 */
502e53470feSOleksandr Tymoshenko 		grp |= TWL4030_P1_GRP;
503e53470feSOleksandr Tymoshenko 		err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
504e53470feSOleksandr Tymoshenko 
505e53470feSOleksandr Tymoshenko 	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
506e53470feSOleksandr Tymoshenko 		if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
507e53470feSOleksandr Tymoshenko 			grp |= TWL6030_P1_GRP;
508e53470feSOleksandr Tymoshenko 			err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
509e53470feSOleksandr Tymoshenko 			if (err)
510e53470feSOleksandr Tymoshenko 				goto done;
511e53470feSOleksandr Tymoshenko 		}
512e53470feSOleksandr Tymoshenko 
513e53470feSOleksandr Tymoshenko 		/* Write the resource state to "ON" */
514e53470feSOleksandr Tymoshenko 		err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01);
515e53470feSOleksandr Tymoshenko 	}
516e53470feSOleksandr Tymoshenko 
517e53470feSOleksandr Tymoshenko done:
518e53470feSOleksandr Tymoshenko 	if (!xlocked)
519e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_DOWNGRADE(sc);
520e53470feSOleksandr Tymoshenko 
521e53470feSOleksandr Tymoshenko 	return (err);
522e53470feSOleksandr Tymoshenko }
523e53470feSOleksandr Tymoshenko 
524e53470feSOleksandr Tymoshenko /**
525e53470feSOleksandr Tymoshenko  *	twl_vreg_write_regulator_voltage - sets the voltage level on a regulator
526e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
527e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator structure
528e53470feSOleksandr Tymoshenko  *	@millivolts: the voltage to set
529e53470feSOleksandr Tymoshenko  *
530e53470feSOleksandr Tymoshenko  *	Sets the voltage output on a given regulator, if the regulator is not
531e53470feSOleksandr Tymoshenko  *	enabled, it will be enabled.
532e53470feSOleksandr Tymoshenko  *
533e53470feSOleksandr Tymoshenko  *	LOCKING:
534e53470feSOleksandr Tymoshenko  *	On entry expects the TWL VREG lock to be held, may upgrade the lock to
535e53470feSOleksandr Tymoshenko  *	exclusive but if so it will be downgraded once again before returning.
536e53470feSOleksandr Tymoshenko  *
537e53470feSOleksandr Tymoshenko  *	RETURNS:
538e53470feSOleksandr Tymoshenko  *	Zero on success or an error code on failure.
539e53470feSOleksandr Tymoshenko  */
540e53470feSOleksandr Tymoshenko static int
twl_vreg_write_regulator_voltage(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,int millivolts)541e53470feSOleksandr Tymoshenko twl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc,
542e53470feSOleksandr Tymoshenko     struct twl_regulator_entry *regulator, int millivolts)
543e53470feSOleksandr Tymoshenko {
544e53470feSOleksandr Tymoshenko 	int err;
545e53470feSOleksandr Tymoshenko 	uint8_t vsel;
546e53470feSOleksandr Tymoshenko 	int xlocked;
547e53470feSOleksandr Tymoshenko 
548e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
549e53470feSOleksandr Tymoshenko 
550e53470feSOleksandr Tymoshenko 	/* If millivolts is zero then we simply disable the output */
551e53470feSOleksandr Tymoshenko 	if (millivolts == 0)
552e53470feSOleksandr Tymoshenko 		return (twl_vreg_disable_regulator(sc, regulator));
553e53470feSOleksandr Tymoshenko 
554e53470feSOleksandr Tymoshenko 	/* If the regulator has a fixed voltage then check the setting matches
555e53470feSOleksandr Tymoshenko 	 * and simply enable.
556e53470feSOleksandr Tymoshenko 	 */
557e53470feSOleksandr Tymoshenko 	if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) {
558e53470feSOleksandr Tymoshenko 		if (millivolts != regulator->fixed_voltage)
559e53470feSOleksandr Tymoshenko 			return (EINVAL);
560e53470feSOleksandr Tymoshenko 
561e53470feSOleksandr Tymoshenko 		return (twl_vreg_enable_regulator(sc, regulator));
562e53470feSOleksandr Tymoshenko 	}
563e53470feSOleksandr Tymoshenko 
564e53470feSOleksandr Tymoshenko 	/* Get the VSEL value for the given voltage */
565e53470feSOleksandr Tymoshenko 	err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel);
566e53470feSOleksandr Tymoshenko 	if (err)
567e53470feSOleksandr Tymoshenko 		return (err);
568e53470feSOleksandr Tymoshenko 
569e53470feSOleksandr Tymoshenko 	/* Need to upgrade because writing the voltage and enabling should be atomic */
570e53470feSOleksandr Tymoshenko 	xlocked = sx_xlocked(&sc->sc_sx);
571e53470feSOleksandr Tymoshenko 	if (!xlocked)
572e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_UPGRADE(sc);
573e53470feSOleksandr Tymoshenko 
574e53470feSOleksandr Tymoshenko 	/* Set voltage and enable (atomically) */
575e53470feSOleksandr Tymoshenko 	err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f));
576e53470feSOleksandr Tymoshenko 	if (!err) {
577e53470feSOleksandr Tymoshenko 		err = twl_vreg_enable_regulator(sc, regulator);
578e53470feSOleksandr Tymoshenko 	}
579e53470feSOleksandr Tymoshenko 
580e53470feSOleksandr Tymoshenko 	if (!xlocked)
581e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_DOWNGRADE(sc);
582e53470feSOleksandr Tymoshenko 
583e53470feSOleksandr Tymoshenko 	if ((twl_vreg_debug > 1) && !err)
584e53470feSOleksandr Tymoshenko 		device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n",
585e53470feSOleksandr Tymoshenko 		    regulator->name, millivolts, vsel);
586e53470feSOleksandr Tymoshenko 
587e53470feSOleksandr Tymoshenko 	return (err);
588e53470feSOleksandr Tymoshenko }
589e53470feSOleksandr Tymoshenko 
590e53470feSOleksandr Tymoshenko /**
591e53470feSOleksandr Tymoshenko  *	twl_vreg_read_regulator_voltage - reads the voltage on a given regulator
592e53470feSOleksandr Tymoshenko  *	@sc: the device soft context
593e53470feSOleksandr Tymoshenko  *	@regulator: pointer to the regulator structure
594e53470feSOleksandr Tymoshenko  *	@millivolts: upon return will contain the voltage on the regulator
595e53470feSOleksandr Tymoshenko  *
596e53470feSOleksandr Tymoshenko  *	LOCKING:
597e53470feSOleksandr Tymoshenko  *	On entry expects the TWL VREG lock to be held. It will upgrade the lock to
598e53470feSOleksandr Tymoshenko  *	exclusive if not already, but if so, it will be downgraded again before
599e53470feSOleksandr Tymoshenko  *	returning.
600e53470feSOleksandr Tymoshenko  *
601e53470feSOleksandr Tymoshenko  *	RETURNS:
602e53470feSOleksandr Tymoshenko  *	Zero on success, or otherwise an error code.
603e53470feSOleksandr Tymoshenko  */
604e53470feSOleksandr Tymoshenko static int
twl_vreg_read_regulator_voltage(struct twl_vreg_softc * sc,struct twl_regulator_entry * regulator,int * millivolts)605e53470feSOleksandr Tymoshenko twl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc,
606e53470feSOleksandr Tymoshenko     struct twl_regulator_entry *regulator, int *millivolts)
607e53470feSOleksandr Tymoshenko {
608e53470feSOleksandr Tymoshenko 	int err;
609e53470feSOleksandr Tymoshenko 	int en = 0;
610e53470feSOleksandr Tymoshenko 	int xlocked;
611e53470feSOleksandr Tymoshenko 	uint8_t vsel;
612e53470feSOleksandr Tymoshenko 
613e53470feSOleksandr Tymoshenko 	TWL_VREG_ASSERT_LOCKED(sc);
614e53470feSOleksandr Tymoshenko 
615e53470feSOleksandr Tymoshenko 	/* Need to upgrade the lock because checking enabled state and voltage
616e53470feSOleksandr Tymoshenko 	 * should be atomic.
617e53470feSOleksandr Tymoshenko 	 */
618e53470feSOleksandr Tymoshenko 	xlocked = sx_xlocked(&sc->sc_sx);
619e53470feSOleksandr Tymoshenko 	if (!xlocked)
620e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_UPGRADE(sc);
621e53470feSOleksandr Tymoshenko 
622e53470feSOleksandr Tymoshenko 	/* Check if the regulator is currently enabled */
623e53470feSOleksandr Tymoshenko 	err = twl_vreg_is_regulator_enabled(sc, regulator, &en);
624e53470feSOleksandr Tymoshenko 	if (err)
625e53470feSOleksandr Tymoshenko 		goto done;
626e53470feSOleksandr Tymoshenko 
627e53470feSOleksandr Tymoshenko 	*millivolts = 0;
628e53470feSOleksandr Tymoshenko 	if (!en)
629e53470feSOleksandr Tymoshenko 		goto done;
630e53470feSOleksandr Tymoshenko 
631e53470feSOleksandr Tymoshenko 	/* Not all voltages are adjustable */
632e53470feSOleksandr Tymoshenko 	if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) {
633e53470feSOleksandr Tymoshenko 		*millivolts = regulator->fixed_voltage;
634e53470feSOleksandr Tymoshenko 		goto done;
635e53470feSOleksandr Tymoshenko 	}
636e53470feSOleksandr Tymoshenko 
637e53470feSOleksandr Tymoshenko 	/* For variable voltages read the voltage register */
638e53470feSOleksandr Tymoshenko 	err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel);
639e53470feSOleksandr Tymoshenko 	if (err)
640e53470feSOleksandr Tymoshenko 		goto done;
641e53470feSOleksandr Tymoshenko 
642e53470feSOleksandr Tymoshenko 	vsel &= (regulator->num_supp_voltages - 1);
643e53470feSOleksandr Tymoshenko 	if (regulator->supp_voltages[vsel] == UNDF) {
644e53470feSOleksandr Tymoshenko 		err = EINVAL;
645e53470feSOleksandr Tymoshenko 		goto done;
646e53470feSOleksandr Tymoshenko 	}
647e53470feSOleksandr Tymoshenko 
648e53470feSOleksandr Tymoshenko 	*millivolts = regulator->supp_voltages[vsel];
649e53470feSOleksandr Tymoshenko 
650e53470feSOleksandr Tymoshenko done:
651e53470feSOleksandr Tymoshenko 	if (!xlocked)
652e53470feSOleksandr Tymoshenko 		TWL_VREG_LOCK_DOWNGRADE(sc);
653e53470feSOleksandr Tymoshenko 
654e53470feSOleksandr Tymoshenko 	if ((twl_vreg_debug > 1) && !err)
655e53470feSOleksandr Tymoshenko 		device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n",
656e53470feSOleksandr Tymoshenko 		    regulator->name, *millivolts, vsel);
657e53470feSOleksandr Tymoshenko 
658e53470feSOleksandr Tymoshenko 	return (err);
659e53470feSOleksandr Tymoshenko }
660e53470feSOleksandr Tymoshenko 
661e53470feSOleksandr Tymoshenko /**
662e53470feSOleksandr Tymoshenko  *	twl_vreg_get_voltage - public interface to read the voltage on a regulator
663e53470feSOleksandr Tymoshenko  *	@dev: TWL VREG device
664e53470feSOleksandr Tymoshenko  *	@name: the name of the regulator to read the voltage of
665e53470feSOleksandr Tymoshenko  *	@millivolts: pointer to an integer that upon return will contain the mV
666e53470feSOleksandr Tymoshenko  *
667e53470feSOleksandr Tymoshenko  *	If the regulator is disabled the function will set the @millivolts to zero.
668e53470feSOleksandr Tymoshenko  *
669e53470feSOleksandr Tymoshenko  *	LOCKING:
670e53470feSOleksandr Tymoshenko  *	Internally the function takes and releases the TWL VREG lock.
671e53470feSOleksandr Tymoshenko  *
672e53470feSOleksandr Tymoshenko  *	RETURNS:
673e53470feSOleksandr Tymoshenko  *	Zero on success or a negative error code on failure.
674e53470feSOleksandr Tymoshenko  */
675e53470feSOleksandr Tymoshenko int
twl_vreg_get_voltage(device_t dev,const char * name,int * millivolts)676e53470feSOleksandr Tymoshenko twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts)
677e53470feSOleksandr Tymoshenko {
678e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc;
679e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator;
680e53470feSOleksandr Tymoshenko 	int err = EINVAL;
681e53470feSOleksandr Tymoshenko 
682e53470feSOleksandr Tymoshenko 	if (millivolts == NULL)
683e53470feSOleksandr Tymoshenko 		return (EINVAL);
684e53470feSOleksandr Tymoshenko 
685e53470feSOleksandr Tymoshenko 	sc = device_get_softc(dev);
686e53470feSOleksandr Tymoshenko 
687e53470feSOleksandr Tymoshenko 	TWL_VREG_SLOCK(sc);
688e53470feSOleksandr Tymoshenko 
689e53470feSOleksandr Tymoshenko 	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
690e53470feSOleksandr Tymoshenko 		if (strcmp(regulator->name, name) == 0) {
691e53470feSOleksandr Tymoshenko 			err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts);
692e53470feSOleksandr Tymoshenko 			break;
693e53470feSOleksandr Tymoshenko 		}
694e53470feSOleksandr Tymoshenko 	}
695e53470feSOleksandr Tymoshenko 
696e53470feSOleksandr Tymoshenko 	TWL_VREG_SUNLOCK(sc);
697e53470feSOleksandr Tymoshenko 
698e53470feSOleksandr Tymoshenko 	return (err);
699e53470feSOleksandr Tymoshenko }
700e53470feSOleksandr Tymoshenko 
701e53470feSOleksandr Tymoshenko /**
702e53470feSOleksandr Tymoshenko  *	twl_vreg_set_voltage - public interface to write the voltage on a regulator
703e53470feSOleksandr Tymoshenko  *	@dev: TWL VREG device
704e53470feSOleksandr Tymoshenko  *	@name: the name of the regulator to read the voltage of
705e53470feSOleksandr Tymoshenko  *	@millivolts: the voltage to set in millivolts
706e53470feSOleksandr Tymoshenko  *
707e53470feSOleksandr Tymoshenko  *	Sets the output voltage on a given regulator. If the regulator is a fixed
708e53470feSOleksandr Tymoshenko  *	voltage reg then the @millivolts value should match the fixed voltage. If
709e53470feSOleksandr Tymoshenko  *	a variable regulator then the @millivolt value must fit within the max/min
710e53470feSOleksandr Tymoshenko  *	range of the given regulator.
711e53470feSOleksandr Tymoshenko  *
712e53470feSOleksandr Tymoshenko  *	LOCKING:
713e53470feSOleksandr Tymoshenko  *	Internally the function takes and releases the TWL VREG lock.
714e53470feSOleksandr Tymoshenko  *
715e53470feSOleksandr Tymoshenko  *	RETURNS:
716e53470feSOleksandr Tymoshenko  *	Zero on success or a negative error code on failure.
717e53470feSOleksandr Tymoshenko  */
718e53470feSOleksandr Tymoshenko int
twl_vreg_set_voltage(device_t dev,const char * name,int millivolts)719e53470feSOleksandr Tymoshenko twl_vreg_set_voltage(device_t dev, const char *name, int millivolts)
720e53470feSOleksandr Tymoshenko {
721e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc;
722e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator;
723e53470feSOleksandr Tymoshenko 	int err = EINVAL;
724e53470feSOleksandr Tymoshenko 
725e53470feSOleksandr Tymoshenko 	sc = device_get_softc(dev);
726e53470feSOleksandr Tymoshenko 
727e53470feSOleksandr Tymoshenko 	TWL_VREG_SLOCK(sc);
728e53470feSOleksandr Tymoshenko 
729e53470feSOleksandr Tymoshenko 	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
730e53470feSOleksandr Tymoshenko 		if (strcmp(regulator->name, name) == 0) {
731e53470feSOleksandr Tymoshenko 			err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts);
732e53470feSOleksandr Tymoshenko 			break;
733e53470feSOleksandr Tymoshenko 		}
734e53470feSOleksandr Tymoshenko 	}
735e53470feSOleksandr Tymoshenko 
736e53470feSOleksandr Tymoshenko 	TWL_VREG_SUNLOCK(sc);
737e53470feSOleksandr Tymoshenko 
738e53470feSOleksandr Tymoshenko 	return (err);
739e53470feSOleksandr Tymoshenko }
740e53470feSOleksandr Tymoshenko 
741e53470feSOleksandr Tymoshenko /**
742e53470feSOleksandr Tymoshenko  *	twl_sysctl_voltage - reads or writes the voltage for a regulator
743e53470feSOleksandr Tymoshenko  *	@SYSCTL_HANDLER_ARGS: arguments for the callback
744e53470feSOleksandr Tymoshenko  *
745e53470feSOleksandr Tymoshenko  *	Callback for the sysctl entry for the regulator, simply used to return
746e53470feSOleksandr Tymoshenko  *	the voltage on a particular regulator.
747e53470feSOleksandr Tymoshenko  *
748e53470feSOleksandr Tymoshenko  *	LOCKING:
749e53470feSOleksandr Tymoshenko  *	Takes the TWL_VREG shared lock internally.
750e53470feSOleksandr Tymoshenko  *
751e53470feSOleksandr Tymoshenko  *	RETURNS:
752e53470feSOleksandr Tymoshenko  *	Zero on success or an error code on failure.
753e53470feSOleksandr Tymoshenko  */
754e53470feSOleksandr Tymoshenko static int
twl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS)755e53470feSOleksandr Tymoshenko twl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS)
756e53470feSOleksandr Tymoshenko {
757e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1;
758e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator;
759e53470feSOleksandr Tymoshenko 	int voltage;
760e53470feSOleksandr Tymoshenko 	int found = 0;
761e53470feSOleksandr Tymoshenko 
762e53470feSOleksandr Tymoshenko 	TWL_VREG_SLOCK(sc);
763e53470feSOleksandr Tymoshenko 
764e53470feSOleksandr Tymoshenko 	/* Find the regulator with the matching name */
765e53470feSOleksandr Tymoshenko 	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
766e53470feSOleksandr Tymoshenko 		if (strcmp(regulator->name, oidp->oid_name) == 0) {
767e53470feSOleksandr Tymoshenko 			found = 1;
768e53470feSOleksandr Tymoshenko 			break;
769e53470feSOleksandr Tymoshenko 		}
770e53470feSOleksandr Tymoshenko 	}
771e53470feSOleksandr Tymoshenko 
772e53470feSOleksandr Tymoshenko 	/* Sanity check that we found the regulator */
773e53470feSOleksandr Tymoshenko 	if (!found) {
774e53470feSOleksandr Tymoshenko 		TWL_VREG_SUNLOCK(sc);
775e53470feSOleksandr Tymoshenko 		return (EINVAL);
776e53470feSOleksandr Tymoshenko 	}
777e53470feSOleksandr Tymoshenko 
778e53470feSOleksandr Tymoshenko 	twl_vreg_read_regulator_voltage(sc, regulator, &voltage);
779e53470feSOleksandr Tymoshenko 
780e53470feSOleksandr Tymoshenko 	TWL_VREG_SUNLOCK(sc);
781e53470feSOleksandr Tymoshenko 
782e53470feSOleksandr Tymoshenko 	return sysctl_handle_int(oidp, &voltage, 0, req);
783e53470feSOleksandr Tymoshenko }
784e53470feSOleksandr Tymoshenko 
785e53470feSOleksandr Tymoshenko /**
786e53470feSOleksandr Tymoshenko  *	twl_add_regulator - adds single voltage regulator sysctls for the device
787e53470feSOleksandr Tymoshenko  *	@sc: device soft context
788e53470feSOleksandr Tymoshenko  *	@name: the name of the regulator
789e53470feSOleksandr Tymoshenko  *	@nsub: the number of the subdevice
790e53470feSOleksandr Tymoshenko  *	@regbase: the base address of the voltage regulator registers
791e53470feSOleksandr Tymoshenko  *	@fixed_voltage: if a fixed voltage regulator this defines it's voltage
792e53470feSOleksandr Tymoshenko  *	@voltages: if a variable voltage regulator, an array of possible voltages
793e53470feSOleksandr Tymoshenko  *	@num_voltages: the number of entries @voltages
794e53470feSOleksandr Tymoshenko  *
795e53470feSOleksandr Tymoshenko  *	Adds a voltage regulator to the device and also a sysctl interface for the
796e53470feSOleksandr Tymoshenko  *	regulator.
797e53470feSOleksandr Tymoshenko  *
798e53470feSOleksandr Tymoshenko  *	LOCKING:
799e53470feSOleksandr Tymoshenko  *	The TWL_VEG exclusive lock must be held while this function is called.
800e53470feSOleksandr Tymoshenko  *
801e53470feSOleksandr Tymoshenko  *	RETURNS:
802e53470feSOleksandr Tymoshenko  *	Pointer to the new regulator entry on success, otherwise on failure NULL.
803e53470feSOleksandr Tymoshenko  */
804e53470feSOleksandr Tymoshenko static struct twl_regulator_entry*
twl_vreg_add_regulator(struct twl_vreg_softc * sc,const char * name,uint8_t nsub,uint8_t regbase,uint16_t fixed_voltage,const uint16_t * voltages,uint32_t num_voltages)805e53470feSOleksandr Tymoshenko twl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name,
806e53470feSOleksandr Tymoshenko 	uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage,
807e53470feSOleksandr Tymoshenko 	const uint16_t *voltages, uint32_t num_voltages)
808e53470feSOleksandr Tymoshenko {
809e53470feSOleksandr Tymoshenko 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
810e53470feSOleksandr Tymoshenko 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
811e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *new;
812e53470feSOleksandr Tymoshenko 
813e53470feSOleksandr Tymoshenko 	new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
814e53470feSOleksandr Tymoshenko 	if (new == NULL)
815e53470feSOleksandr Tymoshenko 		return (NULL);
816e53470feSOleksandr Tymoshenko 
817e53470feSOleksandr Tymoshenko 	strncpy(new->name, name, TWL_VREG_MAX_NAMELEN);
818e53470feSOleksandr Tymoshenko 	new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0';
819e53470feSOleksandr Tymoshenko 
820e53470feSOleksandr Tymoshenko 	new->sub_dev = nsub;
821e53470feSOleksandr Tymoshenko 	new->reg_off = regbase;
822e53470feSOleksandr Tymoshenko 
823e53470feSOleksandr Tymoshenko 	new->fixed_voltage = fixed_voltage;
824e53470feSOleksandr Tymoshenko 
825e53470feSOleksandr Tymoshenko 	new->supp_voltages = voltages;
826e53470feSOleksandr Tymoshenko 	new->num_supp_voltages = num_voltages;
827e53470feSOleksandr Tymoshenko 
828e53470feSOleksandr Tymoshenko 	/* Add a sysctl entry for the voltage */
829e53470feSOleksandr Tymoshenko 	new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
8307029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
831e53470feSOleksandr Tymoshenko 	    twl_vreg_sysctl_voltage, "I", "voltage regulator");
832e53470feSOleksandr Tymoshenko 
833e53470feSOleksandr Tymoshenko 	/* Finally add the regulator to list of supported regulators */
834e53470feSOleksandr Tymoshenko 	LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries);
835e53470feSOleksandr Tymoshenko 
836e53470feSOleksandr Tymoshenko 	return (new);
837e53470feSOleksandr Tymoshenko }
838e53470feSOleksandr Tymoshenko 
839e53470feSOleksandr Tymoshenko /**
840e53470feSOleksandr Tymoshenko  *	twl_vreg_add_regulators - adds any voltage regulators to the device
841e53470feSOleksandr Tymoshenko  *	@sc: device soft context
842e53470feSOleksandr Tymoshenko  *	@chip: the name of the chip used in the hints
843e53470feSOleksandr Tymoshenko  *	@regulators: the list of possible voltage regulators
844e53470feSOleksandr Tymoshenko  *
845e53470feSOleksandr Tymoshenko  *	Loops over the list of regulators and matches up with the FDT values,
846e53470feSOleksandr Tymoshenko  *	adjusting the actual voltage based on the supplied values.
847e53470feSOleksandr Tymoshenko  *
848e53470feSOleksandr Tymoshenko  *	LOCKING:
849e53470feSOleksandr Tymoshenko  *	The TWL_VEG exclusive lock must be held while this function is called.
850e53470feSOleksandr Tymoshenko  *
851e53470feSOleksandr Tymoshenko  *	RETURNS:
852e53470feSOleksandr Tymoshenko  *	Always returns 0.
853e53470feSOleksandr Tymoshenko  */
854e53470feSOleksandr Tymoshenko static int
twl_vreg_add_regulators(struct twl_vreg_softc * sc,const struct twl_regulator * regulators)855e53470feSOleksandr Tymoshenko twl_vreg_add_regulators(struct twl_vreg_softc *sc,
856e53470feSOleksandr Tymoshenko 	const struct twl_regulator *regulators)
857e53470feSOleksandr Tymoshenko {
858e53470feSOleksandr Tymoshenko 	int err;
859e53470feSOleksandr Tymoshenko 	int millivolts;
860e53470feSOleksandr Tymoshenko 	const struct twl_regulator *walker;
861e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *entry;
862e53470feSOleksandr Tymoshenko 	phandle_t child;
863e53470feSOleksandr Tymoshenko 	char rnames[256];
864e53470feSOleksandr Tymoshenko 	char *name, *voltage;
865e53470feSOleksandr Tymoshenko 	int len = 0, prop_len;
866e53470feSOleksandr Tymoshenko 
867e53470feSOleksandr Tymoshenko 	/* Add the regulators from the list */
868e53470feSOleksandr Tymoshenko 	walker = &regulators[0];
869e53470feSOleksandr Tymoshenko 	while (walker->name != NULL) {
870e53470feSOleksandr Tymoshenko 		/* Add the regulator to the list */
871e53470feSOleksandr Tymoshenko 		entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev,
872e53470feSOleksandr Tymoshenko 		    walker->regbase, walker->fixedvoltage,
873e53470feSOleksandr Tymoshenko 		    walker->voltages, walker->num_voltages);
874e53470feSOleksandr Tymoshenko 		if (entry == NULL)
875e53470feSOleksandr Tymoshenko 			continue;
876e53470feSOleksandr Tymoshenko 
877e53470feSOleksandr Tymoshenko 		walker++;
878e53470feSOleksandr Tymoshenko 	}
879e53470feSOleksandr Tymoshenko 
880e53470feSOleksandr Tymoshenko 	/* Check if the FDT is telling us to set any voltages */
881e53470feSOleksandr Tymoshenko 	child = ofw_bus_get_node(sc->sc_pdev);
882e53470feSOleksandr Tymoshenko 	if (child) {
883e53470feSOleksandr Tymoshenko 		prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames));
884e53470feSOleksandr Tymoshenko 		while (len < prop_len) {
885e53470feSOleksandr Tymoshenko 			name = rnames + len;
886e53470feSOleksandr Tymoshenko 			len += strlen(name) + 1;
887e53470feSOleksandr Tymoshenko 			if ((len >= prop_len) || (name[0] == '\0'))
888e53470feSOleksandr Tymoshenko 				break;
889e53470feSOleksandr Tymoshenko 
890e53470feSOleksandr Tymoshenko 			voltage = rnames + len;
891e53470feSOleksandr Tymoshenko 			len += strlen(voltage) + 1;
892e53470feSOleksandr Tymoshenko 			if (voltage[0] == '\0')
893e53470feSOleksandr Tymoshenko 				break;
894e53470feSOleksandr Tymoshenko 
895e53470feSOleksandr Tymoshenko 			millivolts = strtoul(voltage, NULL, 0);
896e53470feSOleksandr Tymoshenko 
897e53470feSOleksandr Tymoshenko 			LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
898e53470feSOleksandr Tymoshenko 				if (strcmp(entry->name, name) == 0) {
899e53470feSOleksandr Tymoshenko 					twl_vreg_write_regulator_voltage(sc, entry, millivolts);
900e53470feSOleksandr Tymoshenko 					break;
901e53470feSOleksandr Tymoshenko 				}
902e53470feSOleksandr Tymoshenko 			}
903e53470feSOleksandr Tymoshenko 		}
904e53470feSOleksandr Tymoshenko 	}
905e53470feSOleksandr Tymoshenko 
906e53470feSOleksandr Tymoshenko 	if (twl_vreg_debug) {
907e53470feSOleksandr Tymoshenko 		LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
908e53470feSOleksandr Tymoshenko 			err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts);
909e53470feSOleksandr Tymoshenko 			if (!err)
910e53470feSOleksandr Tymoshenko 				device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts);
911e53470feSOleksandr Tymoshenko 		}
912e53470feSOleksandr Tymoshenko 	}
913e53470feSOleksandr Tymoshenko 
914e53470feSOleksandr Tymoshenko 	return (0);
915e53470feSOleksandr Tymoshenko }
916e53470feSOleksandr Tymoshenko 
917e53470feSOleksandr Tymoshenko /**
918e53470feSOleksandr Tymoshenko  *	twl_vreg_init - initialises the list of regulators
919e53470feSOleksandr Tymoshenko  *	@dev: the twl_vreg device
920e53470feSOleksandr Tymoshenko  *
921e53470feSOleksandr Tymoshenko  *	This function is called as an intrhook once interrupts have been enabled,
922e53470feSOleksandr Tymoshenko  *	this is done so that the driver has the option to enable/disable or set
923e53470feSOleksandr Tymoshenko  *	the voltage level based on settings providied in the FDT.
924e53470feSOleksandr Tymoshenko  *
925e53470feSOleksandr Tymoshenko  *	LOCKING:
926e53470feSOleksandr Tymoshenko  *	Takes the exclusive lock in the function.
927e53470feSOleksandr Tymoshenko  */
928e53470feSOleksandr Tymoshenko static void
twl_vreg_init(void * dev)929e53470feSOleksandr Tymoshenko twl_vreg_init(void *dev)
930e53470feSOleksandr Tymoshenko {
931e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc;
932e53470feSOleksandr Tymoshenko 
933e53470feSOleksandr Tymoshenko 	sc = device_get_softc((device_t)dev);
934e53470feSOleksandr Tymoshenko 
935e53470feSOleksandr Tymoshenko 	TWL_VREG_XLOCK(sc);
936e53470feSOleksandr Tymoshenko 
937e53470feSOleksandr Tymoshenko 	if (twl_is_4030(sc->sc_pdev))
938e53470feSOleksandr Tymoshenko 		twl_vreg_add_regulators(sc, twl4030_regulators);
939e53470feSOleksandr Tymoshenko 	else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
940e53470feSOleksandr Tymoshenko 		twl_vreg_add_regulators(sc, twl6030_regulators);
941e53470feSOleksandr Tymoshenko 
942e53470feSOleksandr Tymoshenko 	TWL_VREG_XUNLOCK(sc);
943e53470feSOleksandr Tymoshenko 
944e53470feSOleksandr Tymoshenko 	config_intrhook_disestablish(&sc->sc_init_hook);
945e53470feSOleksandr Tymoshenko }
946e53470feSOleksandr Tymoshenko 
947e53470feSOleksandr Tymoshenko static int
twl_vreg_probe(device_t dev)948e53470feSOleksandr Tymoshenko twl_vreg_probe(device_t dev)
949e53470feSOleksandr Tymoshenko {
950e53470feSOleksandr Tymoshenko 	if (twl_is_4030(device_get_parent(dev)))
951e53470feSOleksandr Tymoshenko 		device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators");
952e53470feSOleksandr Tymoshenko 	else if (twl_is_6025(device_get_parent(dev)) ||
953e53470feSOleksandr Tymoshenko 	         twl_is_6030(device_get_parent(dev)))
954e53470feSOleksandr Tymoshenko 		device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators");
955e53470feSOleksandr Tymoshenko 	else
956e53470feSOleksandr Tymoshenko 		return (ENXIO);
957e53470feSOleksandr Tymoshenko 
958e53470feSOleksandr Tymoshenko 	return (0);
959e53470feSOleksandr Tymoshenko }
960e53470feSOleksandr Tymoshenko 
961e53470feSOleksandr Tymoshenko static int
twl_vreg_attach(device_t dev)962e53470feSOleksandr Tymoshenko twl_vreg_attach(device_t dev)
963e53470feSOleksandr Tymoshenko {
964e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc;
965e53470feSOleksandr Tymoshenko 
966e53470feSOleksandr Tymoshenko 	sc = device_get_softc(dev);
967e53470feSOleksandr Tymoshenko 	sc->sc_dev = dev;
968e53470feSOleksandr Tymoshenko 	sc->sc_pdev = device_get_parent(dev);
969e53470feSOleksandr Tymoshenko 
970e53470feSOleksandr Tymoshenko 	TWL_VREG_LOCK_INIT(sc);
971e53470feSOleksandr Tymoshenko 
972e53470feSOleksandr Tymoshenko 	LIST_INIT(&sc->sc_vreg_list);
973e53470feSOleksandr Tymoshenko 
974e53470feSOleksandr Tymoshenko 	/* We have to wait until interrupts are enabled. I2C read and write
975e53470feSOleksandr Tymoshenko 	 * only works if the interrupts are available.
976e53470feSOleksandr Tymoshenko 	 */
977e53470feSOleksandr Tymoshenko 	sc->sc_init_hook.ich_func = twl_vreg_init;
978e53470feSOleksandr Tymoshenko 	sc->sc_init_hook.ich_arg = dev;
979e53470feSOleksandr Tymoshenko 
980e53470feSOleksandr Tymoshenko 	if (config_intrhook_establish(&sc->sc_init_hook) != 0)
981e53470feSOleksandr Tymoshenko 		return (ENOMEM);
982e53470feSOleksandr Tymoshenko 
983e53470feSOleksandr Tymoshenko 	return (0);
984e53470feSOleksandr Tymoshenko }
985e53470feSOleksandr Tymoshenko 
986e53470feSOleksandr Tymoshenko static int
twl_vreg_detach(device_t dev)987e53470feSOleksandr Tymoshenko twl_vreg_detach(device_t dev)
988e53470feSOleksandr Tymoshenko {
989e53470feSOleksandr Tymoshenko 	struct twl_vreg_softc *sc;
990e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *regulator;
991e53470feSOleksandr Tymoshenko 	struct twl_regulator_entry *tmp;
992e53470feSOleksandr Tymoshenko 
993e53470feSOleksandr Tymoshenko 	sc = device_get_softc(dev);
994e53470feSOleksandr Tymoshenko 
995e53470feSOleksandr Tymoshenko 	/* Take the lock and free all the added regulators */
996e53470feSOleksandr Tymoshenko 	TWL_VREG_XLOCK(sc);
997e53470feSOleksandr Tymoshenko 
998e53470feSOleksandr Tymoshenko 	LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) {
999e53470feSOleksandr Tymoshenko 		LIST_REMOVE(regulator, entries);
1000e53470feSOleksandr Tymoshenko 		sysctl_remove_oid(regulator->oid, 1, 0);
1001e53470feSOleksandr Tymoshenko 		free(regulator, M_DEVBUF);
1002e53470feSOleksandr Tymoshenko 	}
1003e53470feSOleksandr Tymoshenko 
1004e53470feSOleksandr Tymoshenko 	TWL_VREG_XUNLOCK(sc);
1005e53470feSOleksandr Tymoshenko 
1006e53470feSOleksandr Tymoshenko 	TWL_VREG_LOCK_DESTROY(sc);
1007e53470feSOleksandr Tymoshenko 
1008e53470feSOleksandr Tymoshenko 	return (0);
1009e53470feSOleksandr Tymoshenko }
1010e53470feSOleksandr Tymoshenko 
1011e53470feSOleksandr Tymoshenko static device_method_t twl_vreg_methods[] = {
1012e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_probe,		twl_vreg_probe),
1013e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_attach,	twl_vreg_attach),
1014e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_detach,	twl_vreg_detach),
1015e53470feSOleksandr Tymoshenko 
1016e53470feSOleksandr Tymoshenko 	{0, 0},
1017e53470feSOleksandr Tymoshenko };
1018e53470feSOleksandr Tymoshenko 
1019e53470feSOleksandr Tymoshenko static driver_t twl_vreg_driver = {
1020e53470feSOleksandr Tymoshenko 	"twl_vreg",
1021e53470feSOleksandr Tymoshenko 	twl_vreg_methods,
1022e53470feSOleksandr Tymoshenko 	sizeof(struct twl_vreg_softc),
1023e53470feSOleksandr Tymoshenko };
1024e53470feSOleksandr Tymoshenko 
10258537e671SJohn Baldwin DRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, 0, 0);
1026e53470feSOleksandr Tymoshenko MODULE_VERSION(twl_vreg, 1);
1027