Blame view

drivers/mfd/twl6040.c 20.8 KB
2b27bdcc2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
2
3
4
5
6
7
8
9
  /*
   * MFD driver for TWL6040 audio device
   *
   * Authors:	Misael Lopez Cruz <misael.lopez@ti.com>
   *		Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
   *		Peter Ujfalusi <peter.ujfalusi@ti.com>
   *
   * Copyright:	(C) 2011 Texas Instruments, Inc.
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
10
11
12
13
14
15
   */
  
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/slab.h>
  #include <linux/kernel.h>
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
16
  #include <linux/err.h>
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
17
  #include <linux/platform_device.h>
37e13ceca   Peter Ujfalusi   mfd: Add support ...
18
19
20
21
  #include <linux/of.h>
  #include <linux/of_irq.h>
  #include <linux/of_gpio.h>
  #include <linux/of_platform.h>
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
22
23
  #include <linux/gpio.h>
  #include <linux/delay.h>
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
24
25
  #include <linux/i2c.h>
  #include <linux/regmap.h>
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
26
27
  #include <linux/mfd/core.h>
  #include <linux/mfd/twl6040.h>
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
28
  #include <linux/regulator/consumer.h>
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
29

31b402e3c   Peter Ujfalusi   MFD: twl6040: Cac...
30
  #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
31
  #define TWL6040_NUM_SUPPLIES	(2)
31b402e3c   Peter Ujfalusi   MFD: twl6040: Cac...
32

de1e23f83   Krzysztof Kozlowski   mfd: twl6040: Con...
33
  static const struct reg_default twl6040_defaults[] = {
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  	{ 0x01, 0x4B }, /* REG_ASICID	(ro) */
  	{ 0x02, 0x00 }, /* REG_ASICREV	(ro) */
  	{ 0x03, 0x00 }, /* REG_INTID	*/
  	{ 0x04, 0x00 }, /* REG_INTMR	*/
  	{ 0x05, 0x00 }, /* REG_NCPCTRL	*/
  	{ 0x06, 0x00 }, /* REG_LDOCTL	*/
  	{ 0x07, 0x60 }, /* REG_HPPLLCTL	*/
  	{ 0x08, 0x00 }, /* REG_LPPLLCTL	*/
  	{ 0x09, 0x4A }, /* REG_LPPLLDIV	*/
  	{ 0x0A, 0x00 }, /* REG_AMICBCTL	*/
  	{ 0x0B, 0x00 }, /* REG_DMICBCTL	*/
  	{ 0x0C, 0x00 }, /* REG_MICLCTL	*/
  	{ 0x0D, 0x00 }, /* REG_MICRCTL	*/
  	{ 0x0E, 0x00 }, /* REG_MICGAIN	*/
  	{ 0x0F, 0x1B }, /* REG_LINEGAIN	*/
  	{ 0x10, 0x00 }, /* REG_HSLCTL	*/
  	{ 0x11, 0x00 }, /* REG_HSRCTL	*/
  	{ 0x12, 0x00 }, /* REG_HSGAIN	*/
  	{ 0x13, 0x00 }, /* REG_EARCTL	*/
  	{ 0x14, 0x00 }, /* REG_HFLCTL	*/
  	{ 0x15, 0x00 }, /* REG_HFLGAIN	*/
  	{ 0x16, 0x00 }, /* REG_HFRCTL	*/
  	{ 0x17, 0x00 }, /* REG_HFRGAIN	*/
  	{ 0x18, 0x00 }, /* REG_VIBCTLL	*/
  	{ 0x19, 0x00 }, /* REG_VIBDATL	*/
  	{ 0x1A, 0x00 }, /* REG_VIBCTLR	*/
  	{ 0x1B, 0x00 }, /* REG_VIBDATR	*/
  	{ 0x1C, 0x00 }, /* REG_HKCTL1	*/
  	{ 0x1D, 0x00 }, /* REG_HKCTL2	*/
  	{ 0x1E, 0x00 }, /* REG_GPOCTL	*/
  	{ 0x1F, 0x00 }, /* REG_ALB	*/
  	{ 0x20, 0x00 }, /* REG_DLB	*/
  	/* 0x28, REG_TRIM1 */
  	/* 0x29, REG_TRIM2 */
  	/* 0x2A, REG_TRIM3 */
  	/* 0x2B, REG_HSOTRIM */
  	/* 0x2C, REG_HFOTRIM */
  	{ 0x2D, 0x08 }, /* REG_ACCCTL	*/
  	{ 0x2E, 0x00 }, /* REG_STATUS	(ro) */
  };
