xref: /openbmc/linux/drivers/mfd/twl6040.c (revision 75c08f17)
1210afeecSPeter Ujfalusi /*
2210afeecSPeter Ujfalusi  * MFD driver for TWL6040 audio device
3210afeecSPeter Ujfalusi  *
4210afeecSPeter Ujfalusi  * Authors:	Misael Lopez Cruz <misael.lopez@ti.com>
5210afeecSPeter Ujfalusi  *		Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
6210afeecSPeter Ujfalusi  *		Peter Ujfalusi <peter.ujfalusi@ti.com>
7210afeecSPeter Ujfalusi  *
8210afeecSPeter Ujfalusi  * Copyright:	(C) 2011 Texas Instruments, Inc.
9210afeecSPeter Ujfalusi  *
10210afeecSPeter Ujfalusi  * This program is free software; you can redistribute it and/or modify
11210afeecSPeter Ujfalusi  * it under the terms of the GNU General Public License version 2 as
12210afeecSPeter Ujfalusi  * published by the Free Software Foundation.
13210afeecSPeter Ujfalusi  *
14210afeecSPeter Ujfalusi  * This program is distributed in the hope that it will be useful, but
15210afeecSPeter Ujfalusi  * WITHOUT ANY WARRANTY; without even the implied warranty of
16210afeecSPeter Ujfalusi  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17210afeecSPeter Ujfalusi  * General Public License for more details.
18210afeecSPeter Ujfalusi  *
19210afeecSPeter Ujfalusi  * You should have received a copy of the GNU General Public License
20210afeecSPeter Ujfalusi  * along with this program; if not, write to the Free Software
21210afeecSPeter Ujfalusi  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22210afeecSPeter Ujfalusi  * 02110-1301 USA
23210afeecSPeter Ujfalusi  *
24210afeecSPeter Ujfalusi  */
25210afeecSPeter Ujfalusi 
26210afeecSPeter Ujfalusi #include <linux/module.h>
27210afeecSPeter Ujfalusi #include <linux/types.h>
28210afeecSPeter Ujfalusi #include <linux/slab.h>
29210afeecSPeter Ujfalusi #include <linux/kernel.h>
30210afeecSPeter Ujfalusi #include <linux/err.h>
31210afeecSPeter Ujfalusi #include <linux/platform_device.h>
32210afeecSPeter Ujfalusi #include <linux/of.h>
33210afeecSPeter Ujfalusi #include <linux/of_irq.h>
34210afeecSPeter Ujfalusi #include <linux/of_gpio.h>
35210afeecSPeter Ujfalusi #include <linux/of_platform.h>
36210afeecSPeter Ujfalusi #include <linux/gpio.h>
37210afeecSPeter Ujfalusi #include <linux/delay.h>
38210afeecSPeter Ujfalusi #include <linux/i2c.h>
39210afeecSPeter Ujfalusi #include <linux/regmap.h>
40210afeecSPeter Ujfalusi #include <linux/mfd/core.h>
41210afeecSPeter Ujfalusi #include <linux/mfd/twl6040.h>
42210afeecSPeter Ujfalusi #include <linux/regulator/consumer.h>
43210afeecSPeter Ujfalusi 
44210afeecSPeter Ujfalusi #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
45210afeecSPeter Ujfalusi #define TWL6040_NUM_SUPPLIES	(2)
46210afeecSPeter Ujfalusi 
47de1e23f8SKrzysztof Kozlowski static const struct reg_default twl6040_defaults[] = {
48c7f9129dSPeter Ujfalusi 	{ 0x01, 0x4B }, /* REG_ASICID	(ro) */
49c7f9129dSPeter Ujfalusi 	{ 0x02, 0x00 }, /* REG_ASICREV	(ro) */
50c7f9129dSPeter Ujfalusi 	{ 0x03, 0x00 }, /* REG_INTID	*/
51c7f9129dSPeter Ujfalusi 	{ 0x04, 0x00 }, /* REG_INTMR	*/
52c7f9129dSPeter Ujfalusi 	{ 0x05, 0x00 }, /* REG_NCPCTRL	*/
53c7f9129dSPeter Ujfalusi 	{ 0x06, 0x00 }, /* REG_LDOCTL	*/
54c7f9129dSPeter Ujfalusi 	{ 0x07, 0x60 }, /* REG_HPPLLCTL	*/
55c7f9129dSPeter Ujfalusi 	{ 0x08, 0x00 }, /* REG_LPPLLCTL	*/
56c7f9129dSPeter Ujfalusi 	{ 0x09, 0x4A }, /* REG_LPPLLDIV	*/
57c7f9129dSPeter Ujfalusi 	{ 0x0A, 0x00 }, /* REG_AMICBCTL	*/
58c7f9129dSPeter Ujfalusi 	{ 0x0B, 0x00 }, /* REG_DMICBCTL	*/
59c7f9129dSPeter Ujfalusi 	{ 0x0C, 0x00 }, /* REG_MICLCTL	*/
60c7f9129dSPeter Ujfalusi 	{ 0x0D, 0x00 }, /* REG_MICRCTL	*/
61c7f9129dSPeter Ujfalusi 	{ 0x0E, 0x00 }, /* REG_MICGAIN	*/
62c7f9129dSPeter Ujfalusi 	{ 0x0F, 0x1B }, /* REG_LINEGAIN	*/
63c7f9129dSPeter Ujfalusi 	{ 0x10, 0x00 }, /* REG_HSLCTL	*/
64c7f9129dSPeter Ujfalusi 	{ 0x11, 0x00 }, /* REG_HSRCTL	*/
65c7f9129dSPeter Ujfalusi 	{ 0x12, 0x00 }, /* REG_HSGAIN	*/
66c7f9129dSPeter Ujfalusi 	{ 0x13, 0x00 }, /* REG_EARCTL	*/
67c7f9129dSPeter Ujfalusi 	{ 0x14, 0x00 }, /* REG_HFLCTL	*/
68c7f9129dSPeter Ujfalusi 	{ 0x15, 0x00 }, /* REG_HFLGAIN	*/
69c7f9129dSPeter Ujfalusi 	{ 0x16, 0x00 }, /* REG_HFRCTL	*/
70c7f9129dSPeter Ujfalusi 	{ 0x17, 0x00 }, /* REG_HFRGAIN	*/
71c7f9129dSPeter Ujfalusi 	{ 0x18, 0x00 }, /* REG_VIBCTLL	*/
72c7f9129dSPeter Ujfalusi 	{ 0x19, 0x00 }, /* REG_VIBDATL	*/
73c7f9129dSPeter Ujfalusi 	{ 0x1A, 0x00 }, /* REG_VIBCTLR	*/
74c7f9129dSPeter Ujfalusi 	{ 0x1B, 0x00 }, /* REG_VIBDATR	*/
75c7f9129dSPeter Ujfalusi 	{ 0x1C, 0x00 }, /* REG_HKCTL1	*/
76c7f9129dSPeter Ujfalusi 	{ 0x1D, 0x00 }, /* REG_HKCTL2	*/
77c7f9129dSPeter Ujfalusi 	{ 0x1E, 0x00 }, /* REG_GPOCTL	*/
78c7f9129dSPeter Ujfalusi 	{ 0x1F, 0x00 }, /* REG_ALB	*/
79c7f9129dSPeter Ujfalusi 	{ 0x20, 0x00 }, /* REG_DLB	*/
80c7f9129dSPeter Ujfalusi 	/* 0x28, REG_TRIM1 */
81c7f9129dSPeter Ujfalusi 	/* 0x29, REG_TRIM2 */
82c7f9129dSPeter Ujfalusi 	/* 0x2A, REG_TRIM3 */
83c7f9129dSPeter Ujfalusi 	/* 0x2B, REG_HSOTRIM */
84c7f9129dSPeter Ujfalusi 	/* 0x2C, REG_HFOTRIM */
85c7f9129dSPeter Ujfalusi 	{ 0x2D, 0x08 }, /* REG_ACCCTL	*/
86c7f9129dSPeter Ujfalusi 	{ 0x2E, 0x00 }, /* REG_STATUS	(ro) */
87c7f9129dSPeter Ujfalusi };
88c7f9129dSPeter Ujfalusi 
898019ff6cSNariman Poushin static struct reg_sequence twl6040_patch[] = {
9011e38e11SPeter Ujfalusi 	/*
9111e38e11SPeter Ujfalusi 	 * Select I2C bus access to dual access registers
9211e38e11SPeter Ujfalusi 	 * Interrupt register is cleared on read
9311e38e11SPeter Ujfalusi 	 * Select fast mode for i2c (400KHz)
9411e38e11SPeter Ujfalusi 	 */
9511e38e11SPeter Ujfalusi 	{ TWL6040_REG_ACCCTL,
9611e38e11SPeter Ujfalusi 		TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) },
97c7f9129dSPeter Ujfalusi };
98c7f9129dSPeter Ujfalusi 
99c7f9129dSPeter Ujfalusi 
100df04b624SPeter Ujfalusi static bool twl6040_has_vibra(struct device_node *node)
101210afeecSPeter Ujfalusi {
102210afeecSPeter Ujfalusi #ifdef CONFIG_OF
103210afeecSPeter Ujfalusi 	if (of_find_node_by_name(node, "vibra"))
104210afeecSPeter Ujfalusi 		return true;
105210afeecSPeter Ujfalusi #endif
106210afeecSPeter Ujfalusi 	return false;
107210afeecSPeter Ujfalusi }
108210afeecSPeter Ujfalusi 
109210afeecSPeter Ujfalusi int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
110210afeecSPeter Ujfalusi {
111210afeecSPeter Ujfalusi 	int ret;
112210afeecSPeter Ujfalusi 	unsigned int val;
113210afeecSPeter Ujfalusi 
114210afeecSPeter Ujfalusi 	ret = regmap_read(twl6040->regmap, reg, &val);
115210afeecSPeter Ujfalusi 	if (ret < 0)
116210afeecSPeter Ujfalusi 		return ret;
117210afeecSPeter Ujfalusi 
118210afeecSPeter Ujfalusi 	return val;
119210afeecSPeter Ujfalusi }
120210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_read);
121210afeecSPeter Ujfalusi 
122210afeecSPeter Ujfalusi int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
123210afeecSPeter Ujfalusi {
124210afeecSPeter Ujfalusi 	int ret;
125210afeecSPeter Ujfalusi 
126210afeecSPeter Ujfalusi 	ret = regmap_write(twl6040->regmap, reg, val);
127210afeecSPeter Ujfalusi 
128210afeecSPeter Ujfalusi 	return ret;
129210afeecSPeter Ujfalusi }
130210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_write);
131210afeecSPeter Ujfalusi 
132210afeecSPeter Ujfalusi int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
133210afeecSPeter Ujfalusi {
134210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, mask);
135210afeecSPeter Ujfalusi }
136210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_bits);
137210afeecSPeter Ujfalusi 
138210afeecSPeter Ujfalusi int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
139210afeecSPeter Ujfalusi {
140210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, 0);
141210afeecSPeter Ujfalusi }
142210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_clear_bits);
143210afeecSPeter Ujfalusi 
144210afeecSPeter Ujfalusi /* twl6040 codec manual power-up sequence */
145210afeecSPeter Ujfalusi static int twl6040_power_up_manual(struct twl6040 *twl6040)
146210afeecSPeter Ujfalusi {
147210afeecSPeter Ujfalusi 	u8 ldoctl, ncpctl, lppllctl;
148210afeecSPeter Ujfalusi 	int ret;
149210afeecSPeter Ujfalusi 
150210afeecSPeter Ujfalusi 	/* enable high-side LDO, reference system and internal oscillator */
151210afeecSPeter Ujfalusi 	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
152210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
153210afeecSPeter Ujfalusi 	if (ret)
154210afeecSPeter Ujfalusi 		return ret;
155210afeecSPeter Ujfalusi 	usleep_range(10000, 10500);
156210afeecSPeter Ujfalusi 
157210afeecSPeter Ujfalusi 	/* enable negative charge pump */
158210afeecSPeter Ujfalusi 	ncpctl = TWL6040_NCPENA;
159210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
160210afeecSPeter Ujfalusi 	if (ret)
161210afeecSPeter Ujfalusi 		goto ncp_err;
162210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
163210afeecSPeter Ujfalusi 
164210afeecSPeter Ujfalusi 	/* enable low-side LDO */
165210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_LSLDOENA;
166210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
167210afeecSPeter Ujfalusi 	if (ret)
168210afeecSPeter Ujfalusi 		goto lsldo_err;
169210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
170210afeecSPeter Ujfalusi 
171210afeecSPeter Ujfalusi 	/* enable low-power PLL */
172210afeecSPeter Ujfalusi 	lppllctl = TWL6040_LPLLENA;
173210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
174210afeecSPeter Ujfalusi 	if (ret)
175210afeecSPeter Ujfalusi 		goto lppll_err;
176210afeecSPeter Ujfalusi 	usleep_range(5000, 5500);
177210afeecSPeter Ujfalusi 
178210afeecSPeter Ujfalusi 	/* disable internal oscillator */
179210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_OSCENA;
180210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
181210afeecSPeter Ujfalusi 	if (ret)
182210afeecSPeter Ujfalusi 		goto osc_err;
183210afeecSPeter Ujfalusi 
184210afeecSPeter Ujfalusi 	return 0;
185210afeecSPeter Ujfalusi 
186210afeecSPeter Ujfalusi osc_err:
187210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
188210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
189210afeecSPeter Ujfalusi lppll_err:
190210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
191210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
192210afeecSPeter Ujfalusi lsldo_err:
193210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
194210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
195210afeecSPeter Ujfalusi ncp_err:
196210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
197210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
198210afeecSPeter Ujfalusi 
199210afeecSPeter Ujfalusi 	dev_err(twl6040->dev, "manual power-up failed\n");
200210afeecSPeter Ujfalusi 	return ret;
201210afeecSPeter Ujfalusi }
202210afeecSPeter Ujfalusi 
203210afeecSPeter Ujfalusi /* twl6040 manual power-down sequence */
204210afeecSPeter Ujfalusi static void twl6040_power_down_manual(struct twl6040 *twl6040)
205210afeecSPeter Ujfalusi {
206210afeecSPeter Ujfalusi 	u8 ncpctl, ldoctl, lppllctl;
207210afeecSPeter Ujfalusi 
208210afeecSPeter Ujfalusi 	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
209210afeecSPeter Ujfalusi 	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
210210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
211210afeecSPeter Ujfalusi 
212210afeecSPeter Ujfalusi 	/* enable internal oscillator */
213210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_OSCENA;
214210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
215210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
216210afeecSPeter Ujfalusi 
217210afeecSPeter Ujfalusi 	/* disable low-power PLL */
218210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
219210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
220210afeecSPeter Ujfalusi 
221210afeecSPeter Ujfalusi 	/* disable low-side LDO */
222210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
223210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
224210afeecSPeter Ujfalusi 
225210afeecSPeter Ujfalusi 	/* disable negative charge pump */
226210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
227210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
228210afeecSPeter Ujfalusi 
229210afeecSPeter Ujfalusi 	/* disable high-side LDO, reference system and internal oscillator */
230210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
231210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
232210afeecSPeter Ujfalusi }
233210afeecSPeter Ujfalusi 
234210afeecSPeter Ujfalusi static irqreturn_t twl6040_readyint_handler(int irq, void *data)
235210afeecSPeter Ujfalusi {
236210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
237210afeecSPeter Ujfalusi 
238210afeecSPeter Ujfalusi 	complete(&twl6040->ready);
239210afeecSPeter Ujfalusi 
240210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
241210afeecSPeter Ujfalusi }
242210afeecSPeter Ujfalusi 
243210afeecSPeter Ujfalusi static irqreturn_t twl6040_thint_handler(int irq, void *data)
244210afeecSPeter Ujfalusi {
245210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
246210afeecSPeter Ujfalusi 	u8 status;
247210afeecSPeter Ujfalusi 
248210afeecSPeter Ujfalusi 	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
249210afeecSPeter Ujfalusi 	if (status & TWL6040_TSHUTDET) {
250210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Thermal shutdown, powering-off");
251210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
252210afeecSPeter Ujfalusi 	} else {
253210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on");
254210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 1);
255210afeecSPeter Ujfalusi 	}
256210afeecSPeter Ujfalusi 
257210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
258210afeecSPeter Ujfalusi }
259210afeecSPeter Ujfalusi 
260210afeecSPeter Ujfalusi static int twl6040_power_up_automatic(struct twl6040 *twl6040)
261210afeecSPeter Ujfalusi {
262210afeecSPeter Ujfalusi 	int time_left;
263210afeecSPeter Ujfalusi 
264210afeecSPeter Ujfalusi 	gpio_set_value(twl6040->audpwron, 1);
265210afeecSPeter Ujfalusi 
266210afeecSPeter Ujfalusi 	time_left = wait_for_completion_timeout(&twl6040->ready,
267210afeecSPeter Ujfalusi 						msecs_to_jiffies(144));
268210afeecSPeter Ujfalusi 	if (!time_left) {
269210afeecSPeter Ujfalusi 		u8 intid;
270210afeecSPeter Ujfalusi 
271210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "timeout waiting for READYINT\n");
272210afeecSPeter Ujfalusi 		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
273210afeecSPeter Ujfalusi 		if (!(intid & TWL6040_READYINT)) {
274210afeecSPeter Ujfalusi 			dev_err(twl6040->dev, "automatic power-up failed\n");
275210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
276210afeecSPeter Ujfalusi 			return -ETIMEDOUT;
277210afeecSPeter Ujfalusi 		}
278210afeecSPeter Ujfalusi 	}
279210afeecSPeter Ujfalusi 
280210afeecSPeter Ujfalusi 	return 0;
281210afeecSPeter Ujfalusi }
282210afeecSPeter Ujfalusi 
283210afeecSPeter Ujfalusi int twl6040_power(struct twl6040 *twl6040, int on)
284210afeecSPeter Ujfalusi {
285210afeecSPeter Ujfalusi 	int ret = 0;
286210afeecSPeter Ujfalusi 
287210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
288210afeecSPeter Ujfalusi 
289210afeecSPeter Ujfalusi 	if (on) {
290210afeecSPeter Ujfalusi 		/* already powered-up */
291210afeecSPeter Ujfalusi 		if (twl6040->power_count++)
292210afeecSPeter Ujfalusi 			goto out;
293210afeecSPeter Ujfalusi 
29468bab866SPeter Ujfalusi 		clk_prepare_enable(twl6040->clk32k);
29568bab866SPeter Ujfalusi 
296c7f9129dSPeter Ujfalusi 		/* Allow writes to the chip */
297c7f9129dSPeter Ujfalusi 		regcache_cache_only(twl6040->regmap, false);
298c7f9129dSPeter Ujfalusi 
299210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
300210afeecSPeter Ujfalusi 			/* use automatic power-up sequence */
301210afeecSPeter Ujfalusi 			ret = twl6040_power_up_automatic(twl6040);
302210afeecSPeter Ujfalusi 			if (ret) {
303210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
304210afeecSPeter Ujfalusi 				goto out;
305210afeecSPeter Ujfalusi 			}
306210afeecSPeter Ujfalusi 		} else {
307210afeecSPeter Ujfalusi 			/* use manual power-up sequence */
308210afeecSPeter Ujfalusi 			ret = twl6040_power_up_manual(twl6040);
309210afeecSPeter Ujfalusi 			if (ret) {
310210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
311210afeecSPeter Ujfalusi 				goto out;
312210afeecSPeter Ujfalusi 			}
313210afeecSPeter Ujfalusi 		}
314c7f9129dSPeter Ujfalusi 
315c7f9129dSPeter Ujfalusi 		/* Sync with the HW */
316c7f9129dSPeter Ujfalusi 		regcache_sync(twl6040->regmap);
317c7f9129dSPeter Ujfalusi 
318210afeecSPeter Ujfalusi 		/* Default PLL configuration after power up */
319210afeecSPeter Ujfalusi 		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
320210afeecSPeter Ujfalusi 		twl6040->sysclk = 19200000;
321210afeecSPeter Ujfalusi 		twl6040->mclk = 32768;
322210afeecSPeter Ujfalusi 	} else {
323210afeecSPeter Ujfalusi 		/* already powered-down */
324210afeecSPeter Ujfalusi 		if (!twl6040->power_count) {
325210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
326210afeecSPeter Ujfalusi 				"device is already powered-off\n");
327210afeecSPeter Ujfalusi 			ret = -EPERM;
328210afeecSPeter Ujfalusi 			goto out;
329210afeecSPeter Ujfalusi 		}
330210afeecSPeter Ujfalusi 
331210afeecSPeter Ujfalusi 		if (--twl6040->power_count)
332210afeecSPeter Ujfalusi 			goto out;
333210afeecSPeter Ujfalusi 
334210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
335210afeecSPeter Ujfalusi 			/* use AUDPWRON line */
336210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
337210afeecSPeter Ujfalusi 
338210afeecSPeter Ujfalusi 			/* power-down sequence latency */
339210afeecSPeter Ujfalusi 			usleep_range(500, 700);
340210afeecSPeter Ujfalusi 		} else {
341210afeecSPeter Ujfalusi 			/* use manual power-down sequence */
342210afeecSPeter Ujfalusi 			twl6040_power_down_manual(twl6040);
343210afeecSPeter Ujfalusi 		}
344c7f9129dSPeter Ujfalusi 
345c7f9129dSPeter Ujfalusi 		/* Set regmap to cache only and mark it as dirty */
346c7f9129dSPeter Ujfalusi 		regcache_cache_only(twl6040->regmap, true);
347c7f9129dSPeter Ujfalusi 		regcache_mark_dirty(twl6040->regmap);
348c7f9129dSPeter Ujfalusi 
349210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
350210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
35168bab866SPeter Ujfalusi 
35268bab866SPeter Ujfalusi 		clk_disable_unprepare(twl6040->clk32k);
353210afeecSPeter Ujfalusi 	}
354210afeecSPeter Ujfalusi 
355210afeecSPeter Ujfalusi out:
356210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
357210afeecSPeter Ujfalusi 	return ret;
358210afeecSPeter Ujfalusi }
359210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_power);
360210afeecSPeter Ujfalusi 
361210afeecSPeter Ujfalusi int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
362210afeecSPeter Ujfalusi 		    unsigned int freq_in, unsigned int freq_out)
363210afeecSPeter Ujfalusi {
364210afeecSPeter Ujfalusi 	u8 hppllctl, lppllctl;
365210afeecSPeter Ujfalusi 	int ret = 0;
366210afeecSPeter Ujfalusi 
367210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
368210afeecSPeter Ujfalusi 
369210afeecSPeter Ujfalusi 	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
370210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
371210afeecSPeter Ujfalusi 
372210afeecSPeter Ujfalusi 	/* Force full reconfiguration when switching between PLL */
373210afeecSPeter Ujfalusi 	if (pll_id != twl6040->pll) {
374210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
375210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
376210afeecSPeter Ujfalusi 	}
377210afeecSPeter Ujfalusi 
378210afeecSPeter Ujfalusi 	switch (pll_id) {
379210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_LPPLL:
380210afeecSPeter Ujfalusi 		/* low-power PLL divider */
381210afeecSPeter Ujfalusi 		/* Change the sysclk configuration only if it has been canged */
382210afeecSPeter Ujfalusi 		if (twl6040->sysclk != freq_out) {
383210afeecSPeter Ujfalusi 			switch (freq_out) {
384210afeecSPeter Ujfalusi 			case 17640000:
385210afeecSPeter Ujfalusi 				lppllctl |= TWL6040_LPLLFIN;
386210afeecSPeter Ujfalusi 				break;
387210afeecSPeter Ujfalusi 			case 19200000:
388210afeecSPeter Ujfalusi 				lppllctl &= ~TWL6040_LPLLFIN;
389210afeecSPeter Ujfalusi 				break;
390210afeecSPeter Ujfalusi 			default:
391210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
392210afeecSPeter Ujfalusi 					"freq_out %d not supported\n",
393210afeecSPeter Ujfalusi 					freq_out);
394210afeecSPeter Ujfalusi 				ret = -EINVAL;
395210afeecSPeter Ujfalusi 				goto pll_out;
396210afeecSPeter Ujfalusi 			}
397210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
398210afeecSPeter Ujfalusi 					  lppllctl);
399210afeecSPeter Ujfalusi 		}
400210afeecSPeter Ujfalusi 
401210afeecSPeter Ujfalusi 		/* The PLL in use has not been change, we can exit */
402210afeecSPeter Ujfalusi 		if (twl6040->pll == pll_id)
403210afeecSPeter Ujfalusi 			break;
404210afeecSPeter Ujfalusi 
405210afeecSPeter Ujfalusi 		switch (freq_in) {
406210afeecSPeter Ujfalusi 		case 32768:
407210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_LPLLENA;
408210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
409210afeecSPeter Ujfalusi 					  lppllctl);
410210afeecSPeter Ujfalusi 			mdelay(5);
411210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_HPLLSEL;
412210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
413210afeecSPeter Ujfalusi 					  lppllctl);
414210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_HPLLENA;
415210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
416210afeecSPeter Ujfalusi 					  hppllctl);
417210afeecSPeter Ujfalusi 			break;
418210afeecSPeter Ujfalusi 		default:
419210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
420210afeecSPeter Ujfalusi 				"freq_in %d not supported\n", freq_in);
421210afeecSPeter Ujfalusi 			ret = -EINVAL;
422210afeecSPeter Ujfalusi 			goto pll_out;
423210afeecSPeter Ujfalusi 		}
424210afeecSPeter Ujfalusi 		break;
425210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_HPPLL:
426210afeecSPeter Ujfalusi 		/* high-performance PLL can provide only 19.2 MHz */
427210afeecSPeter Ujfalusi 		if (freq_out != 19200000) {
428210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
429210afeecSPeter Ujfalusi 				"freq_out %d not supported\n", freq_out);
430210afeecSPeter Ujfalusi 			ret = -EINVAL;
431210afeecSPeter Ujfalusi 			goto pll_out;
432210afeecSPeter Ujfalusi 		}
433210afeecSPeter Ujfalusi 
434210afeecSPeter Ujfalusi 		if (twl6040->mclk != freq_in) {
435210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_MCLK_MSK;
436210afeecSPeter Ujfalusi 
437210afeecSPeter Ujfalusi 			switch (freq_in) {
438210afeecSPeter Ujfalusi 			case 12000000:
439210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
440210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_12000KHZ |
441210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
442210afeecSPeter Ujfalusi 				break;
443210afeecSPeter Ujfalusi 			case 19200000:
444ac8320c4SPeter Ujfalusi 				/* PLL enabled, bypass mode */
445ac8320c4SPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_19200KHZ |
446ac8320c4SPeter Ujfalusi 					    TWL6040_HPLLBP | TWL6040_HPLLENA;
447210afeecSPeter Ujfalusi 				break;
448210afeecSPeter Ujfalusi 			case 26000000:
449210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
450210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_26000KHZ |
451210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
452210afeecSPeter Ujfalusi 				break;
453210afeecSPeter Ujfalusi 			case 38400000:
454ac8320c4SPeter Ujfalusi 				/* PLL enabled, bypass mode */
455210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_38400KHZ |
456ac8320c4SPeter Ujfalusi 					    TWL6040_HPLLBP | TWL6040_HPLLENA;
457210afeecSPeter Ujfalusi 				break;
458210afeecSPeter Ujfalusi 			default:
459210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
460210afeecSPeter Ujfalusi 					"freq_in %d not supported\n", freq_in);
461210afeecSPeter Ujfalusi 				ret = -EINVAL;
462210afeecSPeter Ujfalusi 				goto pll_out;
463210afeecSPeter Ujfalusi 			}
464210afeecSPeter Ujfalusi 
465210afeecSPeter Ujfalusi 			/*
466210afeecSPeter Ujfalusi 			 * enable clock slicer to ensure input waveform is
467210afeecSPeter Ujfalusi 			 * square
468210afeecSPeter Ujfalusi 			 */
469210afeecSPeter Ujfalusi 			hppllctl |= TWL6040_HPLLSQRENA;
470210afeecSPeter Ujfalusi 
471210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
472210afeecSPeter Ujfalusi 					  hppllctl);
473210afeecSPeter Ujfalusi 			usleep_range(500, 700);
474210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_HPLLSEL;
475210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
476210afeecSPeter Ujfalusi 					  lppllctl);
477210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_LPLLENA;
478210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
479210afeecSPeter Ujfalusi 					  lppllctl);
480210afeecSPeter Ujfalusi 		}
481210afeecSPeter Ujfalusi 		break;
482210afeecSPeter Ujfalusi 	default:
483210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
484210afeecSPeter Ujfalusi 		ret = -EINVAL;
485210afeecSPeter Ujfalusi 		goto pll_out;
486210afeecSPeter Ujfalusi 	}
487210afeecSPeter Ujfalusi 
488210afeecSPeter Ujfalusi 	twl6040->sysclk = freq_out;
489210afeecSPeter Ujfalusi 	twl6040->mclk = freq_in;
490210afeecSPeter Ujfalusi 	twl6040->pll = pll_id;
491210afeecSPeter Ujfalusi 
492210afeecSPeter Ujfalusi pll_out:
493210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
494210afeecSPeter Ujfalusi 	return ret;
495210afeecSPeter Ujfalusi }
496210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_pll);
497210afeecSPeter Ujfalusi 
498210afeecSPeter Ujfalusi int twl6040_get_pll(struct twl6040 *twl6040)
499210afeecSPeter Ujfalusi {
500210afeecSPeter Ujfalusi 	if (twl6040->power_count)
501210afeecSPeter Ujfalusi 		return twl6040->pll;
502210afeecSPeter Ujfalusi 	else
503210afeecSPeter Ujfalusi 		return -ENODEV;
504210afeecSPeter Ujfalusi }
505210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_pll);
506210afeecSPeter Ujfalusi 
507210afeecSPeter Ujfalusi unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
508210afeecSPeter Ujfalusi {
509210afeecSPeter Ujfalusi 	return twl6040->sysclk;
510210afeecSPeter Ujfalusi }
511210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_sysclk);
512210afeecSPeter Ujfalusi 
513210afeecSPeter Ujfalusi /* Get the combined status of the vibra control register */
514210afeecSPeter Ujfalusi int twl6040_get_vibralr_status(struct twl6040 *twl6040)
515210afeecSPeter Ujfalusi {
516c6f39257SMark Brown 	unsigned int reg;
517c6f39257SMark Brown 	int ret;
518210afeecSPeter Ujfalusi 	u8 status;
519210afeecSPeter Ujfalusi 
520c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, &reg);
521c6f39257SMark Brown 	if (ret != 0)
522c6f39257SMark Brown 		return ret;
523c6f39257SMark Brown 	status = reg;
524c6f39257SMark Brown 
525c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, &reg);
526c6f39257SMark Brown 	if (ret != 0)
527c6f39257SMark Brown 		return ret;
528c6f39257SMark Brown 	status |= reg;
529c6f39257SMark Brown 
530210afeecSPeter Ujfalusi 	status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
531210afeecSPeter Ujfalusi 
532210afeecSPeter Ujfalusi 	return status;
533210afeecSPeter Ujfalusi }
534210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_vibralr_status);
535210afeecSPeter Ujfalusi 
536210afeecSPeter Ujfalusi static struct resource twl6040_vibra_rsrc[] = {
537210afeecSPeter Ujfalusi 	{
538210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
539210afeecSPeter Ujfalusi 	},
540210afeecSPeter Ujfalusi };
541210afeecSPeter Ujfalusi 
542210afeecSPeter Ujfalusi static struct resource twl6040_codec_rsrc[] = {
543210afeecSPeter Ujfalusi 	{
544210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
545210afeecSPeter Ujfalusi 	},
546210afeecSPeter Ujfalusi };
547210afeecSPeter Ujfalusi 
548210afeecSPeter Ujfalusi static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
549210afeecSPeter Ujfalusi {
550210afeecSPeter Ujfalusi 	/* Register 0 is not readable */
551210afeecSPeter Ujfalusi 	if (!reg)
552210afeecSPeter Ujfalusi 		return false;
553210afeecSPeter Ujfalusi 	return true;
554210afeecSPeter Ujfalusi }
555210afeecSPeter Ujfalusi 
556c6f39257SMark Brown static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)
557c6f39257SMark Brown {
558c6f39257SMark Brown 	switch (reg) {
559c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICID:
560c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICREV:
561c7f9129dSPeter Ujfalusi 	case TWL6040_REG_INTID:
562c7f9129dSPeter Ujfalusi 	case TWL6040_REG_LPPLLCTL:
563c7f9129dSPeter Ujfalusi 	case TWL6040_REG_HPPLLCTL:
564c7f9129dSPeter Ujfalusi 	case TWL6040_REG_STATUS:
565c7f9129dSPeter Ujfalusi 		return true;
566c7f9129dSPeter Ujfalusi 	default:
567c7f9129dSPeter Ujfalusi 		return false;
568c7f9129dSPeter Ujfalusi 	}
569c7f9129dSPeter Ujfalusi }
570c7f9129dSPeter Ujfalusi 
571c7f9129dSPeter Ujfalusi static bool twl6040_writeable_reg(struct device *dev, unsigned int reg)
572c7f9129dSPeter Ujfalusi {
573c7f9129dSPeter Ujfalusi 	switch (reg) {
574c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICID:
575c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICREV:
576c7f9129dSPeter Ujfalusi 	case TWL6040_REG_STATUS:
577c6f39257SMark Brown 		return false;
578c6f39257SMark Brown 	default:
579c6f39257SMark Brown 		return true;
580c6f39257SMark Brown 	}
581c6f39257SMark Brown }
582c6f39257SMark Brown 
583de1e23f8SKrzysztof Kozlowski static const struct regmap_config twl6040_regmap_config = {
584210afeecSPeter Ujfalusi 	.reg_bits = 8,
585210afeecSPeter Ujfalusi 	.val_bits = 8,
586c7f9129dSPeter Ujfalusi 
587c7f9129dSPeter Ujfalusi 	.reg_defaults = twl6040_defaults,
588c7f9129dSPeter Ujfalusi 	.num_reg_defaults = ARRAY_SIZE(twl6040_defaults),
589c7f9129dSPeter Ujfalusi 
590210afeecSPeter Ujfalusi 	.max_register = TWL6040_REG_STATUS, /* 0x2e */
591210afeecSPeter Ujfalusi 
592210afeecSPeter Ujfalusi 	.readable_reg = twl6040_readable_reg,
593c6f39257SMark Brown 	.volatile_reg = twl6040_volatile_reg,
594c7f9129dSPeter Ujfalusi 	.writeable_reg = twl6040_writeable_reg,
595c6f39257SMark Brown 
596c6f39257SMark Brown 	.cache_type = REGCACHE_RBTREE,
597210afeecSPeter Ujfalusi };
598210afeecSPeter Ujfalusi 
599210afeecSPeter Ujfalusi static const struct regmap_irq twl6040_irqs[] = {
600210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_THINT, },
601210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, },
602210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HOOKINT, },
603210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HFINT, },
604210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_VIBINT, },
605210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_READYINT, },
606210afeecSPeter Ujfalusi };
607210afeecSPeter Ujfalusi 
608210afeecSPeter Ujfalusi static struct regmap_irq_chip twl6040_irq_chip = {
609210afeecSPeter Ujfalusi 	.name = "twl6040",
610210afeecSPeter Ujfalusi 	.irqs = twl6040_irqs,
611210afeecSPeter Ujfalusi 	.num_irqs = ARRAY_SIZE(twl6040_irqs),
612210afeecSPeter Ujfalusi 
613210afeecSPeter Ujfalusi 	.num_regs = 1,
614210afeecSPeter Ujfalusi 	.status_base = TWL6040_REG_INTID,
615210afeecSPeter Ujfalusi 	.mask_base = TWL6040_REG_INTMR,
616210afeecSPeter Ujfalusi };
617210afeecSPeter Ujfalusi 
618612b95cdSGreg Kroah-Hartman static int twl6040_probe(struct i2c_client *client,
619210afeecSPeter Ujfalusi 			 const struct i2c_device_id *id)
620210afeecSPeter Ujfalusi {
621210afeecSPeter Ujfalusi 	struct device_node *node = client->dev.of_node;
622210afeecSPeter Ujfalusi 	struct twl6040 *twl6040;
623210afeecSPeter Ujfalusi 	struct mfd_cell *cell = NULL;
624210afeecSPeter Ujfalusi 	int irq, ret, children = 0;
625210afeecSPeter Ujfalusi 
626df04b624SPeter Ujfalusi 	if (!node) {
627df04b624SPeter Ujfalusi 		dev_err(&client->dev, "of node is missing\n");
628210afeecSPeter Ujfalusi 		return -EINVAL;
629210afeecSPeter Ujfalusi 	}
630210afeecSPeter Ujfalusi 
631210afeecSPeter Ujfalusi 	/* In order to operate correctly we need valid interrupt config */
632210afeecSPeter Ujfalusi 	if (!client->irq) {
633210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Invalid IRQ configuration\n");
634210afeecSPeter Ujfalusi 		return -EINVAL;
635210afeecSPeter Ujfalusi 	}
636210afeecSPeter Ujfalusi 
637210afeecSPeter Ujfalusi 	twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
638210afeecSPeter Ujfalusi 			       GFP_KERNEL);
639ecc8fa1cSPeter Ujfalusi 	if (!twl6040)
640ecc8fa1cSPeter Ujfalusi 		return -ENOMEM;
641210afeecSPeter Ujfalusi 
642210afeecSPeter Ujfalusi 	twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
643ecc8fa1cSPeter Ujfalusi 	if (IS_ERR(twl6040->regmap))
644ecc8fa1cSPeter Ujfalusi 		return PTR_ERR(twl6040->regmap);
645210afeecSPeter Ujfalusi 
646210afeecSPeter Ujfalusi 	i2c_set_clientdata(client, twl6040);
647210afeecSPeter Ujfalusi 
64868bab866SPeter Ujfalusi 	twl6040->clk32k = devm_clk_get(&client->dev, "clk32k");
64968bab866SPeter Ujfalusi 	if (IS_ERR(twl6040->clk32k)) {
65075c08f17STony Lindgren 		if (PTR_ERR(twl6040->clk32k) == -EPROBE_DEFER)
65175c08f17STony Lindgren 			return -EPROBE_DEFER;
65268bab866SPeter Ujfalusi 		dev_info(&client->dev, "clk32k is not handled\n");
65368bab866SPeter Ujfalusi 		twl6040->clk32k = NULL;
65468bab866SPeter Ujfalusi 	}
65568bab866SPeter Ujfalusi 
656210afeecSPeter Ujfalusi 	twl6040->supplies[0].supply = "vio";
657210afeecSPeter Ujfalusi 	twl6040->supplies[1].supply = "v2v1";
658990810b0SJingoo Han 	ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
659210afeecSPeter Ujfalusi 				      twl6040->supplies);
660210afeecSPeter Ujfalusi 	if (ret != 0) {
661210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
662501d609aSWolfram Sang 		return ret;
663210afeecSPeter Ujfalusi 	}
664210afeecSPeter Ujfalusi 
665210afeecSPeter Ujfalusi 	ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
666210afeecSPeter Ujfalusi 	if (ret != 0) {
667210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
668501d609aSWolfram Sang 		return ret;
669210afeecSPeter Ujfalusi 	}
670210afeecSPeter Ujfalusi 
671210afeecSPeter Ujfalusi 	twl6040->dev = &client->dev;
672210afeecSPeter Ujfalusi 	twl6040->irq = client->irq;
673210afeecSPeter Ujfalusi 
674210afeecSPeter Ujfalusi 	mutex_init(&twl6040->mutex);
675210afeecSPeter Ujfalusi 	init_completion(&twl6040->ready);
676210afeecSPeter Ujfalusi 
677006cea3aSPeter Ujfalusi 	regmap_register_patch(twl6040->regmap, twl6040_patch,
678006cea3aSPeter Ujfalusi 			      ARRAY_SIZE(twl6040_patch));
679006cea3aSPeter Ujfalusi 
680210afeecSPeter Ujfalusi 	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
68189d68998SFlorian Vaussard 	if (twl6040->rev < 0) {
68289d68998SFlorian Vaussard 		dev_err(&client->dev, "Failed to read revision register: %d\n",
68389d68998SFlorian Vaussard 			twl6040->rev);
684f2b86781SJulia Lawall 		ret = twl6040->rev;
68589d68998SFlorian Vaussard 		goto gpio_err;
68689d68998SFlorian Vaussard 	}
687210afeecSPeter Ujfalusi 
688210afeecSPeter Ujfalusi 	/* ERRATA: Automatic power-up is not possible in ES1.0 */
689df04b624SPeter Ujfalusi 	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
690210afeecSPeter Ujfalusi 		twl6040->audpwron = of_get_named_gpio(node,
691210afeecSPeter Ujfalusi 						      "ti,audpwron-gpio", 0);
692df04b624SPeter Ujfalusi 	else
693210afeecSPeter Ujfalusi 		twl6040->audpwron = -EINVAL;
694210afeecSPeter Ujfalusi 
695210afeecSPeter Ujfalusi 	if (gpio_is_valid(twl6040->audpwron)) {
696990810b0SJingoo Han 		ret = devm_gpio_request_one(&client->dev, twl6040->audpwron,
697990810b0SJingoo Han 					    GPIOF_OUT_INIT_LOW, "audpwron");
698210afeecSPeter Ujfalusi 		if (ret)
699210afeecSPeter Ujfalusi 			goto gpio_err;
70002d02728SPeter Ujfalusi 
70102d02728SPeter Ujfalusi 		/* Clear any pending interrupt */
70202d02728SPeter Ujfalusi 		twl6040_reg_read(twl6040, TWL6040_REG_INTID);
703210afeecSPeter Ujfalusi 	}
704210afeecSPeter Ujfalusi 
70537aefe9fSPeter Ujfalusi 	ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
70637aefe9fSPeter Ujfalusi 				  0, &twl6040_irq_chip, &twl6040->irq_data);
707210afeecSPeter Ujfalusi 	if (ret < 0)
708990810b0SJingoo Han 		goto gpio_err;
709210afeecSPeter Ujfalusi 
710210afeecSPeter Ujfalusi 	twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,
711210afeecSPeter Ujfalusi 						 TWL6040_IRQ_READY);
712210afeecSPeter Ujfalusi 	twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,
713210afeecSPeter Ujfalusi 					      TWL6040_IRQ_TH);
714210afeecSPeter Ujfalusi 
715990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,
716210afeecSPeter Ujfalusi 					twl6040_readyint_handler, IRQF_ONESHOT,
717210afeecSPeter Ujfalusi 					"twl6040_irq_ready", twl6040);
718210afeecSPeter Ujfalusi 	if (ret) {
719210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret);
720210afeecSPeter Ujfalusi 		goto readyirq_err;
721210afeecSPeter Ujfalusi 	}
722210afeecSPeter Ujfalusi 
723990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,
724210afeecSPeter Ujfalusi 					twl6040_thint_handler, IRQF_ONESHOT,
725210afeecSPeter Ujfalusi 					"twl6040_irq_th", twl6040);
726210afeecSPeter Ujfalusi 	if (ret) {
727210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret);
728fc5ee96fSWei Yongjun 		goto readyirq_err;
729210afeecSPeter Ujfalusi 	}
730210afeecSPeter Ujfalusi 
731210afeecSPeter Ujfalusi 	/*
732210afeecSPeter Ujfalusi 	 * The main functionality of twl6040 to provide audio on OMAP4+ systems.
733210afeecSPeter Ujfalusi 	 * We can add the ASoC codec child whenever this driver has been loaded.
734210afeecSPeter Ujfalusi 	 */
735210afeecSPeter Ujfalusi 	irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
736210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
737210afeecSPeter Ujfalusi 	cell->name = "twl6040-codec";
738210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].start = irq;
739210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].end = irq;
740210afeecSPeter Ujfalusi 	cell->resources = twl6040_codec_rsrc;
741210afeecSPeter Ujfalusi 	cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
742210afeecSPeter Ujfalusi 	children++;
743210afeecSPeter Ujfalusi 
744df04b624SPeter Ujfalusi 	/* Vibra input driver support */
745df04b624SPeter Ujfalusi 	if (twl6040_has_vibra(node)) {
746210afeecSPeter Ujfalusi 		irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
747210afeecSPeter Ujfalusi 
748210afeecSPeter Ujfalusi 		cell = &twl6040->cells[children];
749210afeecSPeter Ujfalusi 		cell->name = "twl6040-vibra";
750210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].start = irq;
751210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].end = irq;
752210afeecSPeter Ujfalusi 		cell->resources = twl6040_vibra_rsrc;
753210afeecSPeter Ujfalusi 		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
754210afeecSPeter Ujfalusi 		children++;
755210afeecSPeter Ujfalusi 	}
756210afeecSPeter Ujfalusi 
757df04b624SPeter Ujfalusi 	/* GPO support */
758210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
759210afeecSPeter Ujfalusi 	cell->name = "twl6040-gpo";
760210afeecSPeter Ujfalusi 	children++;
761210afeecSPeter Ujfalusi 
762c7f9129dSPeter Ujfalusi 	/* The chip is powered down so mark regmap to cache only and dirty */
763c7f9129dSPeter Ujfalusi 	regcache_cache_only(twl6040->regmap, true);
764c7f9129dSPeter Ujfalusi 	regcache_mark_dirty(twl6040->regmap);
765c7f9129dSPeter Ujfalusi 
766210afeecSPeter Ujfalusi 	ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
767210afeecSPeter Ujfalusi 			      NULL, 0, NULL);
768210afeecSPeter Ujfalusi 	if (ret)
769fc5ee96fSWei Yongjun 		goto readyirq_err;
770210afeecSPeter Ujfalusi 
771210afeecSPeter Ujfalusi 	return 0;
772210afeecSPeter Ujfalusi 
773210afeecSPeter Ujfalusi readyirq_err:
774210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
775210afeecSPeter Ujfalusi gpio_err:
776210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
777210afeecSPeter Ujfalusi 	return ret;
778210afeecSPeter Ujfalusi }
779210afeecSPeter Ujfalusi 
780612b95cdSGreg Kroah-Hartman static int twl6040_remove(struct i2c_client *client)
781210afeecSPeter Ujfalusi {
782210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = i2c_get_clientdata(client);
783210afeecSPeter Ujfalusi 
784210afeecSPeter Ujfalusi 	if (twl6040->power_count)
785210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
786210afeecSPeter Ujfalusi 
787210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
788210afeecSPeter Ujfalusi 
789210afeecSPeter Ujfalusi 	mfd_remove_devices(&client->dev);
790210afeecSPeter Ujfalusi 
791210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
792210afeecSPeter Ujfalusi 
793210afeecSPeter Ujfalusi 	return 0;
794210afeecSPeter Ujfalusi }
795210afeecSPeter Ujfalusi 
796210afeecSPeter Ujfalusi static const struct i2c_device_id twl6040_i2c_id[] = {
797210afeecSPeter Ujfalusi 	{ "twl6040", 0, },
798210afeecSPeter Ujfalusi 	{ "twl6041", 0, },
799210afeecSPeter Ujfalusi 	{ },
800210afeecSPeter Ujfalusi };
801210afeecSPeter Ujfalusi MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
802210afeecSPeter Ujfalusi 
803210afeecSPeter Ujfalusi static struct i2c_driver twl6040_driver = {
804210afeecSPeter Ujfalusi 	.driver = {
805210afeecSPeter Ujfalusi 		.name = "twl6040",
806210afeecSPeter Ujfalusi 	},
807210afeecSPeter Ujfalusi 	.probe		= twl6040_probe,
808612b95cdSGreg Kroah-Hartman 	.remove		= twl6040_remove,
809210afeecSPeter Ujfalusi 	.id_table	= twl6040_i2c_id,
810210afeecSPeter Ujfalusi };
811210afeecSPeter Ujfalusi 
812210afeecSPeter Ujfalusi module_i2c_driver(twl6040_driver);
813210afeecSPeter Ujfalusi 
814210afeecSPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 MFD");
815210afeecSPeter Ujfalusi MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
816210afeecSPeter Ujfalusi MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
817210afeecSPeter Ujfalusi MODULE_LICENSE("GPL");
818