xref: /openbmc/linux/drivers/mfd/twl6040.c (revision fc5ee96f)
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 
47df04b624SPeter Ujfalusi static bool twl6040_has_vibra(struct device_node *node)
48210afeecSPeter Ujfalusi {
49210afeecSPeter Ujfalusi #ifdef CONFIG_OF
50210afeecSPeter Ujfalusi 	if (of_find_node_by_name(node, "vibra"))
51210afeecSPeter Ujfalusi 		return true;
52210afeecSPeter Ujfalusi #endif
53210afeecSPeter Ujfalusi 	return false;
54210afeecSPeter Ujfalusi }
55210afeecSPeter Ujfalusi 
56210afeecSPeter Ujfalusi int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
57210afeecSPeter Ujfalusi {
58210afeecSPeter Ujfalusi 	int ret;
59210afeecSPeter Ujfalusi 	unsigned int val;
60210afeecSPeter Ujfalusi 
61210afeecSPeter Ujfalusi 	ret = regmap_read(twl6040->regmap, reg, &val);
62210afeecSPeter Ujfalusi 	if (ret < 0)
63210afeecSPeter Ujfalusi 		return ret;
64210afeecSPeter Ujfalusi 
65210afeecSPeter Ujfalusi 	return val;
66210afeecSPeter Ujfalusi }
67210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_read);
68210afeecSPeter Ujfalusi 
69210afeecSPeter Ujfalusi int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
70210afeecSPeter Ujfalusi {
71210afeecSPeter Ujfalusi 	int ret;
72210afeecSPeter Ujfalusi 
73210afeecSPeter Ujfalusi 	ret = regmap_write(twl6040->regmap, reg, val);
74210afeecSPeter Ujfalusi 
75210afeecSPeter Ujfalusi 	return ret;
76210afeecSPeter Ujfalusi }
77210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_write);
78210afeecSPeter Ujfalusi 
79210afeecSPeter Ujfalusi int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
80210afeecSPeter Ujfalusi {
81210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, mask);
82210afeecSPeter Ujfalusi }
83210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_bits);
84210afeecSPeter Ujfalusi 
85210afeecSPeter Ujfalusi int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
86210afeecSPeter Ujfalusi {
87210afeecSPeter Ujfalusi 	return regmap_update_bits(twl6040->regmap, reg, mask, 0);
88210afeecSPeter Ujfalusi }
89210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_clear_bits);
90210afeecSPeter Ujfalusi 
91210afeecSPeter Ujfalusi /* twl6040 codec manual power-up sequence */
92210afeecSPeter Ujfalusi static int twl6040_power_up_manual(struct twl6040 *twl6040)
93210afeecSPeter Ujfalusi {
94210afeecSPeter Ujfalusi 	u8 ldoctl, ncpctl, lppllctl;
95210afeecSPeter Ujfalusi 	int ret;
96210afeecSPeter Ujfalusi 
97210afeecSPeter Ujfalusi 	/* enable high-side LDO, reference system and internal oscillator */
98210afeecSPeter Ujfalusi 	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
99210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
100210afeecSPeter Ujfalusi 	if (ret)
101210afeecSPeter Ujfalusi 		return ret;
102210afeecSPeter Ujfalusi 	usleep_range(10000, 10500);
103210afeecSPeter Ujfalusi 
104210afeecSPeter Ujfalusi 	/* enable negative charge pump */
105210afeecSPeter Ujfalusi 	ncpctl = TWL6040_NCPENA;
106210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
107210afeecSPeter Ujfalusi 	if (ret)
108210afeecSPeter Ujfalusi 		goto ncp_err;
109210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
110210afeecSPeter Ujfalusi 
111210afeecSPeter Ujfalusi 	/* enable low-side LDO */
112210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_LSLDOENA;
113210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
114210afeecSPeter Ujfalusi 	if (ret)
115210afeecSPeter Ujfalusi 		goto lsldo_err;
116210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
117210afeecSPeter Ujfalusi 
118210afeecSPeter Ujfalusi 	/* enable low-power PLL */
119210afeecSPeter Ujfalusi 	lppllctl = TWL6040_LPLLENA;
120210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
121210afeecSPeter Ujfalusi 	if (ret)
122210afeecSPeter Ujfalusi 		goto lppll_err;
123210afeecSPeter Ujfalusi 	usleep_range(5000, 5500);
124210afeecSPeter Ujfalusi 
125210afeecSPeter Ujfalusi 	/* disable internal oscillator */
126210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_OSCENA;
127210afeecSPeter Ujfalusi 	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
128210afeecSPeter Ujfalusi 	if (ret)
129210afeecSPeter Ujfalusi 		goto osc_err;
130210afeecSPeter Ujfalusi 
131210afeecSPeter Ujfalusi 	return 0;
132210afeecSPeter Ujfalusi 
133210afeecSPeter Ujfalusi osc_err:
134210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
135210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
136210afeecSPeter Ujfalusi lppll_err:
137210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
138210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
139210afeecSPeter Ujfalusi lsldo_err:
140210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
141210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
142210afeecSPeter Ujfalusi ncp_err:
143210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
144210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
145210afeecSPeter Ujfalusi 
146210afeecSPeter Ujfalusi 	dev_err(twl6040->dev, "manual power-up failed\n");
147210afeecSPeter Ujfalusi 	return ret;
148210afeecSPeter Ujfalusi }
149210afeecSPeter Ujfalusi 
150210afeecSPeter Ujfalusi /* twl6040 manual power-down sequence */
151210afeecSPeter Ujfalusi static void twl6040_power_down_manual(struct twl6040 *twl6040)
152210afeecSPeter Ujfalusi {
153210afeecSPeter Ujfalusi 	u8 ncpctl, ldoctl, lppllctl;
154210afeecSPeter Ujfalusi 
155210afeecSPeter Ujfalusi 	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
156210afeecSPeter Ujfalusi 	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
157210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
158210afeecSPeter Ujfalusi 
159210afeecSPeter Ujfalusi 	/* enable internal oscillator */
160210afeecSPeter Ujfalusi 	ldoctl |= TWL6040_OSCENA;
161210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
162210afeecSPeter Ujfalusi 	usleep_range(1000, 1500);
163210afeecSPeter Ujfalusi 
164210afeecSPeter Ujfalusi 	/* disable low-power PLL */
165210afeecSPeter Ujfalusi 	lppllctl &= ~TWL6040_LPLLENA;
166210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
167210afeecSPeter Ujfalusi 
168210afeecSPeter Ujfalusi 	/* disable low-side LDO */
169210afeecSPeter Ujfalusi 	ldoctl &= ~TWL6040_LSLDOENA;
170210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
171210afeecSPeter Ujfalusi 
172210afeecSPeter Ujfalusi 	/* disable negative charge pump */
173210afeecSPeter Ujfalusi 	ncpctl &= ~TWL6040_NCPENA;
174210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
175210afeecSPeter Ujfalusi 
176210afeecSPeter Ujfalusi 	/* disable high-side LDO, reference system and internal oscillator */
177210afeecSPeter Ujfalusi 	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
178210afeecSPeter Ujfalusi 	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
179210afeecSPeter Ujfalusi }
180210afeecSPeter Ujfalusi 
181210afeecSPeter Ujfalusi static irqreturn_t twl6040_readyint_handler(int irq, void *data)
182210afeecSPeter Ujfalusi {
183210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
184210afeecSPeter Ujfalusi 
185210afeecSPeter Ujfalusi 	complete(&twl6040->ready);
186210afeecSPeter Ujfalusi 
187210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
188210afeecSPeter Ujfalusi }
189210afeecSPeter Ujfalusi 
190210afeecSPeter Ujfalusi static irqreturn_t twl6040_thint_handler(int irq, void *data)
191210afeecSPeter Ujfalusi {
192210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = data;
193210afeecSPeter Ujfalusi 	u8 status;
194210afeecSPeter Ujfalusi 
195210afeecSPeter Ujfalusi 	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
196210afeecSPeter Ujfalusi 	if (status & TWL6040_TSHUTDET) {
197210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Thermal shutdown, powering-off");
198210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
199210afeecSPeter Ujfalusi 	} else {
200210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on");
201210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 1);
202210afeecSPeter Ujfalusi 	}
203210afeecSPeter Ujfalusi 
204210afeecSPeter Ujfalusi 	return IRQ_HANDLED;
205210afeecSPeter Ujfalusi }
206210afeecSPeter Ujfalusi 
207210afeecSPeter Ujfalusi static int twl6040_power_up_automatic(struct twl6040 *twl6040)
208210afeecSPeter Ujfalusi {
209210afeecSPeter Ujfalusi 	int time_left;
210210afeecSPeter Ujfalusi 
211210afeecSPeter Ujfalusi 	gpio_set_value(twl6040->audpwron, 1);
212210afeecSPeter Ujfalusi 
213210afeecSPeter Ujfalusi 	time_left = wait_for_completion_timeout(&twl6040->ready,
214210afeecSPeter Ujfalusi 						msecs_to_jiffies(144));
215210afeecSPeter Ujfalusi 	if (!time_left) {
216210afeecSPeter Ujfalusi 		u8 intid;
217210afeecSPeter Ujfalusi 
218210afeecSPeter Ujfalusi 		dev_warn(twl6040->dev, "timeout waiting for READYINT\n");
219210afeecSPeter Ujfalusi 		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
220210afeecSPeter Ujfalusi 		if (!(intid & TWL6040_READYINT)) {
221210afeecSPeter Ujfalusi 			dev_err(twl6040->dev, "automatic power-up failed\n");
222210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
223210afeecSPeter Ujfalusi 			return -ETIMEDOUT;
224210afeecSPeter Ujfalusi 		}
225210afeecSPeter Ujfalusi 	}
226210afeecSPeter Ujfalusi 
227210afeecSPeter Ujfalusi 	return 0;
228210afeecSPeter Ujfalusi }
229210afeecSPeter Ujfalusi 
230210afeecSPeter Ujfalusi int twl6040_power(struct twl6040 *twl6040, int on)
231210afeecSPeter Ujfalusi {
232210afeecSPeter Ujfalusi 	int ret = 0;
233210afeecSPeter Ujfalusi 
234210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
235210afeecSPeter Ujfalusi 
236210afeecSPeter Ujfalusi 	if (on) {
237210afeecSPeter Ujfalusi 		/* already powered-up */
238210afeecSPeter Ujfalusi 		if (twl6040->power_count++)
239210afeecSPeter Ujfalusi 			goto out;
240210afeecSPeter Ujfalusi 
241210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
242210afeecSPeter Ujfalusi 			/* use automatic power-up sequence */
243210afeecSPeter Ujfalusi 			ret = twl6040_power_up_automatic(twl6040);
244210afeecSPeter Ujfalusi 			if (ret) {
245210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
246210afeecSPeter Ujfalusi 				goto out;
247210afeecSPeter Ujfalusi 			}
248210afeecSPeter Ujfalusi 		} else {
249210afeecSPeter Ujfalusi 			/* use manual power-up sequence */
250210afeecSPeter Ujfalusi 			ret = twl6040_power_up_manual(twl6040);
251210afeecSPeter Ujfalusi 			if (ret) {
252210afeecSPeter Ujfalusi 				twl6040->power_count = 0;
253210afeecSPeter Ujfalusi 				goto out;
254210afeecSPeter Ujfalusi 			}
255210afeecSPeter Ujfalusi 		}
256210afeecSPeter Ujfalusi 		/* Default PLL configuration after power up */
257210afeecSPeter Ujfalusi 		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
258210afeecSPeter Ujfalusi 		twl6040->sysclk = 19200000;
259210afeecSPeter Ujfalusi 		twl6040->mclk = 32768;
260210afeecSPeter Ujfalusi 	} else {
261210afeecSPeter Ujfalusi 		/* already powered-down */
262210afeecSPeter Ujfalusi 		if (!twl6040->power_count) {
263210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
264210afeecSPeter Ujfalusi 				"device is already powered-off\n");
265210afeecSPeter Ujfalusi 			ret = -EPERM;
266210afeecSPeter Ujfalusi 			goto out;
267210afeecSPeter Ujfalusi 		}
268210afeecSPeter Ujfalusi 
269210afeecSPeter Ujfalusi 		if (--twl6040->power_count)
270210afeecSPeter Ujfalusi 			goto out;
271210afeecSPeter Ujfalusi 
272210afeecSPeter Ujfalusi 		if (gpio_is_valid(twl6040->audpwron)) {
273210afeecSPeter Ujfalusi 			/* use AUDPWRON line */
274210afeecSPeter Ujfalusi 			gpio_set_value(twl6040->audpwron, 0);
275210afeecSPeter Ujfalusi 
276210afeecSPeter Ujfalusi 			/* power-down sequence latency */
277210afeecSPeter Ujfalusi 			usleep_range(500, 700);
278210afeecSPeter Ujfalusi 		} else {
279210afeecSPeter Ujfalusi 			/* use manual power-down sequence */
280210afeecSPeter Ujfalusi 			twl6040_power_down_manual(twl6040);
281210afeecSPeter Ujfalusi 		}
282210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
283210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
284210afeecSPeter Ujfalusi 	}
285210afeecSPeter Ujfalusi 
286210afeecSPeter Ujfalusi out:
287210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
288210afeecSPeter Ujfalusi 	return ret;
289210afeecSPeter Ujfalusi }
290210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_power);
291210afeecSPeter Ujfalusi 
292210afeecSPeter Ujfalusi int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
293210afeecSPeter Ujfalusi 		    unsigned int freq_in, unsigned int freq_out)
294210afeecSPeter Ujfalusi {
295210afeecSPeter Ujfalusi 	u8 hppllctl, lppllctl;
296210afeecSPeter Ujfalusi 	int ret = 0;
297210afeecSPeter Ujfalusi 
298210afeecSPeter Ujfalusi 	mutex_lock(&twl6040->mutex);
299210afeecSPeter Ujfalusi 
300210afeecSPeter Ujfalusi 	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
301210afeecSPeter Ujfalusi 	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
302210afeecSPeter Ujfalusi 
303210afeecSPeter Ujfalusi 	/* Force full reconfiguration when switching between PLL */
304210afeecSPeter Ujfalusi 	if (pll_id != twl6040->pll) {
305210afeecSPeter Ujfalusi 		twl6040->sysclk = 0;
306210afeecSPeter Ujfalusi 		twl6040->mclk = 0;
307210afeecSPeter Ujfalusi 	}
308210afeecSPeter Ujfalusi 
309210afeecSPeter Ujfalusi 	switch (pll_id) {
310210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_LPPLL:
311210afeecSPeter Ujfalusi 		/* low-power PLL divider */
312210afeecSPeter Ujfalusi 		/* Change the sysclk configuration only if it has been canged */
313210afeecSPeter Ujfalusi 		if (twl6040->sysclk != freq_out) {
314210afeecSPeter Ujfalusi 			switch (freq_out) {
315210afeecSPeter Ujfalusi 			case 17640000:
316210afeecSPeter Ujfalusi 				lppllctl |= TWL6040_LPLLFIN;
317210afeecSPeter Ujfalusi 				break;
318210afeecSPeter Ujfalusi 			case 19200000:
319210afeecSPeter Ujfalusi 				lppllctl &= ~TWL6040_LPLLFIN;
320210afeecSPeter Ujfalusi 				break;
321210afeecSPeter Ujfalusi 			default:
322210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
323210afeecSPeter Ujfalusi 					"freq_out %d not supported\n",
324210afeecSPeter Ujfalusi 					freq_out);
325210afeecSPeter Ujfalusi 				ret = -EINVAL;
326210afeecSPeter Ujfalusi 				goto pll_out;
327210afeecSPeter Ujfalusi 			}
328210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
329210afeecSPeter Ujfalusi 					  lppllctl);
330210afeecSPeter Ujfalusi 		}
331210afeecSPeter Ujfalusi 
332210afeecSPeter Ujfalusi 		/* The PLL in use has not been change, we can exit */
333210afeecSPeter Ujfalusi 		if (twl6040->pll == pll_id)
334210afeecSPeter Ujfalusi 			break;
335210afeecSPeter Ujfalusi 
336210afeecSPeter Ujfalusi 		switch (freq_in) {
337210afeecSPeter Ujfalusi 		case 32768:
338210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_LPLLENA;
339210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
340210afeecSPeter Ujfalusi 					  lppllctl);
341210afeecSPeter Ujfalusi 			mdelay(5);
342210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_HPLLSEL;
343210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
344210afeecSPeter Ujfalusi 					  lppllctl);
345210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_HPLLENA;
346210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
347210afeecSPeter Ujfalusi 					  hppllctl);
348210afeecSPeter Ujfalusi 			break;
349210afeecSPeter Ujfalusi 		default:
350210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
351210afeecSPeter Ujfalusi 				"freq_in %d not supported\n", freq_in);
352210afeecSPeter Ujfalusi 			ret = -EINVAL;
353210afeecSPeter Ujfalusi 			goto pll_out;
354210afeecSPeter Ujfalusi 		}
355210afeecSPeter Ujfalusi 		break;
356210afeecSPeter Ujfalusi 	case TWL6040_SYSCLK_SEL_HPPLL:
357210afeecSPeter Ujfalusi 		/* high-performance PLL can provide only 19.2 MHz */
358210afeecSPeter Ujfalusi 		if (freq_out != 19200000) {
359210afeecSPeter Ujfalusi 			dev_err(twl6040->dev,
360210afeecSPeter Ujfalusi 				"freq_out %d not supported\n", freq_out);
361210afeecSPeter Ujfalusi 			ret = -EINVAL;
362210afeecSPeter Ujfalusi 			goto pll_out;
363210afeecSPeter Ujfalusi 		}
364210afeecSPeter Ujfalusi 
365210afeecSPeter Ujfalusi 		if (twl6040->mclk != freq_in) {
366210afeecSPeter Ujfalusi 			hppllctl &= ~TWL6040_MCLK_MSK;
367210afeecSPeter Ujfalusi 
368210afeecSPeter Ujfalusi 			switch (freq_in) {
369210afeecSPeter Ujfalusi 			case 12000000:
370210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
371210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_12000KHZ |
372210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
373210afeecSPeter Ujfalusi 				break;
374210afeecSPeter Ujfalusi 			case 19200000:
375210afeecSPeter Ujfalusi 				/*
376210afeecSPeter Ujfalusi 				* PLL disabled
377210afeecSPeter Ujfalusi 				* (enable PLL if MCLK jitter quality
378210afeecSPeter Ujfalusi 				*  doesn't meet specification)
379210afeecSPeter Ujfalusi 				*/
380210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_19200KHZ;
381210afeecSPeter Ujfalusi 				break;
382210afeecSPeter Ujfalusi 			case 26000000:
383210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
384210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_26000KHZ |
385210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
386210afeecSPeter Ujfalusi 				break;
387210afeecSPeter Ujfalusi 			case 38400000:
388210afeecSPeter Ujfalusi 				/* PLL enabled, active mode */
389210afeecSPeter Ujfalusi 				hppllctl |= TWL6040_MCLK_38400KHZ |
390210afeecSPeter Ujfalusi 					    TWL6040_HPLLENA;
391210afeecSPeter Ujfalusi 				break;
392210afeecSPeter Ujfalusi 			default:
393210afeecSPeter Ujfalusi 				dev_err(twl6040->dev,
394210afeecSPeter Ujfalusi 					"freq_in %d not supported\n", freq_in);
395210afeecSPeter Ujfalusi 				ret = -EINVAL;
396210afeecSPeter Ujfalusi 				goto pll_out;
397210afeecSPeter Ujfalusi 			}
398210afeecSPeter Ujfalusi 
399210afeecSPeter Ujfalusi 			/*
400210afeecSPeter Ujfalusi 			 * enable clock slicer to ensure input waveform is
401210afeecSPeter Ujfalusi 			 * square
402210afeecSPeter Ujfalusi 			 */
403210afeecSPeter Ujfalusi 			hppllctl |= TWL6040_HPLLSQRENA;
404210afeecSPeter Ujfalusi 
405210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
406210afeecSPeter Ujfalusi 					  hppllctl);
407210afeecSPeter Ujfalusi 			usleep_range(500, 700);
408210afeecSPeter Ujfalusi 			lppllctl |= TWL6040_HPLLSEL;
409210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
410210afeecSPeter Ujfalusi 					  lppllctl);
411210afeecSPeter Ujfalusi 			lppllctl &= ~TWL6040_LPLLENA;
412210afeecSPeter Ujfalusi 			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
413210afeecSPeter Ujfalusi 					  lppllctl);
414210afeecSPeter Ujfalusi 		}
415210afeecSPeter Ujfalusi 		break;
416210afeecSPeter Ujfalusi 	default:
417210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
418210afeecSPeter Ujfalusi 		ret = -EINVAL;
419210afeecSPeter Ujfalusi 		goto pll_out;
420210afeecSPeter Ujfalusi 	}
421210afeecSPeter Ujfalusi 
422210afeecSPeter Ujfalusi 	twl6040->sysclk = freq_out;
423210afeecSPeter Ujfalusi 	twl6040->mclk = freq_in;
424210afeecSPeter Ujfalusi 	twl6040->pll = pll_id;
425210afeecSPeter Ujfalusi 
426210afeecSPeter Ujfalusi pll_out:
427210afeecSPeter Ujfalusi 	mutex_unlock(&twl6040->mutex);
428210afeecSPeter Ujfalusi 	return ret;
429210afeecSPeter Ujfalusi }
430210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_pll);
431210afeecSPeter Ujfalusi 
432210afeecSPeter Ujfalusi int twl6040_get_pll(struct twl6040 *twl6040)
433210afeecSPeter Ujfalusi {
434210afeecSPeter Ujfalusi 	if (twl6040->power_count)
435210afeecSPeter Ujfalusi 		return twl6040->pll;
436210afeecSPeter Ujfalusi 	else
437210afeecSPeter Ujfalusi 		return -ENODEV;
438210afeecSPeter Ujfalusi }
439210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_pll);
440210afeecSPeter Ujfalusi 
441210afeecSPeter Ujfalusi unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
442210afeecSPeter Ujfalusi {
443210afeecSPeter Ujfalusi 	return twl6040->sysclk;
444210afeecSPeter Ujfalusi }
445210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_sysclk);
446210afeecSPeter Ujfalusi 
447210afeecSPeter Ujfalusi /* Get the combined status of the vibra control register */
448210afeecSPeter Ujfalusi int twl6040_get_vibralr_status(struct twl6040 *twl6040)
449210afeecSPeter Ujfalusi {
450c6f39257SMark Brown 	unsigned int reg;
451c6f39257SMark Brown 	int ret;
452210afeecSPeter Ujfalusi 	u8 status;
453210afeecSPeter Ujfalusi 
454c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, &reg);
455c6f39257SMark Brown 	if (ret != 0)
456c6f39257SMark Brown 		return ret;
457c6f39257SMark Brown 	status = reg;
458c6f39257SMark Brown 
459c6f39257SMark Brown 	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, &reg);
460c6f39257SMark Brown 	if (ret != 0)
461c6f39257SMark Brown 		return ret;
462c6f39257SMark Brown 	status |= reg;
463c6f39257SMark Brown 
464210afeecSPeter Ujfalusi 	status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
465210afeecSPeter Ujfalusi 
466210afeecSPeter Ujfalusi 	return status;
467210afeecSPeter Ujfalusi }
468210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_vibralr_status);
469210afeecSPeter Ujfalusi 
470210afeecSPeter Ujfalusi static struct resource twl6040_vibra_rsrc[] = {
471210afeecSPeter Ujfalusi 	{
472210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
473210afeecSPeter Ujfalusi 	},
474210afeecSPeter Ujfalusi };
475210afeecSPeter Ujfalusi 
476210afeecSPeter Ujfalusi static struct resource twl6040_codec_rsrc[] = {
477210afeecSPeter Ujfalusi 	{
478210afeecSPeter Ujfalusi 		.flags = IORESOURCE_IRQ,
479210afeecSPeter Ujfalusi 	},
480210afeecSPeter Ujfalusi };
481210afeecSPeter Ujfalusi 
482210afeecSPeter Ujfalusi static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
483210afeecSPeter Ujfalusi {
484210afeecSPeter Ujfalusi 	/* Register 0 is not readable */
485210afeecSPeter Ujfalusi 	if (!reg)
486210afeecSPeter Ujfalusi 		return false;
487210afeecSPeter Ujfalusi 	return true;
488210afeecSPeter Ujfalusi }
489210afeecSPeter Ujfalusi 
490c6f39257SMark Brown static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)
491c6f39257SMark Brown {
492c6f39257SMark Brown 	switch (reg) {
493c6f39257SMark Brown 	case TWL6040_REG_VIBCTLL:
494c6f39257SMark Brown 	case TWL6040_REG_VIBCTLR:
495c6f39257SMark Brown 	case TWL6040_REG_INTMR:
496c6f39257SMark Brown 		return false;
497c6f39257SMark Brown 	default:
498c6f39257SMark Brown 		return true;
499c6f39257SMark Brown 	}
500c6f39257SMark Brown }
501c6f39257SMark Brown 
502210afeecSPeter Ujfalusi static struct regmap_config twl6040_regmap_config = {
503210afeecSPeter Ujfalusi 	.reg_bits = 8,
504210afeecSPeter Ujfalusi 	.val_bits = 8,
505210afeecSPeter Ujfalusi 	.max_register = TWL6040_REG_STATUS, /* 0x2e */
506210afeecSPeter Ujfalusi 
507210afeecSPeter Ujfalusi 	.readable_reg = twl6040_readable_reg,
508c6f39257SMark Brown 	.volatile_reg = twl6040_volatile_reg,
509c6f39257SMark Brown 
510c6f39257SMark Brown 	.cache_type = REGCACHE_RBTREE,
511210afeecSPeter Ujfalusi };
512210afeecSPeter Ujfalusi 
513210afeecSPeter Ujfalusi static const struct regmap_irq twl6040_irqs[] = {
514210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_THINT, },
515210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, },
516210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HOOKINT, },
517210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_HFINT, },
518210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_VIBINT, },
519210afeecSPeter Ujfalusi 	{ .reg_offset = 0, .mask = TWL6040_READYINT, },
520210afeecSPeter Ujfalusi };
521210afeecSPeter Ujfalusi 
522210afeecSPeter Ujfalusi static struct regmap_irq_chip twl6040_irq_chip = {
523210afeecSPeter Ujfalusi 	.name = "twl6040",
524210afeecSPeter Ujfalusi 	.irqs = twl6040_irqs,
525210afeecSPeter Ujfalusi 	.num_irqs = ARRAY_SIZE(twl6040_irqs),
526210afeecSPeter Ujfalusi 
527210afeecSPeter Ujfalusi 	.num_regs = 1,
528210afeecSPeter Ujfalusi 	.status_base = TWL6040_REG_INTID,
529210afeecSPeter Ujfalusi 	.mask_base = TWL6040_REG_INTMR,
530210afeecSPeter Ujfalusi };
531210afeecSPeter Ujfalusi 
532612b95cdSGreg Kroah-Hartman static int twl6040_probe(struct i2c_client *client,
533210afeecSPeter Ujfalusi 			 const struct i2c_device_id *id)
534210afeecSPeter Ujfalusi {
535210afeecSPeter Ujfalusi 	struct device_node *node = client->dev.of_node;
536210afeecSPeter Ujfalusi 	struct twl6040 *twl6040;
537210afeecSPeter Ujfalusi 	struct mfd_cell *cell = NULL;
538210afeecSPeter Ujfalusi 	int irq, ret, children = 0;
539210afeecSPeter Ujfalusi 
540df04b624SPeter Ujfalusi 	if (!node) {
541df04b624SPeter Ujfalusi 		dev_err(&client->dev, "of node is missing\n");
542210afeecSPeter Ujfalusi 		return -EINVAL;
543210afeecSPeter Ujfalusi 	}
544210afeecSPeter Ujfalusi 
545210afeecSPeter Ujfalusi 	/* In order to operate correctly we need valid interrupt config */
546210afeecSPeter Ujfalusi 	if (!client->irq) {
547210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Invalid IRQ configuration\n");
548210afeecSPeter Ujfalusi 		return -EINVAL;
549210afeecSPeter Ujfalusi 	}
550210afeecSPeter Ujfalusi 
551210afeecSPeter Ujfalusi 	twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
552210afeecSPeter Ujfalusi 			       GFP_KERNEL);
553ecc8fa1cSPeter Ujfalusi 	if (!twl6040)
554ecc8fa1cSPeter Ujfalusi 		return -ENOMEM;
555210afeecSPeter Ujfalusi 
556210afeecSPeter Ujfalusi 	twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
557ecc8fa1cSPeter Ujfalusi 	if (IS_ERR(twl6040->regmap))
558ecc8fa1cSPeter Ujfalusi 		return PTR_ERR(twl6040->regmap);
559210afeecSPeter Ujfalusi 
560210afeecSPeter Ujfalusi 	i2c_set_clientdata(client, twl6040);
561210afeecSPeter Ujfalusi 
562210afeecSPeter Ujfalusi 	twl6040->supplies[0].supply = "vio";
563210afeecSPeter Ujfalusi 	twl6040->supplies[1].supply = "v2v1";
564990810b0SJingoo Han 	ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
565210afeecSPeter Ujfalusi 				      twl6040->supplies);
566210afeecSPeter Ujfalusi 	if (ret != 0) {
567210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
568210afeecSPeter Ujfalusi 		goto regulator_get_err;
569210afeecSPeter Ujfalusi 	}
570210afeecSPeter Ujfalusi 
571210afeecSPeter Ujfalusi 	ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
572210afeecSPeter Ujfalusi 	if (ret != 0) {
573210afeecSPeter Ujfalusi 		dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
574990810b0SJingoo Han 		goto regulator_get_err;
575210afeecSPeter Ujfalusi 	}
576210afeecSPeter Ujfalusi 
577210afeecSPeter Ujfalusi 	twl6040->dev = &client->dev;
578210afeecSPeter Ujfalusi 	twl6040->irq = client->irq;
579210afeecSPeter Ujfalusi 
580210afeecSPeter Ujfalusi 	mutex_init(&twl6040->mutex);
581210afeecSPeter Ujfalusi 	init_completion(&twl6040->ready);
582210afeecSPeter Ujfalusi 
583210afeecSPeter Ujfalusi 	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
584210afeecSPeter Ujfalusi 
585210afeecSPeter Ujfalusi 	/* ERRATA: Automatic power-up is not possible in ES1.0 */
586df04b624SPeter Ujfalusi 	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
587210afeecSPeter Ujfalusi 		twl6040->audpwron = of_get_named_gpio(node,
588210afeecSPeter Ujfalusi 						      "ti,audpwron-gpio", 0);
589df04b624SPeter Ujfalusi 	else
590210afeecSPeter Ujfalusi 		twl6040->audpwron = -EINVAL;
591210afeecSPeter Ujfalusi 
592210afeecSPeter Ujfalusi 	if (gpio_is_valid(twl6040->audpwron)) {
593990810b0SJingoo Han 		ret = devm_gpio_request_one(&client->dev, twl6040->audpwron,
594990810b0SJingoo Han 					    GPIOF_OUT_INIT_LOW, "audpwron");
595210afeecSPeter Ujfalusi 		if (ret)
596210afeecSPeter Ujfalusi 			goto gpio_err;
597210afeecSPeter Ujfalusi 	}
598210afeecSPeter Ujfalusi 
59937aefe9fSPeter Ujfalusi 	ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
60037aefe9fSPeter Ujfalusi 				  0, &twl6040_irq_chip,&twl6040->irq_data);
601210afeecSPeter Ujfalusi 	if (ret < 0)
602990810b0SJingoo Han 		goto gpio_err;
603210afeecSPeter Ujfalusi 
604210afeecSPeter Ujfalusi 	twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,
605210afeecSPeter Ujfalusi 						 TWL6040_IRQ_READY);
606210afeecSPeter Ujfalusi 	twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,
607210afeecSPeter Ujfalusi 					      TWL6040_IRQ_TH);
608210afeecSPeter Ujfalusi 
609990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,
610210afeecSPeter Ujfalusi 					twl6040_readyint_handler, IRQF_ONESHOT,
611210afeecSPeter Ujfalusi 					"twl6040_irq_ready", twl6040);
612210afeecSPeter Ujfalusi 	if (ret) {
613210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret);
614210afeecSPeter Ujfalusi 		goto readyirq_err;
615210afeecSPeter Ujfalusi 	}
616210afeecSPeter Ujfalusi 
617990810b0SJingoo Han 	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,
618210afeecSPeter Ujfalusi 					twl6040_thint_handler, IRQF_ONESHOT,
619210afeecSPeter Ujfalusi 					"twl6040_irq_th", twl6040);
620210afeecSPeter Ujfalusi 	if (ret) {
621210afeecSPeter Ujfalusi 		dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret);
622fc5ee96fSWei Yongjun 		goto readyirq_err;
623210afeecSPeter Ujfalusi 	}
624210afeecSPeter Ujfalusi 
625210afeecSPeter Ujfalusi 	/* dual-access registers controlled by I2C only */
626210afeecSPeter Ujfalusi 	twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
627210afeecSPeter Ujfalusi 
628210afeecSPeter Ujfalusi 	/*
629210afeecSPeter Ujfalusi 	 * The main functionality of twl6040 to provide audio on OMAP4+ systems.
630210afeecSPeter Ujfalusi 	 * We can add the ASoC codec child whenever this driver has been loaded.
631210afeecSPeter Ujfalusi 	 */
632210afeecSPeter Ujfalusi 	irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
633210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
634210afeecSPeter Ujfalusi 	cell->name = "twl6040-codec";
635210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].start = irq;
636210afeecSPeter Ujfalusi 	twl6040_codec_rsrc[0].end = irq;
637210afeecSPeter Ujfalusi 	cell->resources = twl6040_codec_rsrc;
638210afeecSPeter Ujfalusi 	cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
639210afeecSPeter Ujfalusi 	children++;
640210afeecSPeter Ujfalusi 
641df04b624SPeter Ujfalusi 	/* Vibra input driver support */
642df04b624SPeter Ujfalusi 	if (twl6040_has_vibra(node)) {
643210afeecSPeter Ujfalusi 		irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
644210afeecSPeter Ujfalusi 
645210afeecSPeter Ujfalusi 		cell = &twl6040->cells[children];
646210afeecSPeter Ujfalusi 		cell->name = "twl6040-vibra";
647210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].start = irq;
648210afeecSPeter Ujfalusi 		twl6040_vibra_rsrc[0].end = irq;
649210afeecSPeter Ujfalusi 		cell->resources = twl6040_vibra_rsrc;
650210afeecSPeter Ujfalusi 		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
651210afeecSPeter Ujfalusi 		children++;
652210afeecSPeter Ujfalusi 	}
653210afeecSPeter Ujfalusi 
654df04b624SPeter Ujfalusi 	/* GPO support */
655210afeecSPeter Ujfalusi 	cell = &twl6040->cells[children];
656210afeecSPeter Ujfalusi 	cell->name = "twl6040-gpo";
657210afeecSPeter Ujfalusi 	children++;
658210afeecSPeter Ujfalusi 
659210afeecSPeter Ujfalusi 	ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
660210afeecSPeter Ujfalusi 			      NULL, 0, NULL);
661210afeecSPeter Ujfalusi 	if (ret)
662fc5ee96fSWei Yongjun 		goto readyirq_err;
663210afeecSPeter Ujfalusi 
664210afeecSPeter Ujfalusi 	return 0;
665210afeecSPeter Ujfalusi 
666210afeecSPeter Ujfalusi readyirq_err:
667210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
668210afeecSPeter Ujfalusi gpio_err:
669210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
670210afeecSPeter Ujfalusi regulator_get_err:
671210afeecSPeter Ujfalusi 	i2c_set_clientdata(client, NULL);
672ecc8fa1cSPeter Ujfalusi 
673210afeecSPeter Ujfalusi 	return ret;
674210afeecSPeter Ujfalusi }
675210afeecSPeter Ujfalusi 
676612b95cdSGreg Kroah-Hartman static int twl6040_remove(struct i2c_client *client)
677210afeecSPeter Ujfalusi {
678210afeecSPeter Ujfalusi 	struct twl6040 *twl6040 = i2c_get_clientdata(client);
679210afeecSPeter Ujfalusi 
680210afeecSPeter Ujfalusi 	if (twl6040->power_count)
681210afeecSPeter Ujfalusi 		twl6040_power(twl6040, 0);
682210afeecSPeter Ujfalusi 
683210afeecSPeter Ujfalusi 	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
684210afeecSPeter Ujfalusi 
685210afeecSPeter Ujfalusi 	mfd_remove_devices(&client->dev);
686210afeecSPeter Ujfalusi 	i2c_set_clientdata(client, NULL);
687210afeecSPeter Ujfalusi 
688210afeecSPeter Ujfalusi 	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
689210afeecSPeter Ujfalusi 
690210afeecSPeter Ujfalusi 	return 0;
691210afeecSPeter Ujfalusi }
692210afeecSPeter Ujfalusi 
693210afeecSPeter Ujfalusi static const struct i2c_device_id twl6040_i2c_id[] = {
694210afeecSPeter Ujfalusi 	{ "twl6040", 0, },
695210afeecSPeter Ujfalusi 	{ "twl6041", 0, },
696210afeecSPeter Ujfalusi 	{ },
697210afeecSPeter Ujfalusi };
698210afeecSPeter Ujfalusi MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
699210afeecSPeter Ujfalusi 
700210afeecSPeter Ujfalusi static struct i2c_driver twl6040_driver = {
701210afeecSPeter Ujfalusi 	.driver = {
702210afeecSPeter Ujfalusi 		.name = "twl6040",
703210afeecSPeter Ujfalusi 		.owner = THIS_MODULE,
704210afeecSPeter Ujfalusi 	},
705210afeecSPeter Ujfalusi 	.probe		= twl6040_probe,
706612b95cdSGreg Kroah-Hartman 	.remove		= twl6040_remove,
707210afeecSPeter Ujfalusi 	.id_table	= twl6040_i2c_id,
708210afeecSPeter Ujfalusi };
709210afeecSPeter Ujfalusi 
710210afeecSPeter Ujfalusi module_i2c_driver(twl6040_driver);
711210afeecSPeter Ujfalusi 
712210afeecSPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 MFD");
713210afeecSPeter Ujfalusi MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
714210afeecSPeter Ujfalusi MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
715210afeecSPeter Ujfalusi MODULE_LICENSE("GPL");
716210afeecSPeter Ujfalusi MODULE_ALIAS("platform:twl6040");
717