8019ff6cf   Nariman Poushin   regmap: Use reg_s...
74
  static struct reg_sequence twl6040_patch[] = {
11e38e11a   Peter Ujfalusi   mfd: twl6040: Sel...
75
76
77
78
79
80
81
  	/*
  	 * Select I2C bus access to dual access registers
  	 * Interrupt register is cleared on read
  	 * Select fast mode for i2c (400KHz)
  	 */
  	{ TWL6040_REG_ACCCTL,
  		TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) },
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
82
  };
85e9b13cb   Johan Hovold   mfd: twl6040: Fix...
83
  static bool twl6040_has_vibra(struct device_node *parent)
ca2cad6ae   Samuel Ortiz   mfd: Fix twl6040 ...
84
  {
85e9b13cb   Johan Hovold   mfd: twl6040: Fix...
85
86
87
88
89
  	struct device_node *node;
  
  	node = of_get_child_by_name(parent, "vibra");
  	if (node) {
  		of_node_put(node);
ca2cad6ae   Samuel Ortiz   mfd: Fix twl6040 ...
90
  		return true;
85e9b13cb   Johan Hovold   mfd: twl6040: Fix...
91
  	}
ca2cad6ae   Samuel Ortiz   mfd: Fix twl6040 ...
92
93
  	return false;
  }
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
94
95
96
  int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
  {
  	int ret;
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
97
  	unsigned int val;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
98

c6f39257c   Mark Brown   mfd: twl6040: Use...
99
100
101
  	ret = regmap_read(twl6040->regmap, reg, &val);
  	if (ret < 0)
  		return ret;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
102
103
104
105
106
107
108
109
  
  	return val;
  }
  EXPORT_SYMBOL(twl6040_reg_read);
  
  int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
  {
  	int ret;
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
110
  	ret = regmap_write(twl6040->regmap, reg, val);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
111
112
113
114
115
116
117
  
  	return ret;
  }
  EXPORT_SYMBOL(twl6040_reg_write);
  
  int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
  {
c600040f0   Axel Lin   mfd: Remove unnee...
118
  	return regmap_update_bits(twl6040->regmap, reg, mask, mask);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
119
120
121
122
123
  }
  EXPORT_SYMBOL(twl6040_set_bits);
  
  int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
  {
c600040f0   Axel Lin   mfd: Remove unnee...
124
  	return regmap_update_bits(twl6040->regmap, reg, mask, 0);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
125
126
127
128
  }
  EXPORT_SYMBOL(twl6040_clear_bits);
  
  /* twl6040 codec manual power-up sequence */
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
129
  static int twl6040_power_up_manual(struct twl6040 *twl6040)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  {
  	u8 ldoctl, ncpctl, lppllctl;
  	int ret;
  
  	/* enable high-side LDO, reference system and internal oscillator */
  	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
  	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  	if (ret)
  		return ret;
  	usleep_range(10000, 10500);
  
  	/* enable negative charge pump */
  	ncpctl = TWL6040_NCPENA;
  	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
  	if (ret)
  		goto ncp_err;
  	usleep_range(1000, 1500);
  
  	/* enable low-side LDO */
  	ldoctl |= TWL6040_LSLDOENA;
  	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  	if (ret)
  		goto lsldo_err;
  	usleep_range(1000, 1500);
  
  	/* enable low-power PLL */
  	lppllctl = TWL6040_LPLLENA;
  	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
  	if (ret)
  		goto lppll_err;
  	usleep_range(5000, 5500);
  
  	/* disable internal oscillator */
  	ldoctl &= ~TWL6040_OSCENA;
  	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  	if (ret)
  		goto osc_err;
  
  	return 0;
  
  osc_err:
  	lppllctl &= ~TWL6040_LPLLENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
  lppll_err:
  	ldoctl &= ~TWL6040_LSLDOENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  lsldo_err:
  	ncpctl &= ~TWL6040_NCPENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
  ncp_err:
  	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
  	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
182
183
  	dev_err(twl6040->dev, "manual power-up failed
  ");
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
184
185
186
187
  	return ret;
  }
  
  /* twl6040 manual power-down sequence */
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
188
  static void twl6040_power_down_manual(struct twl6040 *twl6040)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  {
  	u8 ncpctl, ldoctl, lppllctl;
  
  	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
  	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
  	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
  
  	/* enable internal oscillator */
  	ldoctl |= TWL6040_OSCENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  	usleep_range(1000, 1500);
  
  	/* disable low-power PLL */
  	lppllctl &= ~TWL6040_LPLLENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
  
  	/* disable low-side LDO */
  	ldoctl &= ~TWL6040_LSLDOENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  
  	/* disable negative charge pump */
  	ncpctl &= ~TWL6040_NCPENA;
  	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
  
  	/* disable high-side LDO, reference system and internal oscillator */
  	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
  	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
  }
1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
217
  static irqreturn_t twl6040_readyint_handler(int irq, void *data)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
218
219
  {
  	struct twl6040 *twl6040 = data;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
220

1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
221
  	complete(&twl6040->ready);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
222

1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
223
224
  	return IRQ_HANDLED;
  }
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
225

1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
226
227
228
229
230
231
232
233
234
235
236
237
  static irqreturn_t twl6040_thint_handler(int irq, void *data)
  {
  	struct twl6040 *twl6040 = data;
  	u8 status;
  
  	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
  	if (status & TWL6040_TSHUTDET) {
  		dev_warn(twl6040->dev, "Thermal shutdown, powering-off");
  		twl6040_power(twl6040, 0);
  	} else {
  		dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on");
  		twl6040_power(twl6040, 1);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
238
239
240
241
  	}
  
  	return IRQ_HANDLED;
  }
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
242
  static int twl6040_power_up_automatic(struct twl6040 *twl6040)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
243
244
  {
  	int time_left;
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
245
246
  
  	gpio_set_value(twl6040->audpwron, 1);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
247
248
249
250
  
  	time_left = wait_for_completion_timeout(&twl6040->ready,
  						msecs_to_jiffies(144));
  	if (!time_left) {
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
251
252
253
254
  		u8 intid;
  
  		dev_warn(twl6040->dev, "timeout waiting for READYINT
  ");
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
255
256
  		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
  		if (!(intid & TWL6040_READYINT)) {
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
257
258
259
  			dev_err(twl6040->dev, "automatic power-up failed
  ");
  			gpio_set_value(twl6040->audpwron, 0);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
260
261
262
263
264
265
266
267
268
  			return -ETIMEDOUT;
  		}
  	}
  
  	return 0;
  }
  
  int twl6040_power(struct twl6040 *twl6040, int on)
  {
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
269
270
271
272
273
274
275
276
  	int ret = 0;
  
  	mutex_lock(&twl6040->mutex);
  
  	if (on) {
  		/* already powered-up */
  		if (twl6040->power_count++)
  			goto out;
32852bcab   Javier Martinez Canillas   mfd: twl6040: Che...
277
278
279
280
281
  		ret = clk_prepare_enable(twl6040->clk32k);
  		if (ret) {
  			twl6040->power_count = 0;
  			goto out;
  		}
68bab8662   Peter Ujfalusi   mfd: twl6040: Opt...
282

c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
283
284
  		/* Allow writes to the chip */
  		regcache_cache_only(twl6040->regmap, false);
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
285
286
287
  		if (gpio_is_valid(twl6040->audpwron)) {
  			/* use automatic power-up sequence */
  			ret = twl6040_power_up_automatic(twl6040);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
288
  			if (ret) {
d6441dc5c   Javier Martinez Canillas   mfd: twl6040: Dis...
289
  				clk_disable_unprepare(twl6040->clk32k);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
290
291
292
293
294
  				twl6040->power_count = 0;
  				goto out;
  			}
  		} else {
  			/* use manual power-up sequence */
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
295
  			ret = twl6040_power_up_manual(twl6040);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
296
  			if (ret) {
d6441dc5c   Javier Martinez Canillas   mfd: twl6040: Dis...
297
  				clk_disable_unprepare(twl6040->clk32k);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
298
299
300
301
  				twl6040->power_count = 0;
  				goto out;
  			}
  		}
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
302

48171d0ea   Tony Lindgren   mfd: twl6040: Fix...
303
304
305
306
307
  		/*
  		 * Register access can produce errors after power-up unless we
  		 * wait at least 8ms based on measurements on duovero.
  		 */
  		usleep_range(10000, 12000);
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
308
  		/* Sync with the HW */
48171d0ea   Tony Lindgren   mfd: twl6040: Fix...
309
310
311
312
313
314
315
  		ret = regcache_sync(twl6040->regmap);
  		if (ret) {
  			dev_err(twl6040->dev, "Failed to sync with the HW: %i
  ",
  				ret);
  			goto out;
  		}
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
316

cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
317
318
  		/* Default PLL configuration after power up */
  		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
319
  		twl6040->sysclk_rate = 19200000;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
320
321
322
  	} else {
  		/* already powered-down */
  		if (!twl6040->power_count) {
2d7c957e2   Peter Ujfalusi   MFD: twl6040: Rem...
323
  			dev_err(twl6040->dev,
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
324
325
326
327
328
329
330
331
  				"device is already powered-off
  ");
  			ret = -EPERM;
  			goto out;
  		}
  
  		if (--twl6040->power_count)
  			goto out;
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
332
  		if (gpio_is_valid(twl6040->audpwron)) {
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
333
  			/* use AUDPWRON line */
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
334
  			gpio_set_value(twl6040->audpwron, 0);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
335
336
337
338
339
  
  			/* power-down sequence latency */
  			usleep_range(500, 700);
  		} else {
  			/* use manual power-down sequence */
f9be13435   Peter Ujfalusi   mfd: twl6040: Res...
340
  			twl6040_power_down_manual(twl6040);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
341
  		}
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
342
343
344
345
  
  		/* Set regmap to cache only and mark it as dirty */
  		regcache_cache_only(twl6040->regmap, true);
  		regcache_mark_dirty(twl6040->regmap);
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
346
347
348
349
350
351
  		twl6040->sysclk_rate = 0;
  
  		if (twl6040->pll == TWL6040_SYSCLK_SEL_HPPLL) {
  			clk_disable_unprepare(twl6040->mclk);
  			twl6040->mclk_rate = 0;
  		}
68bab8662   Peter Ujfalusi   mfd: twl6040: Opt...
352
353
  
  		clk_disable_unprepare(twl6040->clk32k);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
354
355
356
357
358
359
360
  	}
  
  out:
  	mutex_unlock(&twl6040->mutex);
  	return ret;
  }
  EXPORT_SYMBOL(twl6040_power);
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
361
  int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
362
363
364
365
366
367
368
369
370
  		    unsigned int freq_in, unsigned int freq_out)
  {
  	u8 hppllctl, lppllctl;
  	int ret = 0;
  
  	mutex_lock(&twl6040->mutex);
  
  	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
  	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
371
372
  	/* Force full reconfiguration when switching between PLL */
  	if (pll_id != twl6040->pll) {
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
373
374
  		twl6040->sysclk_rate = 0;
  		twl6040->mclk_rate = 0;
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
375
  	}
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
376
377
  	switch (pll_id) {
  	case TWL6040_SYSCLK_SEL_LPPLL:
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
378
  		/* low-power PLL divider */
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
379
  		/* Change the sysclk configuration only if it has been canged */
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
380
  		if (twl6040->sysclk_rate != freq_out) {
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
  			switch (freq_out) {
  			case 17640000:
  				lppllctl |= TWL6040_LPLLFIN;
  				break;
  			case 19200000:
  				lppllctl &= ~TWL6040_LPLLFIN;
  				break;
  			default:
  				dev_err(twl6040->dev,
  					"freq_out %d not supported
  ",
  					freq_out);
  				ret = -EINVAL;
  				goto pll_out;
  			}
  			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
  					  lppllctl);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
398
  		}
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
399
400
401
402
  
  		/* The PLL in use has not been change, we can exit */
  		if (twl6040->pll == pll_id)
  			break;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  
  		switch (freq_in) {
  		case 32768:
  			lppllctl |= TWL6040_LPLLENA;
  			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
  					  lppllctl);
  			mdelay(5);
  			lppllctl &= ~TWL6040_HPLLSEL;
  			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
  					  lppllctl);
  			hppllctl &= ~TWL6040_HPLLENA;
  			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
  					  hppllctl);
  			break;
  		default:
2d7c957e2   Peter Ujfalusi   MFD: twl6040: Rem...
418
  			dev_err(twl6040->dev,
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
419
420
421
422
423
  				"freq_in %d not supported
  ", freq_in);
  			ret = -EINVAL;
  			goto pll_out;
  		}
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
424
425
  
  		clk_disable_unprepare(twl6040->mclk);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
426
  		break;
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
427
  	case TWL6040_SYSCLK_SEL_HPPLL:
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
428
429
  		/* high-performance PLL can provide only 19.2 MHz */
  		if (freq_out != 19200000) {
2d7c957e2   Peter Ujfalusi   MFD: twl6040: Rem...
430
  			dev_err(twl6040->dev,
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
431
432
433
434
435
  				"freq_out %d not supported
  ", freq_out);
  			ret = -EINVAL;
  			goto pll_out;
  		}
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
436
  		if (twl6040->mclk_rate != freq_in) {
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
437
438
439
440
441
442
443
444
445
  			hppllctl &= ~TWL6040_MCLK_MSK;
  
  			switch (freq_in) {
  			case 12000000:
  				/* PLL enabled, active mode */
  				hppllctl |= TWL6040_MCLK_12000KHZ |
  					    TWL6040_HPLLENA;
  				break;
  			case 19200000:
ac8320c47   Peter Ujfalusi   mfd: twl6040: Cor...
446
447
448
  				/* PLL enabled, bypass mode */
  				hppllctl |= TWL6040_MCLK_19200KHZ |
  					    TWL6040_HPLLBP | TWL6040_HPLLENA;
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
449
450
451
452
453
454
455
  				break;
  			case 26000000:
  				/* PLL enabled, active mode */
  				hppllctl |= TWL6040_MCLK_26000KHZ |
  					    TWL6040_HPLLENA;
  				break;
  			case 38400000:
ac8320c47   Peter Ujfalusi   mfd: twl6040: Cor...
456
  				/* PLL enabled, bypass mode */
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
457
  				hppllctl |= TWL6040_MCLK_38400KHZ |
ac8320c47   Peter Ujfalusi   mfd: twl6040: Cor...
458
  					    TWL6040_HPLLBP | TWL6040_HPLLENA;
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
459
460
461
462
463
464
465
466
  				break;
  			default:
  				dev_err(twl6040->dev,
  					"freq_in %d not supported
  ", freq_in);
  				ret = -EINVAL;
  				goto pll_out;
  			}
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
467

0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
468
469
470
  			/* When switching to HPPLL, enable the mclk first */
  			if (pll_id != twl6040->pll)
  				clk_prepare_enable(twl6040->mclk);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
471
  			/*
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
472
473
  			 * enable clock slicer to ensure input waveform is
  			 * square
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
474
  			 */
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
475
  			hppllctl |= TWL6040_HPLLSQRENA;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
476

2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
477
478
479
480
481
482
483
484
485
  			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
  					  hppllctl);
  			usleep_range(500, 700);
  			lppllctl |= TWL6040_HPLLSEL;
  			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
  					  lppllctl);
  			lppllctl &= ~TWL6040_LPLLENA;
  			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
  					  lppllctl);
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
486
487
  
  			twl6040->mclk_rate = freq_in;
2bd05db71   Peter Ujfalusi   mfd: Avoid twl604...
488
  		}
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
489
490
  		break;
  	default:
2d7c957e2   Peter Ujfalusi   MFD: twl6040: Rem...
491
492
  		dev_err(twl6040->dev, "unknown pll id %d
  ", pll_id);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
493
494
495
  		ret = -EINVAL;
  		goto pll_out;
  	}
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
496
  	twl6040->sysclk_rate = freq_out;
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
497
  	twl6040->pll = pll_id;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
498
499
500
501
502
503
  
  pll_out:
  	mutex_unlock(&twl6040->mutex);
  	return ret;
  }
  EXPORT_SYMBOL(twl6040_set_pll);
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
504
  int twl6040_get_pll(struct twl6040 *twl6040)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
