1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2011
3239281Sgonzo *	Ben Gray <ben.r.gray@gmail.com>.
4239281Sgonzo * All rights reserved.
5239281Sgonzo *
6239281Sgonzo * Redistribution and use in source and binary forms, with or without
7239281Sgonzo * modification, are permitted provided that the following conditions
8239281Sgonzo * are met:
9239281Sgonzo * 1. Redistributions of source code must retain the above copyright
10239281Sgonzo *    notice, this list of conditions and the following disclaimer.
11239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
13239281Sgonzo *    documentation and/or other materials provided with the distribution.
14239281Sgonzo *
15239281Sgonzo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18239281Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25239281Sgonzo * SUCH DAMAGE.
26239281Sgonzo */
27239281Sgonzo
28239281Sgonzo#include <sys/cdefs.h>
29239281Sgonzo__FBSDID("$FreeBSD$");
30239281Sgonzo
31239281Sgonzo/*
32239281Sgonzo * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
33239281Sgonzo *
34239281Sgonzo * This driver covers the voltages regulators (LDO), allows for enabling &
35239281Sgonzo * disabling the voltage output and adjusting the voltage level.
36239281Sgonzo *
37239281Sgonzo * Voltage regulators can belong to different power groups, in this driver we
38239281Sgonzo * put the regulators under our control in the "Application power group".
39239281Sgonzo *
40239281Sgonzo *
41239281Sgonzo * FLATTENED DEVICE TREE (FDT)
42239281Sgonzo * Startup override settings can be specified in the FDT, if they are they
43239281Sgonzo * should be under the twl parent device and take the following form:
44239281Sgonzo *
45239281Sgonzo *    voltage-regulators = "name1", "millivolts1",
46239281Sgonzo *                         "name2", "millivolts2";
47239281Sgonzo *
48239281Sgonzo * Each override should be a pair, the first entry is the name of the regulator
49239281Sgonzo * the second is the voltage (in millivolts) to set for the given regulator.
50239281Sgonzo *
51239281Sgonzo */
52239281Sgonzo
53239281Sgonzo#include <sys/param.h>
54239281Sgonzo#include <sys/systm.h>
55239281Sgonzo#include <sys/kernel.h>
56239281Sgonzo#include <sys/lock.h>
57239281Sgonzo#include <sys/module.h>
58239281Sgonzo#include <sys/bus.h>
59239281Sgonzo#include <sys/resource.h>
60239281Sgonzo#include <sys/rman.h>
61239281Sgonzo#include <sys/sysctl.h>
62239281Sgonzo#include <sys/sx.h>
63239281Sgonzo#include <sys/malloc.h>
64239281Sgonzo
65239281Sgonzo#include <machine/bus.h>
66239281Sgonzo#include <machine/cpu.h>
67239281Sgonzo#include <machine/cpufunc.h>
68239281Sgonzo#include <machine/resource.h>
69239281Sgonzo#include <machine/intr.h>
70239281Sgonzo
71239281Sgonzo#include <dev/ofw/openfirm.h>
72239281Sgonzo#include <dev/ofw/ofw_bus.h>
73239281Sgonzo
74239281Sgonzo#include "twl.h"
75239281Sgonzo#include "twl_vreg.h"
76239281Sgonzo
77239281Sgonzostatic int twl_vreg_debug = 1;
78239281Sgonzo
79239281Sgonzo
80239281Sgonzo/*
81239281Sgonzo * Power Groups bits for the 4030 and 6030 devices
82239281Sgonzo */
83239281Sgonzo#define TWL4030_P3_GRP		0x80	/* Peripherals, power group */
84239281Sgonzo#define TWL4030_P2_GRP		0x40	/* Modem power group */
85239281Sgonzo#define TWL4030_P1_GRP		0x20	/* Application power group (FreeBSD control) */
86239281Sgonzo
87239281Sgonzo#define TWL6030_P3_GRP		0x04	/* Modem power group */
88239281Sgonzo#define TWL6030_P2_GRP		0x02	/* Connectivity power group */
89239281Sgonzo#define TWL6030_P1_GRP		0x01	/* Application power group (FreeBSD control) */
90239281Sgonzo
91239281Sgonzo/*
92239281Sgonzo * Register offsets within a LDO regulator register set
93239281Sgonzo */
94239281Sgonzo#define TWL_VREG_GRP		0x00	/* Regulator GRP register */
95239281Sgonzo#define TWL_VREG_STATE		0x02
96239281Sgonzo#define TWL_VREG_VSEL		0x03	/* Voltage select register */
97239281Sgonzo
98239281Sgonzo#define UNDF  0xFFFF
99239281Sgonzo
100239281Sgonzostatic const uint16_t twl6030_voltages[] = {
101239281Sgonzo	0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
102239281Sgonzo	1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400,
103239281Sgonzo	2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200,
104239281Sgonzo	3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750
105239281Sgonzo};
106239281Sgonzo
107239281Sgonzostatic const uint16_t twl4030_vaux1_voltages[] = {
108239281Sgonzo	1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
109239281Sgonzo};
110239281Sgonzostatic const uint16_t twl4030_vaux2_voltages[] = {
111239281Sgonzo	1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500,
112239281Sgonzo	2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400
113239281Sgonzo};
114239281Sgonzostatic const uint16_t twl4030_vaux3_voltages[] = {
115239281Sgonzo	1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
116239281Sgonzo};
117239281Sgonzostatic const uint16_t twl4030_vaux4_voltages[] = {
118239281Sgonzo	700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
119239281Sgonzo	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
120239281Sgonzo};
121239281Sgonzostatic const uint16_t twl4030_vmmc1_voltages[] = {
122239281Sgonzo	1850, 2850, 3000, 3150
123239281Sgonzo};
124239281Sgonzostatic const uint16_t twl4030_vmmc2_voltages[] = {
125239281Sgonzo	1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
126239281Sgonzo	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
127239281Sgonzo};
128239281Sgonzostatic const uint16_t twl4030_vpll1_voltages[] = {
129239281Sgonzo	1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
130239281Sgonzo};
131239281Sgonzostatic const uint16_t twl4030_vpll2_voltages[] = {
132239281Sgonzo	700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
133239281Sgonzo	2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
134239281Sgonzo};
135239281Sgonzostatic const uint16_t twl4030_vsim_voltages[] = {
136239281Sgonzo	1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
137239281Sgonzo};
138239281Sgonzostatic const uint16_t twl4030_vdac_voltages[] = {
139239281Sgonzo	1200, 1300, 1800, 1800
140239281Sgonzo};
141263456Sdim#if 0 /* vdd1, vdd2, vdio, not currently used. */
142239281Sgonzostatic const uint16_t twl4030_vdd1_voltages[] = {
143239281Sgonzo	800, 1450
144239281Sgonzo};
145239281Sgonzostatic const uint16_t twl4030_vdd2_voltages[] = {
146239281Sgonzo	800, 1450, 1500
147239281Sgonzo};
148239281Sgonzostatic const uint16_t twl4030_vio_voltages[] = {
149239281Sgonzo	1800, 1850
150239281Sgonzo};
151263456Sdim#endif
152239281Sgonzostatic const uint16_t twl4030_vintana2_voltages[] = {
153239281Sgonzo	2500, 2750
154239281Sgonzo};
155239281Sgonzo
156239281Sgonzo/**
157239281Sgonzo *  Support voltage regulators for the different IC's
158239281Sgonzo */
159239281Sgonzostruct twl_regulator {
160239281Sgonzo	const char	*name;
161239281Sgonzo	uint8_t		subdev;
162239281Sgonzo	uint8_t		regbase;
163239281Sgonzo
164239281Sgonzo	uint16_t	fixedvoltage;
165239281Sgonzo
166239281Sgonzo	const uint16_t	*voltages;
167239281Sgonzo	uint32_t	num_voltages;
168239281Sgonzo};
169239281Sgonzo
170239281Sgonzo#define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \
171239281Sgonzo	{ name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) }
172239281Sgonzo#define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \
173239281Sgonzo	{ name, subdev, reg, voltage, NULL, 0 }
174239281Sgonzo
175239281Sgonzostatic const struct twl_regulator twl4030_regulators[] = {
176239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux1",    0, 0x17, twl4030_vaux1_voltages),
177239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux2",    0, 0x1B, twl4030_vaux2_voltages),
178239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux3",    0, 0x1F, twl4030_vaux3_voltages),
179239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux4",    0, 0x23, twl4030_vaux4_voltages),
180239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vmmc1",    0, 0x27, twl4030_vmmc1_voltages),
181239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vmmc2",    0, 0x2B, twl4030_vmmc2_voltages),
182239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vpll1",    0, 0x2F, twl4030_vpll1_voltages),
183239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vpll2",    0, 0x33, twl4030_vpll2_voltages),
184239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vsim",     0, 0x37, twl4030_vsim_voltages),
185239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vdac",     0, 0x3B, twl4030_vdac_voltages),
186239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages),
187239281Sgonzo	TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500),
188239281Sgonzo	TWL_REGULATOR_FIXED("vintdig",  0, 0x47, 1500),
189239281Sgonzo	TWL_REGULATOR_FIXED("vusb1v5",  0, 0x71, 1500),
190239281Sgonzo	TWL_REGULATOR_FIXED("vusb1v8",  0, 0x74, 1800),
191239281Sgonzo	TWL_REGULATOR_FIXED("vusb3v1",  0, 0x77, 3100),
192239281Sgonzo	{ NULL, 0, 0x00, 0, NULL, 0 }
193239281Sgonzo};
194239281Sgonzo
195239281Sgonzostatic const struct twl_regulator twl6030_regulators[] = {
196239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages),
197239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages),
198239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages),
199239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vmmc",  0, 0x98, twl6030_voltages),
200239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vpp",   0, 0x9C, twl6030_voltages),
201239281Sgonzo	TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages),
202239281Sgonzo	TWL_REGULATOR_FIXED("vmem",  0, 0x64, 1800),
203239281Sgonzo	TWL_REGULATOR_FIXED("vusb",  0, 0xA0, 3300),
204239281Sgonzo	TWL_REGULATOR_FIXED("v1v8",  0, 0x46, 1800),
205239281Sgonzo	TWL_REGULATOR_FIXED("v2v1",  0, 0x4C, 2100),
206239281Sgonzo	TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290),
207239281Sgonzo	TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800),
208239281Sgonzo	TWL_REGULATOR_FIXED("vdac",  0, 0x94, 1800),
209239281Sgonzo	TWL_REGULATOR_FIXED("vana",  0, 0x80, 2100),
210239281Sgonzo	{ NULL, 0, 0x00, 0, NULL, 0 }
211239281Sgonzo};
212239281Sgonzo
213239281Sgonzo#define TWL_VREG_MAX_NAMELEN  32
214239281Sgonzo
215239281Sgonzostruct twl_regulator_entry {
216239281Sgonzo	LIST_ENTRY(twl_regulator_entry) entries;
217239281Sgonzo	char                 name[TWL_VREG_MAX_NAMELEN];
218239281Sgonzo	struct sysctl_oid   *oid;
219239281Sgonzo	uint8_t          sub_dev;           /* TWL sub-device group */
220239281Sgonzo	uint8_t          reg_off;           /* base register offset for the LDO */
221239281Sgonzo	uint16_t         fixed_voltage;	    /* the (milli)voltage if LDO is fixed */
222239281Sgonzo	const uint16_t  *supp_voltages;     /* pointer to an array of possible voltages */
223239281Sgonzo	uint32_t         num_supp_voltages; /* the number of supplied voltages */
224239281Sgonzo};
225239281Sgonzo
226239281Sgonzostruct twl_vreg_softc {
227239281Sgonzo	device_t        sc_dev;
228239281Sgonzo	device_t        sc_pdev;
229239281Sgonzo	struct sx       sc_sx;
230239281Sgonzo
231239281Sgonzo	struct intr_config_hook sc_init_hook;
232239281Sgonzo	LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list;
233239281Sgonzo};
234239281Sgonzo
235239281Sgonzo
236239281Sgonzo#define TWL_VREG_XLOCK(_sc)			sx_xlock(&(_sc)->sc_sx)
237239281Sgonzo#define	TWL_VREG_XUNLOCK(_sc)		sx_xunlock(&(_sc)->sc_sx)
238239281Sgonzo#define TWL_VREG_SLOCK(_sc)			sx_slock(&(_sc)->sc_sx)
239239281Sgonzo#define	TWL_VREG_SUNLOCK(_sc)		sx_sunlock(&(_sc)->sc_sx)
240239281Sgonzo#define TWL_VREG_LOCK_INIT(_sc)		sx_init(&(_sc)->sc_sx, "twl_vreg")
241239281Sgonzo#define TWL_VREG_LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->sc_sx);
242239281Sgonzo
243239281Sgonzo#define TWL_VREG_ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->sc_sx, SA_LOCKED);
244239281Sgonzo
245239281Sgonzo#define TWL_VREG_LOCK_UPGRADE(_sc)               \
246239281Sgonzo	do {                                         \
247239281Sgonzo		while (!sx_try_upgrade(&(_sc)->sc_sx))   \
248239281Sgonzo			pause("twl_vreg_ex", (hz / 100));    \
249239281Sgonzo	} while(0)
250239281Sgonzo#define TWL_VREG_LOCK_DOWNGRADE(_sc)	sx_downgrade(&(_sc)->sc_sx);
251239281Sgonzo
252239281Sgonzo
253239281Sgonzo
254239281Sgonzo
255239281Sgonzo/**
256239281Sgonzo *	twl_vreg_read_1 - read single register from the TWL device
257239281Sgonzo *	twl_vreg_write_1 - write a single register in the TWL device
258239281Sgonzo *	@sc: device context
259239281Sgonzo *	@clk: the clock device we're reading from / writing to
260239281Sgonzo *	@off: offset within the clock's register set
261239281Sgonzo *	@val: the value to write or a pointer to a variable to store the result
262239281Sgonzo *
263239281Sgonzo *	RETURNS:
264239281Sgonzo *	Zero on success or an error code on failure.
265239281Sgonzo */
266239281Sgonzostatic inline int
267239281Sgonzotwl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
268239281Sgonzo	uint8_t off, uint8_t *val)
269239281Sgonzo{
270239281Sgonzo	return (twl_read(sc->sc_pdev, regulator->sub_dev,
271239281Sgonzo	    regulator->reg_off + off, val, 1));
272239281Sgonzo}
273239281Sgonzo
274239281Sgonzostatic inline int
275239281Sgonzotwl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
276239281Sgonzo	uint8_t off, uint8_t val)
277239281Sgonzo{
278239281Sgonzo	return (twl_write(sc->sc_pdev, regulator->sub_dev,
279239281Sgonzo	    regulator->reg_off + off, &val, 1));
280239281Sgonzo}
281239281Sgonzo
282239281Sgonzo/**
283239281Sgonzo *	twl_millivolt_to_vsel - gets the vsel bit value to write into the register
284239281Sgonzo *	                        for a desired voltage and regulator
285239281Sgonzo *	@sc: the device soft context
286239281Sgonzo *	@regulator: pointer to the regulator device
287239281Sgonzo *	@millivolts: the millivolts to find the bit value for
288239281Sgonzo *	@vsel: upon return will contain the corresponding register value
289239281Sgonzo *
290239281Sgonzo *	Accepts a (milli)voltage value and tries to find the closest match to the
291239281Sgonzo *	actual supported voltages for the given regulator.  If a match is found
292239281Sgonzo *	within 100mv of the target, @vsel is written with the match and 0 is
293239281Sgonzo *	returned. If no voltage match is found the function returns an non-zero
294239281Sgonzo *	value.
295239281Sgonzo *
296239281Sgonzo *	RETURNS:
297239281Sgonzo *	Zero on success or an error code on failure.
298239281Sgonzo */
299239281Sgonzostatic int
300239281Sgonzotwl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc,
301239281Sgonzo	struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel)
302239281Sgonzo{
303239281Sgonzo	int delta, smallest_delta;
304239281Sgonzo	unsigned i, closest_idx;
305239281Sgonzo
306239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
307239281Sgonzo
308239281Sgonzo	if (regulator->supp_voltages == NULL)
309239281Sgonzo		return (EINVAL);
310239281Sgonzo
311239281Sgonzo	/* Loop over the support voltages and try and find the closest match */
312239281Sgonzo	closest_idx = 0;
313239281Sgonzo	smallest_delta = 0x7fffffff;
314239281Sgonzo	for (i = 0; i < regulator->num_supp_voltages; i++) {
315239281Sgonzo
316239281Sgonzo		/* Ignore undefined values */
317239281Sgonzo		if (regulator->supp_voltages[i] == UNDF)
318239281Sgonzo			continue;
319239281Sgonzo
320239281Sgonzo		/* Calculate the difference */
321239281Sgonzo		delta = millivolts - (int)regulator->supp_voltages[i];
322239281Sgonzo		if (abs(delta) < smallest_delta) {
323239281Sgonzo			smallest_delta = abs(delta);
324239281Sgonzo			closest_idx = i;
325239281Sgonzo		}
326239281Sgonzo	}
327239281Sgonzo
328239281Sgonzo	/* Check we got a voltage that was within 100mv of the actual target, this
329239281Sgonzo	 * is just a value I picked out of thin air.
330239281Sgonzo	 */
331239281Sgonzo	if ((smallest_delta > 100) && (closest_idx < 0x100))
332239281Sgonzo		return (EINVAL);
333239281Sgonzo
334239281Sgonzo	*vsel = closest_idx;
335239281Sgonzo	return (0);
336239281Sgonzo}
337239281Sgonzo
338239281Sgonzo/**
339239281Sgonzo *	twl_vreg_is_regulator_enabled - returns the enabled status of the regulator
340239281Sgonzo *	@sc: the device soft context
341239281Sgonzo *	@regulator: pointer to the regulator device
342239281Sgonzo *	@enabled: stores the enabled status, zero disabled, non-zero enabled
343239281Sgonzo *
344239281Sgonzo *	LOCKING:
345239281Sgonzo *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
346239281Sgonzo *	exclusive if not already but, if so, it will be downgraded again before
347239281Sgonzo *	returning.
348239281Sgonzo *
349239281Sgonzo *	RETURNS:
350239281Sgonzo *	Zero on success or an error code on failure.
351239281Sgonzo */
352239281Sgonzostatic int
353239281Sgonzotwl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc,
354239281Sgonzo	struct twl_regulator_entry *regulator, int *enabled)
355239281Sgonzo{
356239281Sgonzo	int err;
357239281Sgonzo	uint8_t grp;
358239281Sgonzo	uint8_t state;
359239281Sgonzo	int xlocked;
360239281Sgonzo
361239281Sgonzo	if (enabled == NULL)
362239281Sgonzo		return (EINVAL);
363239281Sgonzo
364239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
365239281Sgonzo
366239281Sgonzo	xlocked = sx_xlocked(&sc->sc_sx);
367239281Sgonzo	if (!xlocked)
368239281Sgonzo		TWL_VREG_LOCK_UPGRADE(sc);
369239281Sgonzo
370239281Sgonzo	/* The status reading is different for the different devices */
371239281Sgonzo	if (twl_is_4030(sc->sc_pdev)) {
372239281Sgonzo
373239281Sgonzo		err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state);
374239281Sgonzo		if (err)
375239281Sgonzo			goto done;
376239281Sgonzo
377239281Sgonzo		*enabled = (state & TWL4030_P1_GRP);
378239281Sgonzo
379239281Sgonzo	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
380239281Sgonzo
381239281Sgonzo		/* Check the regulator is in the application group */
382239281Sgonzo		if (twl_is_6030(sc->sc_pdev)) {
383239281Sgonzo			err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
384239281Sgonzo			if (err)
385239281Sgonzo				goto done;
386239281Sgonzo
387239281Sgonzo			if (!(grp & TWL6030_P1_GRP)) {
388239281Sgonzo				*enabled = 0; /* disabled */
389239281Sgonzo				goto done;
390239281Sgonzo			}
391239281Sgonzo		}
392239281Sgonzo
393239281Sgonzo		/* Read the application mode state and verify it's ON */
394239281Sgonzo		err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state);
395239281Sgonzo		if (err)
396239281Sgonzo			goto done;
397239281Sgonzo
398239281Sgonzo		*enabled = ((state & 0x0C) == 0x04);
399239281Sgonzo
400239281Sgonzo	} else {
401239281Sgonzo		err = EINVAL;
402239281Sgonzo	}
403239281Sgonzo
404239281Sgonzodone:
405239281Sgonzo	if (!xlocked)
406239281Sgonzo		TWL_VREG_LOCK_DOWNGRADE(sc);
407239281Sgonzo
408239281Sgonzo	return (err);
409239281Sgonzo}
410239281Sgonzo
411239281Sgonzo/**
412239281Sgonzo *	twl_vreg_disable_regulator - disables a voltage regulator
413239281Sgonzo *	@sc: the device soft context
414239281Sgonzo *	@regulator: pointer to the regulator device
415239281Sgonzo *
416239281Sgonzo *	Disables the regulator which will stop the output drivers.
417239281Sgonzo *
418239281Sgonzo *	LOCKING:
419239281Sgonzo *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
420239281Sgonzo *	exclusive if not already but, if so, it will be downgraded again before
421239281Sgonzo *	returning.
422239281Sgonzo *
423239281Sgonzo *	RETURNS:
424239281Sgonzo *	Zero on success or a positive error code on failure.
425239281Sgonzo */
426239281Sgonzostatic int
427239281Sgonzotwl_vreg_disable_regulator(struct twl_vreg_softc *sc,
428239281Sgonzo	struct twl_regulator_entry *regulator)
429239281Sgonzo{
430239281Sgonzo	int err = 0;
431239281Sgonzo	uint8_t grp;
432239281Sgonzo	int xlocked;
433239281Sgonzo
434239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
435239281Sgonzo
436239281Sgonzo	xlocked = sx_xlocked(&sc->sc_sx);
437239281Sgonzo	if (!xlocked)
438239281Sgonzo		TWL_VREG_LOCK_UPGRADE(sc);
439239281Sgonzo
440239281Sgonzo	if (twl_is_4030(sc->sc_pdev)) {
441239281Sgonzo
442239281Sgonzo		/* Read the regulator CFG_GRP register */
443239281Sgonzo		err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
444239281Sgonzo		if (err)
445239281Sgonzo			goto done;
446239281Sgonzo
447239281Sgonzo		/* On the TWL4030 we just need to remove the regulator from all the
448239281Sgonzo		 * power groups.
449239281Sgonzo		 */
450239281Sgonzo		grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
451239281Sgonzo		err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
452239281Sgonzo
453239281Sgonzo	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
454239281Sgonzo
455239281Sgonzo		/* On TWL6030 we need to make sure we disable power for all groups */
456239281Sgonzo		if (twl_is_6030(sc->sc_pdev))
457239281Sgonzo			grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
458239281Sgonzo		else
459239281Sgonzo			grp = 0x00;
460239281Sgonzo
461239281Sgonzo		/* Write the resource state to "OFF" */
462239281Sgonzo		err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5));
463239281Sgonzo	}
464239281Sgonzo
465239281Sgonzodone:
466239281Sgonzo	if (!xlocked)
467239281Sgonzo		TWL_VREG_LOCK_DOWNGRADE(sc);
468239281Sgonzo
469239281Sgonzo	return (err);
470239281Sgonzo}
471239281Sgonzo
472239281Sgonzo/**
473239281Sgonzo *	twl_vreg_enable_regulator - enables the voltage regulator
474239281Sgonzo *	@sc: the device soft context
475239281Sgonzo *	@regulator: pointer to the regulator device
476239281Sgonzo *
477239281Sgonzo *	Enables the regulator which will enable the voltage out at the currently
478239281Sgonzo *	set voltage.  Set the voltage before calling this function to avoid
479239281Sgonzo *	driving the voltage too high/low by mistake.
480239281Sgonzo *
481239281Sgonzo *	LOCKING:
482239281Sgonzo *	On entry expects the TWL VREG lock to be held. Will upgrade the lock to
483239281Sgonzo *	exclusive if not already but, if so, it will be downgraded again before
484239281Sgonzo *	returning.
485239281Sgonzo *
486239281Sgonzo *	RETURNS:
487239281Sgonzo *	Zero on success or a positive error code on failure.
488239281Sgonzo */
489239281Sgonzostatic int
490239281Sgonzotwl_vreg_enable_regulator(struct twl_vreg_softc *sc,
491239281Sgonzo    struct twl_regulator_entry *regulator)
492239281Sgonzo{
493239281Sgonzo	int err;
494239281Sgonzo	uint8_t grp;
495239281Sgonzo	int xlocked;
496239281Sgonzo
497239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
498239281Sgonzo
499239281Sgonzo	xlocked = sx_xlocked(&sc->sc_sx);
500239281Sgonzo	if (!xlocked)
501239281Sgonzo		TWL_VREG_LOCK_UPGRADE(sc);
502239281Sgonzo
503239281Sgonzo
504239281Sgonzo	err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
505239281Sgonzo	if (err)
506239281Sgonzo		goto done;
507239281Sgonzo
508239281Sgonzo	/* Enable the regulator by ensuring it's in the application power group
509239281Sgonzo	 * and is in the "on" state.
510239281Sgonzo	 */
511239281Sgonzo	if (twl_is_4030(sc->sc_pdev)) {
512239281Sgonzo
513239281Sgonzo		/* On the TWL4030 we just need to ensure the regulator is in the right
514239281Sgonzo		 * power domain, don't need to turn on explicitly like TWL6030.
515239281Sgonzo		 */
516239281Sgonzo		grp |= TWL4030_P1_GRP;
517239281Sgonzo		err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
518239281Sgonzo
519239281Sgonzo	} else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
520239281Sgonzo
521239281Sgonzo		if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
522239281Sgonzo			grp |= TWL6030_P1_GRP;
523239281Sgonzo			err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
524239281Sgonzo			if (err)
525239281Sgonzo				goto done;
526239281Sgonzo		}
527239281Sgonzo
528239281Sgonzo		/* Write the resource state to "ON" */
529239281Sgonzo		err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01);
530239281Sgonzo	}
531239281Sgonzo
532239281Sgonzodone:
533239281Sgonzo	if (!xlocked)
534239281Sgonzo		TWL_VREG_LOCK_DOWNGRADE(sc);
535239281Sgonzo
536239281Sgonzo	return (err);
537239281Sgonzo}
538239281Sgonzo
539239281Sgonzo/**
540239281Sgonzo *	twl_vreg_write_regulator_voltage - sets the voltage level on a regulator
541239281Sgonzo *	@sc: the device soft context
542239281Sgonzo *	@regulator: pointer to the regulator structure
543239281Sgonzo *	@millivolts: the voltage to set
544239281Sgonzo *
545239281Sgonzo *	Sets the voltage output on a given regulator, if the regulator is not
546239281Sgonzo *	enabled, it will be enabled.
547239281Sgonzo *
548239281Sgonzo *	LOCKING:
549239281Sgonzo *	On entry expects the TWL VREG lock to be held, may upgrade the lock to
550239281Sgonzo *	exclusive but if so it will be downgraded once again before returning.
551239281Sgonzo *
552239281Sgonzo *	RETURNS:
553239281Sgonzo *	Zero on success or an error code on failure.
554239281Sgonzo */
555239281Sgonzostatic int
556239281Sgonzotwl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc,
557239281Sgonzo    struct twl_regulator_entry *regulator, int millivolts)
558239281Sgonzo{
559239281Sgonzo	int err;
560239281Sgonzo	uint8_t vsel;
561239281Sgonzo	int xlocked;
562239281Sgonzo
563239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
564239281Sgonzo
565239281Sgonzo	/* If millivolts is zero then we simply disable the output */
566239281Sgonzo	if (millivolts == 0)
567239281Sgonzo		return (twl_vreg_disable_regulator(sc, regulator));
568239281Sgonzo
569239281Sgonzo	/* If the regulator has a fixed voltage then check the setting matches
570239281Sgonzo	 * and simply enable.
571239281Sgonzo	 */
572239281Sgonzo	if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) {
573239281Sgonzo		if (millivolts != regulator->fixed_voltage)
574239281Sgonzo			return (EINVAL);
575239281Sgonzo
576239281Sgonzo		return (twl_vreg_enable_regulator(sc, regulator));
577239281Sgonzo	}
578239281Sgonzo
579239281Sgonzo	/* Get the VSEL value for the given voltage */
580239281Sgonzo	err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel);
581239281Sgonzo	if (err)
582239281Sgonzo		return (err);
583239281Sgonzo
584239281Sgonzo
585239281Sgonzo	/* Need to upgrade because writing the voltage and enabling should be atomic */
586239281Sgonzo	xlocked = sx_xlocked(&sc->sc_sx);
587239281Sgonzo	if (!xlocked)
588239281Sgonzo		TWL_VREG_LOCK_UPGRADE(sc);
589239281Sgonzo
590239281Sgonzo
591239281Sgonzo	/* Set voltage and enable (atomically) */
592239281Sgonzo	err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f));
593239281Sgonzo	if (!err) {
594239281Sgonzo		err = twl_vreg_enable_regulator(sc, regulator);
595239281Sgonzo	}
596239281Sgonzo
597239281Sgonzo	if (!xlocked)
598239281Sgonzo		TWL_VREG_LOCK_DOWNGRADE(sc);
599239281Sgonzo
600239281Sgonzo	if ((twl_vreg_debug > 1) && !err)
601239281Sgonzo		device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n",
602239281Sgonzo		    regulator->name, millivolts, vsel);
603239281Sgonzo
604239281Sgonzo	return (err);
605239281Sgonzo}
606239281Sgonzo
607239281Sgonzo/**
608239281Sgonzo *	twl_vreg_read_regulator_voltage - reads the voltage on a given regulator
609239281Sgonzo *	@sc: the device soft context
610239281Sgonzo *	@regulator: pointer to the regulator structure
611239281Sgonzo *	@millivolts: upon return will contain the voltage on the regulator
612239281Sgonzo *
613239281Sgonzo *	LOCKING:
614239281Sgonzo *	On entry expects the TWL VREG lock to be held. It will upgrade the lock to
615239281Sgonzo *	exclusive if not already, but if so, it will be downgraded again before
616239281Sgonzo *	returning.
617239281Sgonzo *
618239281Sgonzo *	RETURNS:
619239281Sgonzo *	Zero on success, or otherwise an error code.
620239281Sgonzo */
621239281Sgonzostatic int
622239281Sgonzotwl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc,
623239281Sgonzo    struct twl_regulator_entry *regulator, int *millivolts)
624239281Sgonzo{
625239281Sgonzo	int err;
626239281Sgonzo	int en = 0;
627239281Sgonzo	int xlocked;
628239281Sgonzo	uint8_t vsel;
629239281Sgonzo
630239281Sgonzo	TWL_VREG_ASSERT_LOCKED(sc);
631239281Sgonzo
632239281Sgonzo	/* Need to upgrade the lock because checking enabled state and voltage
633239281Sgonzo	 * should be atomic.
634239281Sgonzo	 */
635239281Sgonzo	xlocked = sx_xlocked(&sc->sc_sx);
636239281Sgonzo	if (!xlocked)
637239281Sgonzo		TWL_VREG_LOCK_UPGRADE(sc);
638239281Sgonzo
639239281Sgonzo
640239281Sgonzo	/* Check if the regulator is currently enabled */
641239281Sgonzo	err = twl_vreg_is_regulator_enabled(sc, regulator, &en);
642239281Sgonzo	if (err)
643239281Sgonzo		goto done;
644239281Sgonzo
645239281Sgonzo	*millivolts = 0;
646239281Sgonzo	if (!en)
647239281Sgonzo		goto done;
648239281Sgonzo
649239281Sgonzo
650239281Sgonzo	/* Not all voltages are adjustable */
651239281Sgonzo	if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) {
652239281Sgonzo		*millivolts = regulator->fixed_voltage;
653239281Sgonzo		goto done;
654239281Sgonzo	}
655239281Sgonzo
656239281Sgonzo	/* For variable voltages read the voltage register */
657239281Sgonzo	err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel);
658239281Sgonzo	if (err)
659239281Sgonzo		goto done;
660239281Sgonzo
661239281Sgonzo	vsel &= (regulator->num_supp_voltages - 1);
662239281Sgonzo	if (regulator->supp_voltages[vsel] == UNDF) {
663239281Sgonzo		err = EINVAL;
664239281Sgonzo		goto done;
665239281Sgonzo	}
666239281Sgonzo
667239281Sgonzo	*millivolts = regulator->supp_voltages[vsel];
668239281Sgonzo
669239281Sgonzodone:
670239281Sgonzo	if (!xlocked)
671239281Sgonzo		TWL_VREG_LOCK_DOWNGRADE(sc);
672239281Sgonzo
673239281Sgonzo	if ((twl_vreg_debug > 1) && !err)
674239281Sgonzo		device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n",
675239281Sgonzo		    regulator->name, *millivolts, vsel);
676239281Sgonzo
677239281Sgonzo	return (err);
678239281Sgonzo}
679239281Sgonzo
680239281Sgonzo/**
681239281Sgonzo *	twl_vreg_get_voltage - public interface to read the voltage on a regulator
682239281Sgonzo *	@dev: TWL VREG device
683239281Sgonzo *	@name: the name of the regulator to read the voltage of
684239281Sgonzo *	@millivolts: pointer to an integer that upon return will contain the mV
685239281Sgonzo *
686239281Sgonzo *	If the regulator is disabled the function will set the @millivolts to zero.
687239281Sgonzo *
688239281Sgonzo *	LOCKING:
689239281Sgonzo *	Internally the function takes and releases the TWL VREG lock.
690239281Sgonzo *
691239281Sgonzo *	RETURNS:
692239281Sgonzo *	Zero on success or a negative error code on failure.
693239281Sgonzo */
694239281Sgonzoint
695239281Sgonzotwl_vreg_get_voltage(device_t dev, const char *name, int *millivolts)
696239281Sgonzo{
697239281Sgonzo	struct twl_vreg_softc *sc;
698239281Sgonzo	struct twl_regulator_entry *regulator;
699239281Sgonzo	int err = EINVAL;
700239281Sgonzo
701239281Sgonzo	if (millivolts == NULL)
702239281Sgonzo		return (EINVAL);
703239281Sgonzo
704239281Sgonzo	sc = device_get_softc(dev);
705239281Sgonzo
706239281Sgonzo	TWL_VREG_SLOCK(sc);
707239281Sgonzo
708239281Sgonzo	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
709239281Sgonzo		if (strcmp(regulator->name, name) == 0) {
710239281Sgonzo			err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts);
711239281Sgonzo			break;
712239281Sgonzo		}
713239281Sgonzo	}
714239281Sgonzo
715239281Sgonzo	TWL_VREG_SUNLOCK(sc);
716239281Sgonzo
717239281Sgonzo	return (err);
718239281Sgonzo}
719239281Sgonzo
720239281Sgonzo/**
721239281Sgonzo *	twl_vreg_set_voltage - public interface to write the voltage on a regulator
722239281Sgonzo *	@dev: TWL VREG device
723239281Sgonzo *	@name: the name of the regulator to read the voltage of
724239281Sgonzo *	@millivolts: the voltage to set in millivolts
725239281Sgonzo *
726239281Sgonzo *	Sets the output voltage on a given regulator. If the regulator is a fixed
727239281Sgonzo *	voltage reg then the @millivolts value should match the fixed voltage. If
728239281Sgonzo *	a variable regulator then the @millivolt value must fit within the max/min
729239281Sgonzo *	range of the given regulator.
730239281Sgonzo *
731239281Sgonzo *	LOCKING:
732239281Sgonzo *	Internally the function takes and releases the TWL VREG lock.
733239281Sgonzo *
734239281Sgonzo *	RETURNS:
735239281Sgonzo *	Zero on success or a negative error code on failure.
736239281Sgonzo */
737239281Sgonzoint
738239281Sgonzotwl_vreg_set_voltage(device_t dev, const char *name, int millivolts)
739239281Sgonzo{
740239281Sgonzo	struct twl_vreg_softc *sc;
741239281Sgonzo	struct twl_regulator_entry *regulator;
742239281Sgonzo	int err = EINVAL;
743239281Sgonzo
744239281Sgonzo	sc = device_get_softc(dev);
745239281Sgonzo
746239281Sgonzo	TWL_VREG_SLOCK(sc);
747239281Sgonzo
748239281Sgonzo	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
749239281Sgonzo		if (strcmp(regulator->name, name) == 0) {
750239281Sgonzo			err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts);
751239281Sgonzo			break;
752239281Sgonzo		}
753239281Sgonzo	}
754239281Sgonzo
755239281Sgonzo	TWL_VREG_SUNLOCK(sc);
756239281Sgonzo
757239281Sgonzo	return (err);
758239281Sgonzo}
759239281Sgonzo
760239281Sgonzo/**
761239281Sgonzo *	twl_sysctl_voltage - reads or writes the voltage for a regulator
762239281Sgonzo *	@SYSCTL_HANDLER_ARGS: arguments for the callback
763239281Sgonzo *
764239281Sgonzo *	Callback for the sysctl entry for the regulator, simply used to return
765239281Sgonzo *	the voltage on a particular regulator.
766239281Sgonzo *
767239281Sgonzo *	LOCKING:
768239281Sgonzo *	Takes the TWL_VREG shared lock internally.
769239281Sgonzo *
770239281Sgonzo *	RETURNS:
771239281Sgonzo *	Zero on success or an error code on failure.
772239281Sgonzo */
773239281Sgonzostatic int
774239281Sgonzotwl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS)
775239281Sgonzo{
776239281Sgonzo	struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1;
777239281Sgonzo	struct twl_regulator_entry *regulator;
778239281Sgonzo	int voltage;
779239281Sgonzo	int found = 0;
780239281Sgonzo
781239281Sgonzo	TWL_VREG_SLOCK(sc);
782239281Sgonzo
783239281Sgonzo	/* Find the regulator with the matching name */
784239281Sgonzo	LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
785239281Sgonzo		if (strcmp(regulator->name, oidp->oid_name) == 0) {
786239281Sgonzo			found = 1;
787239281Sgonzo			break;
788239281Sgonzo		}
789239281Sgonzo	}
790239281Sgonzo
791239281Sgonzo	/* Sanity check that we found the regulator */
792239281Sgonzo	if (!found) {
793239281Sgonzo		TWL_VREG_SUNLOCK(sc);
794239281Sgonzo		return (EINVAL);
795239281Sgonzo	}
796239281Sgonzo
797239281Sgonzo	twl_vreg_read_regulator_voltage(sc, regulator, &voltage);
798239281Sgonzo
799239281Sgonzo	TWL_VREG_SUNLOCK(sc);
800239281Sgonzo
801239281Sgonzo	return sysctl_handle_int(oidp, &voltage, 0, req);
802239281Sgonzo}
803239281Sgonzo
804239281Sgonzo/**
805239281Sgonzo *	twl_add_regulator - adds single voltage regulator sysctls for the device
806239281Sgonzo *	@sc: device soft context
807239281Sgonzo *	@name: the name of the regulator
808239281Sgonzo *	@nsub: the number of the subdevice
809239281Sgonzo *	@regbase: the base address of the voltage regulator registers
810239281Sgonzo *	@fixed_voltage: if a fixed voltage regulator this defines it's voltage
811239281Sgonzo *	@voltages: if a variable voltage regulator, an array of possible voltages
812239281Sgonzo *	@num_voltages: the number of entries @voltages
813239281Sgonzo *
814239281Sgonzo *	Adds a voltage regulator to the device and also a sysctl interface for the
815239281Sgonzo *	regulator.
816239281Sgonzo *
817239281Sgonzo *	LOCKING:
818239281Sgonzo *	The TWL_VEG exclusive lock must be held while this function is called.
819239281Sgonzo *
820239281Sgonzo *	RETURNS:
821239281Sgonzo *	Pointer to the new regulator entry on success, otherwise on failure NULL.
822239281Sgonzo */
823239281Sgonzostatic struct twl_regulator_entry*
824239281Sgonzotwl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name,
825239281Sgonzo	uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage,
826239281Sgonzo	const uint16_t *voltages, uint32_t num_voltages)
827239281Sgonzo{
828239281Sgonzo	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
829239281Sgonzo	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
830239281Sgonzo	struct twl_regulator_entry *new;
831239281Sgonzo
832239281Sgonzo	new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
833239281Sgonzo	if (new == NULL)
834239281Sgonzo		return (NULL);
835239281Sgonzo
836239281Sgonzo
837239281Sgonzo	strncpy(new->name, name, TWL_VREG_MAX_NAMELEN);
838239281Sgonzo	new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0';
839239281Sgonzo
840239281Sgonzo	new->sub_dev = nsub;
841239281Sgonzo	new->reg_off = regbase;
842239281Sgonzo
843239281Sgonzo	new->fixed_voltage = fixed_voltage;
844239281Sgonzo
845239281Sgonzo	new->supp_voltages = voltages;
846239281Sgonzo	new->num_supp_voltages = num_voltages;
847239281Sgonzo
848239281Sgonzo
849239281Sgonzo	/* Add a sysctl entry for the voltage */
850239281Sgonzo	new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
851239281Sgonzo	    CTLTYPE_INT | CTLFLAG_RD, sc, 0,
852239281Sgonzo	    twl_vreg_sysctl_voltage, "I", "voltage regulator");
853239281Sgonzo
854239281Sgonzo	/* Finally add the regulator to list of supported regulators */
855239281Sgonzo	LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries);
856239281Sgonzo
857239281Sgonzo	return (new);
858239281Sgonzo}
859239281Sgonzo
860239281Sgonzo/**
861239281Sgonzo *	twl_vreg_add_regulators - adds any voltage regulators to the device
862239281Sgonzo *	@sc: device soft context
863239281Sgonzo *	@chip: the name of the chip used in the hints
864239281Sgonzo *	@regulators: the list of possible voltage regulators
865239281Sgonzo *
866239281Sgonzo *	Loops over the list of regulators and matches up with the FDT values,
867239281Sgonzo *	adjusting the actual voltage based on the supplied values.
868239281Sgonzo *
869239281Sgonzo *	LOCKING:
870239281Sgonzo *	The TWL_VEG exclusive lock must be held while this function is called.
871239281Sgonzo *
872239281Sgonzo *	RETURNS:
873239281Sgonzo *	Always returns 0.
874239281Sgonzo */
875239281Sgonzostatic int
876239281Sgonzotwl_vreg_add_regulators(struct twl_vreg_softc *sc,
877239281Sgonzo	const struct twl_regulator *regulators)
878239281Sgonzo{
879239281Sgonzo	int err;
880239281Sgonzo	int millivolts;
881239281Sgonzo	const struct twl_regulator *walker;
882239281Sgonzo	struct twl_regulator_entry *entry;
883239281Sgonzo	phandle_t child;
884239281Sgonzo	char rnames[256];
885239281Sgonzo	char *name, *voltage;
886239281Sgonzo	int len = 0, prop_len;
887239281Sgonzo
888239281Sgonzo
889239281Sgonzo	/* Add the regulators from the list */
890239281Sgonzo	walker = &regulators[0];
891239281Sgonzo	while (walker->name != NULL) {
892239281Sgonzo
893239281Sgonzo		/* Add the regulator to the list */
894239281Sgonzo		entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev,
895239281Sgonzo		    walker->regbase, walker->fixedvoltage,
896239281Sgonzo		    walker->voltages, walker->num_voltages);
897239281Sgonzo		if (entry == NULL)
898239281Sgonzo			continue;
899239281Sgonzo
900239281Sgonzo		walker++;
901239281Sgonzo	}
902239281Sgonzo
903239281Sgonzo
904239281Sgonzo	/* Check if the FDT is telling us to set any voltages */
905239281Sgonzo	child = ofw_bus_get_node(sc->sc_pdev);
906239281Sgonzo	if (child) {
907239281Sgonzo
908239281Sgonzo		prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames));
909239281Sgonzo		while (len < prop_len) {
910239281Sgonzo			name = rnames + len;
911239281Sgonzo			len += strlen(name) + 1;
912239281Sgonzo			if ((len >= prop_len) || (name[0] == '\0'))
913239281Sgonzo				break;
914239281Sgonzo
915239281Sgonzo			voltage = rnames + len;
916239281Sgonzo			len += strlen(voltage) + 1;
917239281Sgonzo			if (voltage[0] == '\0')
918239281Sgonzo				break;
919239281Sgonzo
920239281Sgonzo			millivolts = strtoul(voltage, NULL, 0);
921239281Sgonzo
922239281Sgonzo			LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
923239281Sgonzo				if (strcmp(entry->name, name) == 0) {
924239281Sgonzo					twl_vreg_write_regulator_voltage(sc, entry, millivolts);
925239281Sgonzo					break;
926239281Sgonzo				}
927239281Sgonzo			}
928239281Sgonzo		}
929239281Sgonzo	}
930239281Sgonzo
931239281Sgonzo
932239281Sgonzo	if (twl_vreg_debug) {
933239281Sgonzo		LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
934239281Sgonzo			err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts);
935239281Sgonzo			if (!err)
936239281Sgonzo				device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts);
937239281Sgonzo		}
938239281Sgonzo	}
939239281Sgonzo
940239281Sgonzo	return (0);
941239281Sgonzo}
942239281Sgonzo
943239281Sgonzo/**
944239281Sgonzo *	twl_vreg_init - initialises the list of regulators
945239281Sgonzo *	@dev: the twl_vreg device
946239281Sgonzo *
947239281Sgonzo *	This function is called as an intrhook once interrupts have been enabled,
948239281Sgonzo *	this is done so that the driver has the option to enable/disable or set
949239281Sgonzo *	the voltage level based on settings providied in the FDT.
950239281Sgonzo *
951239281Sgonzo *	LOCKING:
952239281Sgonzo *	Takes the exclusive lock in the function.
953239281Sgonzo */
954239281Sgonzostatic void
955239281Sgonzotwl_vreg_init(void *dev)
956239281Sgonzo{
957239281Sgonzo	struct twl_vreg_softc *sc;
958239281Sgonzo
959239281Sgonzo	sc = device_get_softc((device_t)dev);
960239281Sgonzo
961239281Sgonzo	TWL_VREG_XLOCK(sc);
962239281Sgonzo
963239281Sgonzo	if (twl_is_4030(sc->sc_pdev))
964239281Sgonzo		twl_vreg_add_regulators(sc, twl4030_regulators);
965239281Sgonzo	else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
966239281Sgonzo		twl_vreg_add_regulators(sc, twl6030_regulators);
967239281Sgonzo
968239281Sgonzo	TWL_VREG_XUNLOCK(sc);
969239281Sgonzo
970239281Sgonzo	config_intrhook_disestablish(&sc->sc_init_hook);
971239281Sgonzo}
972239281Sgonzo
973239281Sgonzostatic int
974239281Sgonzotwl_vreg_probe(device_t dev)
975239281Sgonzo{
976239281Sgonzo	if (twl_is_4030(device_get_parent(dev)))
977239281Sgonzo		device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators");
978239281Sgonzo	else if (twl_is_6025(device_get_parent(dev)) ||
979239281Sgonzo	         twl_is_6030(device_get_parent(dev)))
980239281Sgonzo		device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators");
981239281Sgonzo	else
982239281Sgonzo		return (ENXIO);
983239281Sgonzo
984239281Sgonzo	return (0);
985239281Sgonzo}
986239281Sgonzo
987239281Sgonzostatic int
988239281Sgonzotwl_vreg_attach(device_t dev)
989239281Sgonzo{
990239281Sgonzo	struct twl_vreg_softc *sc;
991239281Sgonzo
992239281Sgonzo	sc = device_get_softc(dev);
993239281Sgonzo	sc->sc_dev = dev;
994239281Sgonzo	sc->sc_pdev = device_get_parent(dev);
995239281Sgonzo
996239281Sgonzo	TWL_VREG_LOCK_INIT(sc);
997239281Sgonzo
998239281Sgonzo	LIST_INIT(&sc->sc_vreg_list);
999239281Sgonzo
1000239281Sgonzo	/* We have to wait until interrupts are enabled. I2C read and write
1001239281Sgonzo	 * only works if the interrupts are available.
1002239281Sgonzo	 */
1003239281Sgonzo	sc->sc_init_hook.ich_func = twl_vreg_init;
1004239281Sgonzo	sc->sc_init_hook.ich_arg = dev;
1005239281Sgonzo
1006239281Sgonzo	if (config_intrhook_establish(&sc->sc_init_hook) != 0)
1007239281Sgonzo		return (ENOMEM);
1008239281Sgonzo
1009239281Sgonzo	return (0);
1010239281Sgonzo}
1011239281Sgonzo
1012239281Sgonzostatic int
1013239281Sgonzotwl_vreg_detach(device_t dev)
1014239281Sgonzo{
1015239281Sgonzo	struct twl_vreg_softc *sc;
1016239281Sgonzo	struct twl_regulator_entry *regulator;
1017239281Sgonzo	struct twl_regulator_entry *tmp;
1018239281Sgonzo
1019239281Sgonzo	sc = device_get_softc(dev);
1020239281Sgonzo
1021239281Sgonzo	/* Take the lock and free all the added regulators */
1022239281Sgonzo	TWL_VREG_XLOCK(sc);
1023239281Sgonzo
1024239281Sgonzo	LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) {
1025239281Sgonzo		LIST_REMOVE(regulator, entries);
1026239281Sgonzo		sysctl_remove_oid(regulator->oid, 1, 0);
1027239281Sgonzo		free(regulator, M_DEVBUF);
1028239281Sgonzo	}
1029239281Sgonzo
1030239281Sgonzo	TWL_VREG_XUNLOCK(sc);
1031239281Sgonzo
1032239281Sgonzo	TWL_VREG_LOCK_DESTROY(sc);
1033239281Sgonzo
1034239281Sgonzo	return (0);
1035239281Sgonzo}
1036239281Sgonzo
1037239281Sgonzostatic device_method_t twl_vreg_methods[] = {
1038239281Sgonzo	DEVMETHOD(device_probe,		twl_vreg_probe),
1039239281Sgonzo	DEVMETHOD(device_attach,	twl_vreg_attach),
1040239281Sgonzo	DEVMETHOD(device_detach,	twl_vreg_detach),
1041239281Sgonzo
1042239281Sgonzo	{0, 0},
1043239281Sgonzo};
1044239281Sgonzo
1045239281Sgonzostatic driver_t twl_vreg_driver = {
1046239281Sgonzo	"twl_vreg",
1047239281Sgonzo	twl_vreg_methods,
1048239281Sgonzo	sizeof(struct twl_vreg_softc),
1049239281Sgonzo};
1050239281Sgonzo
1051239281Sgonzostatic devclass_t twl_vreg_devclass;
1052239281Sgonzo
1053239281SgonzoDRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, twl_vreg_devclass, 0, 0);
1054239281SgonzoMODULE_VERSION(twl_vreg, 1);
1055