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 = ®ulators[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