505
  {
cfb7a33be   Peter Ujfalusi   MFD: twl6040: Rem...
506
507
508
509
  	if (twl6040->power_count)
  		return twl6040->pll;
  	else
  		return -ENODEV;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
510
511
512
513
514
  }
  EXPORT_SYMBOL(twl6040_get_pll);
  
  unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
  {
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
515
  	return twl6040->sysclk_rate;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
516
517
  }
  EXPORT_SYMBOL(twl6040_get_sysclk);
70601ec10   Peter Ujfalusi   MFD: twl6040: fun...
518
519
520
  /* Get the combined status of the vibra control register */
  int twl6040_get_vibralr_status(struct twl6040 *twl6040)
  {
c6f39257c   Mark Brown   mfd: twl6040: Use...
521
522
  	unsigned int reg;
  	int ret;
70601ec10   Peter Ujfalusi   MFD: twl6040: fun...
523
  	u8 status;
c6f39257c   Mark Brown   mfd: twl6040: Use...
524
525
526
527
528
529
530
531
532
  	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, &reg);
  	if (ret != 0)
  		return ret;
  	status = reg;
  
  	ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, &reg);
  	if (ret != 0)
  		return ret;
  	status |= reg;
70601ec10   Peter Ujfalusi   MFD: twl6040: fun...
533
534
535
536
537
  	status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
  
  	return status;
  }
  EXPORT_SYMBOL(twl6040_get_vibralr_status);
