xref: /openbmc/linux/drivers/mfd/twl6040.c (revision c7f9129d)
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 
47c7f9129dSPeter Ujfalusi static 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 
89c7f9129dSPeter Ujfalusi struct reg_default twl6040_patch[] = {
90c7f9129dSPeter Ujfalusi 	/* Select I2C bus access to dual access registers */
91c7f9129dSPeter Ujfalusi 	{ TWL6040_REG_ACCCTL, 0x09 },
92c7f9129dSPeter Ujfalusi };
93c7f9129dSPeter Ujfalusi 
94c7f9129dSPeter Ujfalusi 
95df04b624SPeter Ujfalusi static bool twl6040_has_vibra(struct device_node *node)
96210afeecSPeter Ujfalusi {
97210afeecSPeter Ujfalusi #ifdef CONFIG_OF
98210afeecSPeter Ujfalusi 	if (of_find_node_by_name(node, "vibra"))
99210afeecSPeter Ujfalusi 		return true;
100210afeecSPeter Ujfalusi #endif
101210afeecSPeter Ujfalusi 	return false;
102210afeecSPeter Ujfalusi }
103210afeecSPeter Ujfalusi 
104210afeecSPeter Ujfalusi int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
105210afeecSPeter Ujfalusi {
106210afeecSPeter Ujfalusi 	int ret;
107210afeecSPeter Ujfalusi 	unsigned int val;
108210afeecSPeter Ujfalusi 
109210afeecSPeter Ujfalusi 	ret = regmap_read(twl6040->regmap, reg, &val);
110210afeecSPeter Ujfalusi 	if (ret < 0)
111210afeecSPeter Ujfalusi 		return ret;
112210afeecSPeter Ujfalusi 
113210afeecSPeter Ujfalusi 	return val;
114210afeecSPeter Ujfalusi }
115210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_read);
116210afeecSPeter Ujfalusi 
117210afeecSPeter Ujfalusi int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
118210afeecSPeter Ujfalusi {
119210afeecSPeter Ujfalusi 	int ret;
120210afeecSPeter Ujfalusi 
121210afeecSPeter Ujfalusi 	ret = regmap_write(twl6040->regmap, reg, val);
122210afeecSPeter Ujfalusi 
123210afeecSPeter Ujfalusi 	return ret;
124210afeecSPeter Ujfalusi }
125210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_write);
126210afeecSPeter Ujfalusi 
127210afeecSPeter Ujfalusi int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
128210afeecSPeter Ujfalusi {
129210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, mask);
130210afeecSPeter Ujfalusi }
131210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_bits);
132210afeecSPeter Ujfalusi 
133210afeecSPeter Ujfalusi int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
134210afeecSPeter Ujfalusi {
135210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, 0);
136210afeecSPeter Ujfalusi }
137210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_clear_bits);
138210afeecSPeter Ujfalusi 
139210afeecSPeter Ujfalusi /* twl6040 codec manual power-up sequence */
140210afeecSPeter Ujfalusi static int twl6040_power_up_manual(struct twl6040 *twl6040)
141210afeecSPeter Ujfalusi {
142210afeecSPeter Ujfalusi 	u8 ldoctl, ncpctl, lppllctl;
143210afeecSPeter Ujfalusi 	int ret;
144210afeecSPeter Ujfalusi 
145210afeecSPeter Ujfalusi 	/* enable high-side LDO, reference system and internal oscillator */
146210afeecSPeter Ujfalusi 	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
147210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
148210afeecSPeter Ujfalusi 	if (ret)
149210afeecSPeter Ujfalusi 		return ret;
150210afeecSPeter Ujfalusi 	usleep_range(10000, 10500);
151210afeecSPeter Ujfalusi 
152210afeecSPeter Ujfalusi 	/* enable negative charge pump */
153210afeecSPeter Ujfalusi 	ncpctl = TWL6040_NCPENA;
154210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
155210afeecSPeter Ujfalusi 	if (ret)
156210afeecSPeter Ujfalusi 		goto ncp_err;
157210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
158210afeecSPeter Ujfalusi 
159210afeecSPeter Ujfalusi 	/* enable low-side LDO */
160210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_LSLDOENA;
161210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
162210afeecSPeter Ujfalusi 	if (ret)
163210afeecSPeter Ujfalusi 		goto lsldo_err;
164210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
165210afeecSPeter Ujfalusi 
166210afeecSPeter Ujfalusi 	/* enable low-power PLL */
167210afeecSPeter Ujfalusi 	lppllctl = TWL6040_LPLLENA;
168210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
169210afeecSPeter Ujfalusi 	if (ret)
170210afeecSPeter Ujfalusi 		goto lppll_err;
171210afeecSPeter Ujfalusi 	usleep_range(5000, 5500);
172210afeecSPeter Ujfalusi 
173210afeecSPeter Ujfalusi 	/* disable internal oscillator */
174210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_OSCENA;
175210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
176210afeecSPeter Ujfalusi 	if (ret)
177210afeecSPeter Ujfalusi 		goto osc_err;
178210afeecSPeter Ujfalusi 
179210afeecSPeter Ujfalusi 	return 0;
180210afeecSPeter Ujfalusi 
181210afeecSPeter Ujfalusi osc_err:
182210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
183210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
184210afeecSPeter Ujfalusi lppll_err:
185210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
186210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
187210afeecSPeter Ujfalusi lsldo_err:
188210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
189210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
190210afeecSPeter Ujfalusi ncp_err:
191210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
192210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
193210afeecSPeter Ujfalusi 
194210afeecSPeter Ujfalusi 	dev_err(twl6040->dev, "manual power-up failed\n");
195210afeecSPeter Ujfalusi 	return ret;
196210afeecSPeter Ujfalusi }
197210afeecSPeter Ujfalusi 
198210afeecSPeter Ujfalusi /* twl6040 manual power-down sequence */
199210afeecSPeter Ujfalusi static void twl6040_power_down_manual(struct twl6040 *twl6040)
200210afeecSPeter Ujfalusi {
201210afeecSPeter Ujfalusi 	u8 ncpctl, ldoctl, lppllctl;
202210afeecSPeter Ujfalusi 
203210afeecSPeter Ujfalusi 	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
204210afeecSPeter Ujfalusi 	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
205210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
206210afeecSPeter Ujfalusi 
207210afeecSPeter Ujfalusi 	/* enable internal oscillator */
208210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_OSCENA;
209210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
210210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
211210afeecSPeter Ujfalusi 
212210afeecSPeter Ujfalusi 	/* disable low-power PLL */
213210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
214210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
215210afeecSPeter Ujfalusi 
216210afeecSPeter Ujfalusi 	/* disable low-side LDO */
217210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
218210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
219210afeecSPeter Ujfalusi 
220210afeecSPeter Ujfalusi 	/* disable negative charge pump */
221210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
222210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
223210afeecSPeter Ujfalusi 
224210afeecSPeter Ujfalusi 	/* disable high-side LDO, reference system and internal oscillator */
225210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
226210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
227210afeecSPeter Ujfalusi }
228210afeecSPeter Ujfalusi 
229210afeecSPeter Ujfalusi static irqreturn_t twl6040_readyint_handler(int irq, void *data)
230210afeecSPeter Ujfalusi {
231210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
232210afeecSPeter Ujfalusi 
233210afeecSPeter Ujfalusi 	complete(&twl6040->ready);
234210afeecSPeter Ujfalusi 
235210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
236210afeecSPeter Ujfalusi }
237210afeecSPeter Ujfalusi 
238210afeecSPeter Ujfalusi static irqreturn_t twl6040_thint_handler(int irq, void *data)
239210afeecSPeter Ujfalusi {
240210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
241210afeecSPeter Ujfalusi 	u8 status;
242210afeecSPeter Ujfalusi 
243210afeecSPeter Ujfalusi 	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
244210afeecSPeter Ujfalusi 	if (status & TWL6040_TSHUTDET) {
245210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Thermal shutdown, powering-off");
246210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
247210afeecSPeter Ujfalusi 	} else {
248210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on");
249210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 1);
250210afeecSPeter Ujfalusi 	}
251210afeecSPeter Ujfalusi 
252210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
253210afeecSPeter Ujfalusi }
254210afeecSPeter Ujfalusi 
255210afeecSPeter Ujfalusi static int twl6040_power_up_automatic(struct twl6040 *twl6040)
256210afeecSPeter Ujfalusi {
257210afeecSPeter Ujfalusi 	int time_left;
258210afeecSPeter Ujfalusi 
259210afeecSPeter Ujfalusi 	gpio_set_value(twl6040->audpwron, 1);
260210afeecSPeter Ujfalusi 
261210afeecSPeter Ujfalusi 	time_left = wait_for_completion_timeout(&twl6040->ready,
262210afeecSPeter Ujfalusi 						msecs_to_jiffies(144));
263210afeecSPeter Ujfalusi 	if (!time_left) {
264210afeecSPeter Ujfalusi 		u8 intid;
265210afeecSPeter Ujfalusi 
266210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "timeout waiting for READYINT\n");
267210afeecSPeter Ujfalusi 		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
268210afeecSPeter Ujfalusi 		if (!(intid & TWL6040_READYINT)) {
269210afeecSPeter Ujfalusi 			dev_err(twl6040->dev, "automatic power-up failed\n");
270210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
271210afeecSPeter Ujfalusi 			return -ETIMEDOUT;
272210afeecSPeter Ujfalusi 		}
273210afeecSPeter Ujfalusi 	}
274210afeecSPeter Ujfalusi 
275210afeecSPeter Ujfalusi 	return 0;
276210afeecSPeter Ujfalusi }
277210afeecSPeter Ujfalusi 
278210afeecSPeter Ujfalusi int twl6040_power(struct twl6040 *twl6040, int on)
279210afeecSPeter Ujfalusi {
280210afeecSPeter Ujfalusi 	int ret = 0;
281210afeecSPeter Ujfalusi 
282210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
283210afeecSPeter Ujfalusi 
284210afeecSPeter Ujfalusi 	if (on) {
285210afeecSPeter Ujfalusi 		/* already powered-up */
286210afeecSPeter Ujfalusi 		if (twl6040->power_count++)
287210afeecSPeter Ujfalusi 			goto out;
288210afeecSPeter Ujfalusi 
289c7f9129dSPeter Ujfalusi 		/* Allow writes to the chip */
290c7f9129dSPeter Ujfalusi 		regcache_cache_only(twl6040->regmap, false);
291c7f9129dSPeter Ujfalusi 
292210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
293210afeecSPeter Ujfalusi 			/* use automatic power-up sequence */
294210afeecSPeter Ujfalusi 			ret = twl6040_power_up_automatic(twl6040);
295210afeecSPeter Ujfalusi 			if (ret) {
296210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
297210afeecSPeter Ujfalusi 				goto out;
298210afeecSPeter Ujfalusi 			}
299210afeecSPeter Ujfalusi 		} else {
300210afeecSPeter Ujfalusi 			/* use manual power-up sequence */
301210afeecSPeter Ujfalusi 			ret = twl6040_power_up_manual(twl6040);
302210afeecSPeter Ujfalusi 			if (ret) {
303210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
304210afeecSPeter Ujfalusi 				goto out;
305210afeecSPeter Ujfalusi 			}
306210afeecSPeter Ujfalusi 		}
307c7f9129dSPeter Ujfalusi 
308c7f9129dSPeter Ujfalusi 		/* Sync with the HW */
309c7f9129dSPeter Ujfalusi 		regcache_sync(twl6040->regmap);
310c7f9129dSPeter Ujfalusi 
311210afeecSPeter Ujfalusi 		/* Default PLL configuration after power up */
312210afeecSPeter Ujfalusi 		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
313210afeecSPeter Ujfalusi 		twl6040->sysclk = 19200000;
314210afeecSPeter Ujfalusi 		twl6040->mclk = 32768;
315210afeecSPeter Ujfalusi 	} else {
316210afeecSPeter Ujfalusi 		/* already powered-down */
317210afeecSPeter Ujfalusi 		if (!twl6040->power_count) {
318210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
319210afeecSPeter Ujfalusi 				"device is already powered-off\n");
320210afeecSPeter Ujfalusi 			ret = -EPERM;
321210afeecSPeter Ujfalusi 			goto out;
322210afeecSPeter Ujfalusi 		}
323210afeecSPeter Ujfalusi 
324210afeecSPeter Ujfalusi 		if (--twl6040->power_count)
325210afeecSPeter Ujfalusi 			goto out;
326210afeecSPeter Ujfalusi 
327210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
328210afeecSPeter Ujfalusi 			/* use AUDPWRON line */
329210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
330210afeecSPeter Ujfalusi 
331210afeecSPeter Ujfalusi 			/* power-down sequence latency */
332210afeecSPeter Ujfalusi 			usleep_range(500, 700);
333210afeecSPeter Ujfalusi 		} else {
334210afeecSPeter Ujfalusi 			/* use manual power-down sequence */
335210afeecSPeter Ujfalusi 			twl6040_power_down_manual(twl6040);
336210afeecSPeter Ujfalusi 		}
337c7f9129dSPeter Ujfalusi 
338c7f9129dSPeter Ujfalusi 		/* Set regmap to cache only and mark it as dirty */
339c7f9129dSPeter Ujfalusi 		regcache_cache_only(twl6040->regmap, true);
340c7f9129dSPeter Ujfalusi 		regcache_mark_dirty(twl6040->regmap);
341c7f9129dSPeter Ujfalusi 
342210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
343210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
344210afeecSPeter Ujfalusi 	}
345210afeecSPeter Ujfalusi 
346210afeecSPeter Ujfalusi out:
347210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
348210afeecSPeter Ujfalusi 	return ret;
349210afeecSPeter Ujfalusi }
350210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_power);
351210afeecSPeter Ujfalusi 
352210afeecSPeter Ujfalusi int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
353210afeecSPeter Ujfalusi 		    unsigned int freq_in, unsigned int freq_out)
354210afeecSPeter Ujfalusi {
355210afeecSPeter Ujfalusi 	u8 hppllctl, lppllctl;
356210afeecSPeter Ujfalusi 	int ret = 0;
357210afeecSPeter Ujfalusi 
358210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
359210afeecSPeter Ujfalusi 
360210afeecSPeter Ujfalusi 	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
361210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
362210afeecSPeter Ujfalusi 
363210afeecSPeter Ujfalusi 	/* Force full reconfiguration when switching between PLL */
364210afeecSPeter Ujfalusi 	if (pll_id != twl6040->pll) {
365210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
366210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
367210afeecSPeter Ujfalusi 	}
368210afeecSPeter Ujfalusi 
369210afeecSPeter Ujfalusi 	switch (pll_id) {
370210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_LPPLL:
371210afeecSPeter Ujfalusi 		/* low-power PLL divider */
372210afeecSPeter Ujfalusi 		/* Change the sysclk configuration only if it has been canged */
373210afeecSPeter Ujfalusi 		if (twl6040->sysclk != freq_out) {
374210afeecSPeter Ujfalusi 			switch (freq_out) {
375210afeecSPeter Ujfalusi 			case 17640000:
376210afeecSPeter Ujfalusi 				lppllctl |= TWL6040_LPLLFIN;
377210afeecSPeter Ujfalusi 				break;
378210afeecSPeter Ujfalusi 			case 19200000:
379210afeecSPeter Ujfalusi 				lppllctl &= ~TWL6040_LPLLFIN;
380210afeecSPeter Ujfalusi 				break;
381210afeecSPeter Ujfalusi 			default:
382210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
383210afeecSPeter Ujfalusi 					"freq_out %d not supported\n",
384210afeecSPeter Ujfalusi 					freq_out);
385210afeecSPeter Ujfalusi 				ret = -EINVAL;
386210afeecSPeter Ujfalusi 				goto pll_out;
387210afeecSPeter Ujfalusi 			}
388210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
389210afeecSPeter Ujfalusi 					  lppllctl);
390210afeecSPeter Ujfalusi 		}
391210afeecSPeter Ujfalusi 
392210afeecSPeter Ujfalusi 		/* The PLL in use has not been change, we can exit */
393210afeecSPeter Ujfalusi 		if (twl6040->pll == pll_id)
394210afeecSPeter Ujfalusi 			break;
395210afeecSPeter Ujfalusi 
396210afeecSPeter Ujfalusi 		switch (freq_in) {
397210afeecSPeter Ujfalusi 		case 32768:
398210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_LPLLENA;
399210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
400210afeecSPeter Ujfalusi 					  lppllctl);
401210afeecSPeter Ujfalusi 			mdelay(5);
402210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_HPLLSEL;
403210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
404210afeecSPeter Ujfalusi 					  lppllctl);
405210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_HPLLENA;
406210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
407210afeecSPeter Ujfalusi 					  hppllctl);
408210afeecSPeter Ujfalusi 			break;
409210afeecSPeter Ujfalusi 		default:
410210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
411210afeecSPeter Ujfalusi 				"freq_in %d not supported\n", freq_in);
412210afeecSPeter Ujfalusi 			ret = -EINVAL;
413210afeecSPeter Ujfalusi 			goto pll_out;
414210afeecSPeter Ujfalusi 		}
415210afeecSPeter Ujfalusi 		break;
416210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_HPPLL:
417210afeecSPeter Ujfalusi 		/* high-performance PLL can provide only 19.2 MHz */
418210afeecSPeter Ujfalusi 		if (freq_out != 19200000) {
419210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
420210afeecSPeter Ujfalusi 				"freq_out %d not supported\n", freq_out);
421210afeecSPeter Ujfalusi 			ret = -EINVAL;
422210afeecSPeter Ujfalusi 			goto pll_out;
423210afeecSPeter Ujfalusi 		}
424210afeecSPeter Ujfalusi 
425210afeecSPeter Ujfalusi 		if (twl6040->mclk != freq_in) {
426210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_MCLK_MSK;
427210afeecSPeter Ujfalusi 
428210afeecSPeter Ujfalusi 			switch (freq_in) {
429210afeecSPeter Ujfalusi 			case 12000000:
430210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
431210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_12000KHZ |
432210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
433210afeecSPeter Ujfalusi 				break;
434210afeecSPeter Ujfalusi 			case 19200000:
435210afeecSPeter Ujfalusi 				/*
436210afeecSPeter Ujfalusi 				* PLL disabled
437210afeecSPeter Ujfalusi 				* (enable PLL if MCLK jitter quality
438210afeecSPeter Ujfalusi 				*  doesn't meet specification)
439210afeecSPeter Ujfalusi 				*/
440210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_19200KHZ;
441210afeecSPeter Ujfalusi 				break;
442210afeecSPeter Ujfalusi 			case 26000000:
443210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
444210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_26000KHZ |
445210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
446210afeecSPeter Ujfalusi 				break;
447210afeecSPeter Ujfalusi 			case 38400000:
448210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
449210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_38400KHZ |
450210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
451210afeecSPeter Ujfalusi 				break;
452210afeecSPeter Ujfalusi 			default:
453210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
454210afeecSPeter Ujfalusi 					"freq_in %d not supported\n", freq_in);
455210afeecSPeter Ujfalusi 				ret = -EINVAL;
456210afeecSPeter Ujfalusi 				goto pll_out;
457210afeecSPeter Ujfalusi 			}
458210afeecSPeter Ujfalusi 
459210afeecSPeter Ujfalusi 			/*
460210afeecSPeter Ujfalusi 			 * enable clock slicer to ensure input waveform is
461210afeecSPeter Ujfalusi 			 * square
462210afeecSPeter Ujfalusi 			 */
463210afeecSPeter Ujfalusi 			hppllctl |= TWL6040_HPLLSQRENA;
464210afeecSPeter Ujfalusi 
465210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
466210afeecSPeter Ujfalusi 					  hppllctl);
467210afeecSPeter Ujfalusi 			usleep_range(500, 700);
468210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_HPLLSEL;
469210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
470210afeecSPeter Ujfalusi 					  lppllctl);
471210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_LPLLENA;
472210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
473210afeecSPeter Ujfalusi 					  lppllctl);
474210afeecSPeter Ujfalusi 		}
475210afeecSPeter Ujfalusi 		break;
476210afeecSPeter Ujfalusi 	default:
477210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
478210afeecSPeter Ujfalusi 		ret = -EINVAL;
479210afeecSPeter Ujfalusi 		goto pll_out;
480210afeecSPeter Ujfalusi 	}
481210afeecSPeter Ujfalusi 
482210afeecSPeter Ujfalusi 	twl6040->sysclk = freq_out;
483210afeecSPeter Ujfalusi 	twl6040->mclk = freq_in;
484210afeecSPeter Ujfalusi 	twl6040->pll = pll_id;
485210afeecSPeter Ujfalusi 
486210afeecSPeter Ujfalusi pll_out:
487210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
488210afeecSPeter Ujfalusi 	return ret;
489210afeecSPeter Ujfalusi }
490210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_pll);
491210afeecSPeter Ujfalusi 
492210afeecSPeter Ujfalusi int twl6040_get_pll(struct twl6040 *twl6040)
493210afeecSPeter Ujfalusi {
494210afeecSPeter Ujfalusi 	if (twl6040->power_count)
495210afeecSPeter Ujfalusi 		return twl6040->pll;
496210afeecSPeter Ujfalusi 	else
497210afeecSPeter Ujfalusi 		return -ENODEV;
498210afeecSPeter Ujfalusi }
499210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_pll);
500210afeecSPeter Ujfalusi 
501210afeecSPeter Ujfalusi unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
502210afeecSPeter Ujfalusi {
503210afeecSPeter Ujfalusi 	return twl6040->sysclk;
504210afeecSPeter Ujfalusi }
505210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_sysclk);
506210afeecSPeter Ujfalusi 
507210afeecSPeter Ujfalusi /* Get the combined status of the vibra control register */
508210afeecSPeter Ujfalusi int twl6040_get_vibralr_status(struct twl6040 *twl6040)
509210afeecSPeter Ujfalusi {
510c6f39257SMark Brown 	unsigned int reg;
511c6f39257SMark Brown 	int ret;
512210afeecSPeter Ujfalusi 	u8 status;
513210afeecSPeter Ujfalusi 
514c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, &reg);
515c6f39257SMark Brown 	if (ret != 0)
516c6f39257SMark Brown 		return ret;
517c6f39257SMark Brown 	status = reg;
518c6f39257SMark Brown 
519c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, &reg);
520c6f39257SMark Brown 	if (ret != 0)
521c6f39257SMark Brown 		return ret;
522c6f39257SMark Brown 	status |= reg;
523c6f39257SMark Brown 
524210afeecSPeter Ujfalusi 	status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
525210afeecSPeter Ujfalusi 
526210afeecSPeter Ujfalusi 	return status;
527210afeecSPeter Ujfalusi }
528210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_vibralr_status);
529210afeecSPeter Ujfalusi 
530210afeecSPeter Ujfalusi static struct resource twl6040_vibra_rsrc[] = {
531210afeecSPeter Ujfalusi 	{
532210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
533210afeecSPeter Ujfalusi 	},
534210afeecSPeter Ujfalusi };
535210afeecSPeter Ujfalusi 
536210afeecSPeter Ujfalusi static struct resource twl6040_codec_rsrc[] = {
537210afeecSPeter Ujfalusi 	{
538210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
539210afeecSPeter Ujfalusi 	},
540210afeecSPeter Ujfalusi };
541210afeecSPeter Ujfalusi 
542210afeecSPeter Ujfalusi static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
543210afeecSPeter Ujfalusi {
544210afeecSPeter Ujfalusi 	/* Register 0 is not readable */
545210afeecSPeter Ujfalusi 	if (!reg)
546210afeecSPeter Ujfalusi 		return false;
547210afeecSPeter Ujfalusi 	return true;
548210afeecSPeter Ujfalusi }
549210afeecSPeter Ujfalusi 
550c6f39257SMark Brown static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)
551c6f39257SMark Brown {
552c6f39257SMark Brown 	switch (reg) {
553c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICID:
554c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICREV:
555c7f9129dSPeter Ujfalusi 	case TWL6040_REG_INTID:
556c7f9129dSPeter Ujfalusi 	case TWL6040_REG_LPPLLCTL:
557c7f9129dSPeter Ujfalusi 	case TWL6040_REG_HPPLLCTL:
558c7f9129dSPeter Ujfalusi 	case TWL6040_REG_STATUS:
559c7f9129dSPeter Ujfalusi 		return true;
560c7f9129dSPeter Ujfalusi 	default:
561c7f9129dSPeter Ujfalusi 		return false;
562c7f9129dSPeter Ujfalusi 	}
563c7f9129dSPeter Ujfalusi }
564c7f9129dSPeter Ujfalusi 
565c7f9129dSPeter Ujfalusi static bool twl6040_writeable_reg(struct device *dev, unsigned int reg)
566c7f9129dSPeter Ujfalusi {
567c7f9129dSPeter Ujfalusi 	switch (reg) {
568c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICID:
569c7f9129dSPeter Ujfalusi 	case TWL6040_REG_ASICREV:
570c7f9129dSPeter Ujfalusi 	case TWL6040_REG_STATUS:
571c6f39257SMark Brown 		return false;
572c6f39257SMark Brown 	default:
573c6f39257SMark Brown 		return true;
574c6f39257SMark Brown 	}
575c6f39257SMark Brown }
576c6f39257SMark Brown 
577210afeecSPeter Ujfalusi static struct regmap_config twl6040_regmap_config = {
578210afeecSPeter Ujfalusi 	.reg_bits = 8,
579210afeecSPeter Ujfalusi 	.val_bits = 8,
580c7f9129dSPeter Ujfalusi 
581c7f9129dSPeter Ujfalusi 	.reg_defaults = twl6040_defaults,
582c7f9129dSPeter Ujfalusi 	.num_reg_defaults = ARRAY_SIZE(twl6040_defaults),
583c7f9129dSPeter Ujfalusi 
584210afeecSPeter Ujfalusi 	.max_register = TWL6040_REG_STATUS, /* 0x2e */
585210afeecSPeter Ujfalusi 
586210afeecSPeter Ujfalusi 	.readable_reg = twl6040_readable_reg,
587c6f39257SMark Brown 	.volatile_reg = twl6040_volatile_reg,
588c7f9129dSPeter Ujfalusi 	.writeable_reg = twl6040_writeable_reg,
589c6f39257SMark Brown 
590c6f39257SMark Brown 	.cache_type = REGCACHE_RBTREE,
591210afeecSPeter Ujfalusi };
592210afeecSPeter Ujfalusi 
593210afeecSPeter Ujfalusi static const struct regmap_irq twl6040_irqs[] = {
594210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_THINT, },
595210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, },
596210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HOOKINT, },
597210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HFINT, },
598210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_VIBINT, },
599210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_READYINT, },
600210afeecSPeter Ujfalusi };
601210afeecSPeter Ujfalusi 
602210afeecSPeter Ujfalusi static struct regmap_irq_chip twl6040_irq_chip = {
603210afeecSPeter Ujfalusi 	.name = "twl6040",
604210afeecSPeter Ujfalusi 	.irqs = twl6040_irqs,
605210afeecSPeter Ujfalusi 	.num_irqs = ARRAY_SIZE(twl6040_irqs),
606210afeecSPeter Ujfalusi 
607210afeecSPeter Ujfalusi 	.num_regs = 1,
608210afeecSPeter Ujfalusi 	.status_base = TWL6040_REG_INTID,
609210afeecSPeter Ujfalusi 	.mask_base = TWL6040_REG_INTMR,
610210afeecSPeter Ujfalusi };
611210afeecSPeter Ujfalusi 
612612b95cdSGreg Kroah-Hartman static int twl6040_probe(struct i2c_client *client,
613210afeecSPeter Ujfalusi 			 const struct i2c_device_id *id)
614210afeecSPeter Ujfalusi {
615210afeecSPeter Ujfalusi 	struct device_node *node = client->dev.of_node;
616210afeecSPeter Ujfalusi 	struct twl6040 *twl6040;
617210afeecSPeter Ujfalusi 	struct mfd_cell *cell = NULL;
618210afeecSPeter Ujfalusi 	int irq, ret, children = 0;
619210afeecSPeter Ujfalusi 
620df04b624SPeter Ujfalusi 	if (!node) {
621df04b624SPeter Ujfalusi 		dev_err(&client->dev, "of node is missing\n");
622210afeecSPeter Ujfalusi 		return -EINVAL;
623210afeecSPeter Ujfalusi 	}
624210afeecSPeter Ujfalusi 
625210afeecSPeter Ujfalusi 	/* In order to operate correctly we need valid interrupt config */
626210afeecSPeter Ujfalusi 	if (!client->irq) {
627210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Invalid IRQ configuration\n");
628210afeecSPeter Ujfalusi 		return -EINVAL;
629210afeecSPeter Ujfalusi 	}
630210afeecSPeter Ujfalusi 
631210afeecSPeter Ujfalusi 	twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
632210afeecSPeter Ujfalusi 			       GFP_KERNEL);
633ecc8fa1cSPeter Ujfalusi 	if (!twl6040)
634ecc8fa1cSPeter Ujfalusi 		return -ENOMEM;
635210afeecSPeter Ujfalusi 
636210afeecSPeter Ujfalusi 	twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
637ecc8fa1cSPeter Ujfalusi 	if (IS_ERR(twl6040->regmap))
638ecc8fa1cSPeter Ujfalusi 		return PTR_ERR(twl6040->regmap);
639210afeecSPeter Ujfalusi 
640210afeecSPeter Ujfalusi 	i2c_set_clientdata(client, twl6040);
641210afeecSPeter Ujfalusi 
642210afeecSPeter Ujfalusi 	twl6040->supplies[0].supply = "vio";
643210afeecSPeter Ujfalusi 	twl6040->supplies[1].supply = "v2v1";
644990810b0SJingoo Han 	ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
645210afeecSPeter Ujfalusi 				      twl6040->supplies);
646210afeecSPeter Ujfalusi 	if (ret != 0) {
647210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
648501d609aSWolfram Sang 		return ret;
649210afeecSPeter Ujfalusi 	}
650210afeecSPeter Ujfalusi 
651210afeecSPeter Ujfalusi 	ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
652210afeecSPeter Ujfalusi 	if (ret != 0) {
653210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
654501d609aSWolfram Sang 		return ret;
655210afeecSPeter Ujfalusi 	}
656210afeecSPeter Ujfalusi 
657210afeecSPeter Ujfalusi 	twl6040->dev = &client->dev;
658210afeecSPeter Ujfalusi 	twl6040->irq = client->irq;
659210afeecSPeter Ujfalusi 
660210afeecSPeter Ujfalusi 	mutex_init(&twl6040->mutex);
661210afeecSPeter Ujfalusi 	init_completion(&twl6040->ready);
662210afeecSPeter Ujfalusi 
663210afeecSPeter Ujfalusi 	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
664210afeecSPeter Ujfalusi 
665210afeecSPeter Ujfalusi 	/* ERRATA: Automatic power-up is not possible in ES1.0 */
666df04b624SPeter Ujfalusi 	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
667210afeecSPeter Ujfalusi 		twl6040->audpwron = of_get_named_gpio(node,
668210afeecSPeter Ujfalusi 						      "ti,audpwron-gpio", 0);
669df04b624SPeter Ujfalusi 	else
670210afeecSPeter Ujfalusi 		twl6040->audpwron = -EINVAL;
671210afeecSPeter Ujfalusi 
672210afeecSPeter Ujfalusi 	if (gpio_is_valid(twl6040->audpwron)) {
673990810b0SJingoo Han 		ret = devm_gpio_request_one(&client->dev, twl6040->audpwron,
674990810b0SJingoo Han 					    GPIOF_OUT_INIT_LOW, "audpwron");
675210afeecSPeter Ujfalusi 		if (ret)
676210afeecSPeter Ujfalusi 			goto gpio_err;
677210afeecSPeter Ujfalusi 	}
678210afeecSPeter Ujfalusi 
67937aefe9fSPeter Ujfalusi 	ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
68037aefe9fSPeter Ujfalusi 				  0, &twl6040_irq_chip,&twl6040->irq_data);
681210afeecSPeter Ujfalusi 	if (ret < 0)
682990810b0SJingoo Han 		goto gpio_err;
683210afeecSPeter Ujfalusi 
684210afeecSPeter Ujfalusi 	twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,
685210afeecSPeter Ujfalusi 						 TWL6040_IRQ_READY);
686210afeecSPeter Ujfalusi 	twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,
687210afeecSPeter Ujfalusi 					      TWL6040_IRQ_TH);
688210afeecSPeter Ujfalusi 
689990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,
690210afeecSPeter Ujfalusi 					twl6040_readyint_handler, IRQF_ONESHOT,
691210afeecSPeter Ujfalusi 					"twl6040_irq_ready", twl6040);
692210afeecSPeter Ujfalusi 	if (ret) {
693210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret);
694210afeecSPeter Ujfalusi 		goto readyirq_err;
695210afeecSPeter Ujfalusi 	}
696210afeecSPeter Ujfalusi 
697990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,
698210afeecSPeter Ujfalusi 					twl6040_thint_handler, IRQF_ONESHOT,
699210afeecSPeter Ujfalusi 					"twl6040_irq_th", twl6040);
700210afeecSPeter Ujfalusi 	if (ret) {
701210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret);
702fc5ee96fSWei Yongjun 		goto readyirq_err;
703210afeecSPeter Ujfalusi 	}
704210afeecSPeter Ujfalusi 
705210afeecSPeter Ujfalusi 	/* dual-access registers controlled by I2C only */
706210afeecSPeter Ujfalusi 	twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
707c7f9129dSPeter Ujfalusi 	regmap_register_patch(twl6040->regmap, twl6040_patch,
708c7f9129dSPeter Ujfalusi 			      ARRAY_SIZE(twl6040_patch));
709210afeecSPeter Ujfalusi 
710210afeecSPeter Ujfalusi 	/*
711210afeecSPeter Ujfalusi 	 * The main functionality of twl6040 to provide audio on OMAP4+ systems.
712210afeecSPeter Ujfalusi 	 * We can add the ASoC codec child whenever this driver has been loaded.
713210afeecSPeter Ujfalusi 	 */
714210afeecSPeter Ujfalusi 	irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
715210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
716210afeecSPeter Ujfalusi 	cell->name = "twl6040-codec";
717210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].start = irq;
718210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].end = irq;
719210afeecSPeter Ujfalusi 	cell->resources = twl6040_codec_rsrc;
720210afeecSPeter Ujfalusi 	cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
721210afeecSPeter Ujfalusi 	children++;
722210afeecSPeter Ujfalusi 
723df04b624SPeter Ujfalusi 	/* Vibra input driver support */
724df04b624SPeter Ujfalusi 	if (twl6040_has_vibra(node)) {
725210afeecSPeter Ujfalusi 		irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
726210afeecSPeter Ujfalusi 
727210afeecSPeter Ujfalusi 		cell = &twl6040->cells[children];
728210afeecSPeter Ujfalusi 		cell->name = "twl6040-vibra";
729210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].start = irq;
730210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].end = irq;
731210afeecSPeter Ujfalusi 		cell->resources = twl6040_vibra_rsrc;
732210afeecSPeter Ujfalusi 		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
733210afeecSPeter Ujfalusi 		children++;
734210afeecSPeter Ujfalusi 	}
735210afeecSPeter Ujfalusi 
736df04b624SPeter Ujfalusi 	/* GPO support */
737210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
738210afeecSPeter Ujfalusi 	cell->name = "twl6040-gpo";
739210afeecSPeter Ujfalusi 	children++;
740210afeecSPeter Ujfalusi 
741c7f9129dSPeter Ujfalusi 	/* The chip is powered down so mark regmap to cache only and dirty */
742c7f9129dSPeter Ujfalusi 	regcache_cache_only(twl6040->regmap, true);
743c7f9129dSPeter Ujfalusi 	regcache_mark_dirty(twl6040->regmap);
744c7f9129dSPeter Ujfalusi 
745210afeecSPeter Ujfalusi 	ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
746210afeecSPeter Ujfalusi 			      NULL, 0, NULL);
747210afeecSPeter Ujfalusi 	if (ret)
748fc5ee96fSWei Yongjun 		goto readyirq_err;
749210afeecSPeter Ujfalusi 
750210afeecSPeter Ujfalusi 	return 0;
751210afeecSPeter Ujfalusi 
752210afeecSPeter Ujfalusi readyirq_err:
753210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
754210afeecSPeter Ujfalusi gpio_err:
755210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
756210afeecSPeter Ujfalusi 	return ret;
757210afeecSPeter Ujfalusi }
758210afeecSPeter Ujfalusi 
759612b95cdSGreg Kroah-Hartman static int twl6040_remove(struct i2c_client *client)
760210afeecSPeter Ujfalusi {
761210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = i2c_get_clientdata(client);
762210afeecSPeter Ujfalusi 
763210afeecSPeter Ujfalusi 	if (twl6040->power_count)
764210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
765210afeecSPeter Ujfalusi 
766210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
767210afeecSPeter Ujfalusi 
768210afeecSPeter Ujfalusi 	mfd_remove_devices(&client->dev);
769210afeecSPeter Ujfalusi 
770210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
771210afeecSPeter Ujfalusi 
772210afeecSPeter Ujfalusi 	return 0;
773210afeecSPeter Ujfalusi }
774210afeecSPeter Ujfalusi 
775210afeecSPeter Ujfalusi static const struct i2c_device_id twl6040_i2c_id[] = {
776210afeecSPeter Ujfalusi 	{ "twl6040", 0, },
777210afeecSPeter Ujfalusi 	{ "twl6041", 0, },
778210afeecSPeter Ujfalusi 	{ },
779210afeecSPeter Ujfalusi };
780210afeecSPeter Ujfalusi MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
781210afeecSPeter Ujfalusi 
782210afeecSPeter Ujfalusi static struct i2c_driver twl6040_driver = {
783210afeecSPeter Ujfalusi 	.driver = {
784210afeecSPeter Ujfalusi 		.name = "twl6040",
785210afeecSPeter Ujfalusi 		.owner = THIS_MODULE,
786210afeecSPeter Ujfalusi 	},
787210afeecSPeter Ujfalusi 	.probe		= twl6040_probe,
788612b95cdSGreg Kroah-Hartman 	.remove		= twl6040_remove,
789210afeecSPeter Ujfalusi 	.id_table	= twl6040_i2c_id,
790210afeecSPeter Ujfalusi };
791210afeecSPeter Ujfalusi 
792210afeecSPeter Ujfalusi module_i2c_driver(twl6040_driver);
793210afeecSPeter Ujfalusi 
794210afeecSPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 MFD");
795210afeecSPeter Ujfalusi MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
796210afeecSPeter Ujfalusi MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
797210afeecSPeter Ujfalusi MODULE_LICENSE("GPL");
798210afeecSPeter Ujfalusi MODULE_ALIAS("platform:twl6040");
799