0f962ae2d   Peter Ujfalusi   MFD: twl6040: Use...
538
539
540
541
542
543
544
545
546
547
548
  static struct resource twl6040_vibra_rsrc[] = {
  	{
  		.flags = IORESOURCE_IRQ,
  	},
  };
  
  static struct resource twl6040_codec_rsrc[] = {
  	{
  		.flags = IORESOURCE_IRQ,
  	},
  };
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
549
  static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
550
  {
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
551
552
553
554
555
  	/* Register 0 is not readable */
  	if (!reg)
  		return false;
  	return true;
  }
c6f39257c   Mark Brown   mfd: twl6040: Use...
556
557
558
  static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  	case TWL6040_REG_ASICID:
  	case TWL6040_REG_ASICREV:
  	case TWL6040_REG_INTID:
  	case TWL6040_REG_LPPLLCTL:
  	case TWL6040_REG_HPPLLCTL:
  	case TWL6040_REG_STATUS:
  		return true;
  	default:
  		return false;
  	}
  }
  
  static bool twl6040_writeable_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case TWL6040_REG_ASICID:
  	case TWL6040_REG_ASICREV:
  	case TWL6040_REG_STATUS:
c6f39257c   Mark Brown   mfd: twl6040: Use...
577
578
579
580
581
  		return false;
  	default:
  		return true;
  	}
  }
de1e23f83   Krzysztof Kozlowski   mfd: twl6040: Con...
582
  static const struct regmap_config twl6040_regmap_config = {
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
583
584
  	.reg_bits = 8,
  	.val_bits = 8,
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
585
586
587
  
  	.reg_defaults = twl6040_defaults,
  	.num_reg_defaults = ARRAY_SIZE(twl6040_defaults),
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
588
589
590
  	.max_register = TWL6040_REG_STATUS, /* 0x2e */
  
  	.readable_reg = twl6040_readable_reg,
c6f39257c   Mark Brown   mfd: twl6040: Use...
591
  	.volatile_reg = twl6040_volatile_reg,
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
592
  	.writeable_reg = twl6040_writeable_reg,
c6f39257c   Mark Brown   mfd: twl6040: Use...
593
594
  
  	.cache_type = REGCACHE_RBTREE,
1c96a2f67   David Frey   regmap: split up ...
595
596
  	.use_single_read = true,
  	.use_single_write = true,
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
597
  };
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
  static const struct regmap_irq twl6040_irqs[] = {
  	{ .reg_offset = 0, .mask = TWL6040_THINT, },
  	{ .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, },
  	{ .reg_offset = 0, .mask = TWL6040_HOOKINT, },
  	{ .reg_offset = 0, .mask = TWL6040_HFINT, },
  	{ .reg_offset = 0, .mask = TWL6040_VIBINT, },
  	{ .reg_offset = 0, .mask = TWL6040_READYINT, },
  };
  
  static struct regmap_irq_chip twl6040_irq_chip = {
  	.name = "twl6040",
  	.irqs = twl6040_irqs,
  	.num_irqs = ARRAY_SIZE(twl6040_irqs),
  
  	.num_regs = 1,
  	.status_base = TWL6040_REG_INTID,
  	.mask_base = TWL6040_REG_INTMR,
  };
612b95cd7   Greg Kroah-Hartman   Drivers: mfd: rem...
616
617
  static int twl6040_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
618
  {
37e13ceca   Peter Ujfalusi   mfd: Add support ...
619
  	struct device_node *node = client->dev.of_node;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
620
621
  	struct twl6040 *twl6040;
  	struct mfd_cell *cell = NULL;
1f01d60e4   Peter Ujfalusi   mfd: Register the...
622
  	int irq, ret, children = 0;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
623

df04b6242   Peter Ujfalusi   mfd: twl6040: Rem...
624
625
626
  	if (!node) {
  		dev_err(&client->dev, "of node is missing
  ");
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
627
628
  		return -EINVAL;
  	}
d20e1d21f   Peter Ujfalusi   MFD: twl6040: Dem...
629
  	/* In order to operate correctly we need valid interrupt config */
6712419d6   Peter Ujfalusi   mfd: Allocate twl...
630
  	if (!client->irq) {
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
631
632
  		dev_err(&client->dev, "Invalid IRQ configuration
  ");
d20e1d21f   Peter Ujfalusi   MFD: twl6040: Dem...
633
634
  		return -EINVAL;
  	}
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
635
636
  	twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
  			       GFP_KERNEL);
ecc8fa1c8   Peter Ujfalusi   mfd: twl6040: Cle...
637
638
  	if (!twl6040)
  		return -ENOMEM;
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
639

bbf6adc10   Axel Lin   mfd: Convert twl6...
640
  	twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
ecc8fa1c8   Peter Ujfalusi   mfd: twl6040: Cle...
641
642
  	if (IS_ERR(twl6040->regmap))
  		return PTR_ERR(twl6040->regmap);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
643

8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
644
  	i2c_set_clientdata(client, twl6040);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
645

68bab8662   Peter Ujfalusi   mfd: twl6040: Opt...
646
647
  	twl6040->clk32k = devm_clk_get(&client->dev, "clk32k");
  	if (IS_ERR(twl6040->clk32k)) {
75c08f17e   Tony Lindgren   mfd: twl6040: Fix...
648
649
  		if (PTR_ERR(twl6040->clk32k) == -EPROBE_DEFER)
  			return -EPROBE_DEFER;
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
650
651
  		dev_dbg(&client->dev, "clk32k is not handled
  ");
68bab8662   Peter Ujfalusi   mfd: twl6040: Opt...
652
653
  		twl6040->clk32k = NULL;
  	}
0a58da1e2   Peter Ujfalusi   mfd: twl6040: Han...
654
655
656
657
658
659
660
661
  	twl6040->mclk = devm_clk_get(&client->dev, "mclk");
  	if (IS_ERR(twl6040->mclk)) {
  		if (PTR_ERR(twl6040->mclk) == -EPROBE_DEFER)
  			return -EPROBE_DEFER;
  		dev_dbg(&client->dev, "mclk is not handled
  ");
  		twl6040->mclk = NULL;
  	}
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
662
663
  	twl6040->supplies[0].supply = "vio";
  	twl6040->supplies[1].supply = "v2v1";
990810b03   Jingoo Han   mfd: twl6040: Use...
664
  	ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
665
  				      twl6040->supplies);
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
666
667
668
  	if (ret != 0) {
  		dev_err(&client->dev, "Failed to get supplies: %d
  ", ret);
501d609ab   Wolfram Sang   mfd: twl6040: Rem...
669
  		return ret;
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
670
671
672
673
674
675
  	}
  
  	ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
  	if (ret != 0) {
  		dev_err(&client->dev, "Failed to enable supplies: %d
  ", ret);
501d609ab   Wolfram Sang   mfd: twl6040: Rem...
676
  		return ret;
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
677
  	}
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
678
679
  	twl6040->dev = &client->dev;
  	twl6040->irq = client->irq;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
680
681
  
  	mutex_init(&twl6040->mutex);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
682
  	init_completion(&twl6040->ready);
006cea3ae   Peter Ujfalusi   mfd: twl6040: Mov...
683
684
  	regmap_register_patch(twl6040->regmap, twl6040_patch,
  			      ARRAY_SIZE(twl6040_patch));
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
685
  	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
89d689983   Florian Vaussard   mfd: twl6040: Che...
686
687
688
689
  	if (twl6040->rev < 0) {
  		dev_err(&client->dev, "Failed to read revision register: %d
  ",
  			twl6040->rev);
f2b867816   Julia Lawall   mfd: twl6040: Fix...
690
  		ret = twl6040->rev;
89d689983   Florian Vaussard   mfd: twl6040: Che...
691
692
  		goto gpio_err;
  	}
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
693

77f63e06c   Peter Ujfalusi   MFD: twl6040: Fix...
694
  	/* ERRATA: Automatic power-up is not possible in ES1.0 */
df04b6242   Peter Ujfalusi   mfd: twl6040: Rem...
695
696
697
698
  	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
  		twl6040->audpwron = of_get_named_gpio(node,
  						      "ti,audpwron-gpio", 0);
  	else
77f63e06c   Peter Ujfalusi   MFD: twl6040: Fix...
699
  		twl6040->audpwron = -EINVAL;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
700
  	if (gpio_is_valid(twl6040->audpwron)) {
990810b03   Jingoo Han   mfd: twl6040: Use...
701
  		ret = devm_gpio_request_one(&client->dev, twl6040->audpwron,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
702
  					    GPIOF_OUT_INIT_LOW, "audpwron");
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
703
  		if (ret)
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
704
  			goto gpio_err;
02d02728e   Peter Ujfalusi   mfd: twl6040: Cle...
705
706
707
  
  		/* Clear any pending interrupt */
  		twl6040_reg_read(twl6040, TWL6040_REG_INTID);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
708
  	}
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
709
  	ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
c06f308aa   Lee Jones   mfd: twl6040: Fix...
710
  				  0, &twl6040_irq_chip, &twl6040->irq_data);
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
711
  	if (ret < 0)
990810b03   Jingoo Han   mfd: twl6040: Use...
712
  		goto gpio_err;
d20e1d21f   Peter Ujfalusi   MFD: twl6040: Dem...
713

ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
714
  	twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
715
  						 TWL6040_IRQ_READY);
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
716
  	twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
717
  					      TWL6040_IRQ_TH);
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
718

990810b03   Jingoo Han   mfd: twl6040: Use...
719
  	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
720
721
  					twl6040_readyint_handler, IRQF_ONESHOT,
  					"twl6040_irq_ready", twl6040);
d20e1d21f   Peter Ujfalusi   MFD: twl6040: Dem...
722
  	if (ret) {
1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
723
724
725
726
  		dev_err(twl6040->dev, "READY IRQ request failed: %d
  ", ret);
  		goto readyirq_err;
  	}
990810b03   Jingoo Han   mfd: twl6040: Use...
727
  	ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,
37aefe9f0   Peter Ujfalusi   mfd: twl6040: Cos...
728
729
  					twl6040_thint_handler, IRQF_ONESHOT,
  					"twl6040_irq_th", twl6040);
1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
730
731
732
  	if (ret) {
  		dev_err(twl6040->dev, "Thermal IRQ request failed: %d
  ", ret);
fc5ee96fe   Wei Yongjun   mfd: twl6040: Dro...
733
  		goto readyirq_err;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
734
  	}
1f01d60e4   Peter Ujfalusi   mfd: Register the...
735
736
737
  	/*
  	 * The main functionality of twl6040 to provide audio on OMAP4+ systems.
  	 * We can add the ASoC codec child whenever this driver has been loaded.
1f01d60e4   Peter Ujfalusi   mfd: Register the...
738
  	 */
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
739
  	irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
1f01d60e4   Peter Ujfalusi   mfd: Register the...
740
741
742
743
744
745
  	cell = &twl6040->cells[children];
  	cell->name = "twl6040-codec";
  	twl6040_codec_rsrc[0].start = irq;
  	twl6040_codec_rsrc[0].end = irq;
  	cell->resources = twl6040_codec_rsrc;
  	cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
1f01d60e4   Peter Ujfalusi   mfd: Register the...
746
  	children++;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
747

df04b6242   Peter Ujfalusi   mfd: twl6040: Rem...
748
749
  	/* Vibra input driver support */
  	if (twl6040_has_vibra(node)) {
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
750
  		irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
0f962ae2d   Peter Ujfalusi   MFD: twl6040: Use...
751

f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
752
753
  		cell = &twl6040->cells[children];
  		cell->name = "twl6040-vibra";
0f962ae2d   Peter Ujfalusi   MFD: twl6040: Use...
754
755
756
757
  		twl6040_vibra_rsrc[0].start = irq;
  		twl6040_vibra_rsrc[0].end = irq;
  		cell->resources = twl6040_vibra_rsrc;
  		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
758
759
  		children++;
  	}
df04b6242   Peter Ujfalusi   mfd: twl6040: Rem...
760
761
762
763
  	/* GPO support */
  	cell = &twl6040->cells[children];
  	cell->name = "twl6040-gpo";
  	children++;
5cbe786a6   Peter Ujfalusi   mfd: twl6040: Add...
764

0133d3234   Peter Ujfalusi   mfd: twl6040: Reg...
765
766
767
768
  	/* PDM clock support  */
  	cell = &twl6040->cells[children];
  	cell->name = "twl6040-pdmclk";
  	children++;
c7f9129d2   Peter Ujfalusi   mfd: twl6040: reg...
769
770
771
  	/* The chip is powered down so mark regmap to cache only and dirty */
  	regcache_cache_only(twl6040->regmap, true);
  	regcache_mark_dirty(twl6040->regmap);
1f01d60e4   Peter Ujfalusi   mfd: Register the...
772
  	ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
55692af5e   Mark Brown   mfd: core: Push i...
773
  			      NULL, 0, NULL);
1f01d60e4   Peter Ujfalusi   mfd: Register the...
774
  	if (ret)
fc5ee96fe   Wei Yongjun   mfd: twl6040: Dro...
775
  		goto readyirq_err;
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
776
777
  
  	return 0;
1ac96265a   Peter Ujfalusi   mfd: twl6040: Cor...
778
  readyirq_err:
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
779
  	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
780
781
  gpio_err:
  	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
782
783
  	return ret;
  }
612b95cd7   Greg Kroah-Hartman   Drivers: mfd: rem...
784
  static int twl6040_remove(struct i2c_client *client)
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
785
  {
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
786
  	struct twl6040 *twl6040 = i2c_get_clientdata(client);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
787
788
789
  
  	if (twl6040->power_count)
  		twl6040_power(twl6040, 0);
ab7edb149   Peter Ujfalusi   mfd: twl6040: Con...
790
  	regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
791

8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
792
  	mfd_remove_devices(&client->dev);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
793

5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
794
  	regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
5af7df6b8   Peter Ujfalusi   mfd: Add regulato...
795

f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
796
797
  	return 0;
  }
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
798
799
  static const struct i2c_device_id twl6040_i2c_id[] = {
  	{ "twl6040", 0, },
1fc74aef0   Peter Ujfalusi   mfd: Add support ...
800
  	{ "twl6041", 0, },
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
801
802
803
804
805
806
807
  	{ },
  };
  MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
  
  static struct i2c_driver twl6040_driver = {
  	.driver = {
  		.name = "twl6040",
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
808
  	},
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
809
  	.probe		= twl6040_probe,
612b95cd7   Greg Kroah-Hartman   Drivers: mfd: rem...
810
  	.remove		= twl6040_remove,
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
811
  	.id_table	= twl6040_i2c_id,
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
812
  };
8eaeb9393   Peter Ujfalusi   mfd: Convert twl6...
813
  module_i2c_driver(twl6040_driver);
f19b2823f   Misael Lopez Cruz   mfd: twl6040: Add...
814
815
816
817
818
  
  MODULE_DESCRIPTION("TWL6040 MFD");
  MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
  MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
  MODULE_LICENSE("GPL");