Blame view

drivers/mfd/wm8350-core.c 11.3 KB
89b4012be   Mark Brown   mfd: Core support...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * wm8350-core.c  --  Device access for Wolfson WM8350
   *
   * Copyright 2007, 2008 Wolfson Microelectronics PLC.
   *
   * Author: Liam Girdwood, Mark Brown
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
18
  #include <linux/bug.h>
89b4012be   Mark Brown   mfd: Core support...
19
20
21
  #include <linux/device.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
b7b142d9f   Mark Brown   mfd: Convert wm83...
22
  #include <linux/regmap.h>
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
23
  #include <linux/workqueue.h>
89b4012be   Mark Brown   mfd: Core support...
24
25
26
  
  #include <linux/mfd/wm8350/core.h>
  #include <linux/mfd/wm8350/audio.h>
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
27
  #include <linux/mfd/wm8350/comparator.h>
89b4012be   Mark Brown   mfd: Core support...
28
29
  #include <linux/mfd/wm8350/gpio.h>
  #include <linux/mfd/wm8350/pmic.h>
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
30
  #include <linux/mfd/wm8350/rtc.h>
89b4012be   Mark Brown   mfd: Core support...
31
  #include <linux/mfd/wm8350/supply.h>
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
32
  #include <linux/mfd/wm8350/wdt.h>
89b4012be   Mark Brown   mfd: Core support...
33

89b4012be   Mark Brown   mfd: Core support...
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
  #define WM8350_CLOCK_CONTROL_1		0x28
  #define WM8350_AIF_TEST			0x74
  
  /* debug */
  #define WM8350_BUS_DEBUG 0
  #if WM8350_BUS_DEBUG
  #define dump(regs, src) do { \
  	int i_; \
  	u16 *src_ = src; \
  	printk(KERN_DEBUG); \
  	for (i_ = 0; i_ < regs; i_++) \
  		printk(" 0x%4.4x", *src_++); \
  	printk("
  "); \
  } while (0);
  #else
  #define dump(bytes, src)
  #endif
  
  #define WM8350_LOCK_DEBUG 0
  #if WM8350_LOCK_DEBUG
  #define ldbg(format, arg...) printk(format, ## arg)
  #else
  #define ldbg(format, arg...)
  #endif
  
  /*
   * WM8350 Device IO
   */
89b4012be   Mark Brown   mfd: Core support...
63
  static DEFINE_MUTEX(reg_lock_mutex);
89b4012be   Mark Brown   mfd: Core support...
64

89b4012be   Mark Brown   mfd: Core support...
65
66
67
68
69
  /*
   * Safe read, modify, write methods
   */
  int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
  {
19d57ed5a   Mark Brown   mfd: Remove custo...
70
  	return regmap_update_bits(wm8350->regmap, reg, mask, 0);
89b4012be   Mark Brown   mfd: Core support...
71
72
73
74
75
  }
  EXPORT_SYMBOL_GPL(wm8350_clear_bits);
  
  int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
  {
19d57ed5a   Mark Brown   mfd: Remove custo...
76
  	return regmap_update_bits(wm8350->regmap, reg, mask, mask);
89b4012be   Mark Brown   mfd: Core support...
77
78
79
80
81
  }
  EXPORT_SYMBOL_GPL(wm8350_set_bits);
  
  u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
  {
19d57ed5a   Mark Brown   mfd: Remove custo...
82
  	unsigned int data;
89b4012be   Mark Brown   mfd: Core support...
83
  	int err;
19d57ed5a   Mark Brown   mfd: Remove custo...
84
  	err = regmap_read(wm8350->regmap, reg, &data);
89b4012be   Mark Brown   mfd: Core support...
85
86
87
  	if (err)
  		dev_err(wm8350->dev, "read from reg R%d failed
  ", reg);
89b4012be   Mark Brown   mfd: Core support...
88
89
90
91
92
93
94
  	return data;
  }
  EXPORT_SYMBOL_GPL(wm8350_reg_read);
  
  int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
  {
  	int ret;
89b4012be   Mark Brown   mfd: Core support...
95

19d57ed5a   Mark Brown   mfd: Remove custo...
96
  	ret = regmap_write(wm8350->regmap, reg, val);
89b4012be   Mark Brown   mfd: Core support...
97
98
99
  	if (ret)
  		dev_err(wm8350->dev, "write to reg R%d failed
  ", reg);
89b4012be   Mark Brown   mfd: Core support...
100
101
102
103
104
105
106
107
  	return ret;
  }
  EXPORT_SYMBOL_GPL(wm8350_reg_write);
  
  int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
  		      u16 *dest)
  {
  	int err = 0;
19d57ed5a   Mark Brown   mfd: Remove custo...
108
  	err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs);
89b4012be   Mark Brown   mfd: Core support...
109
110
111
112
  	if (err)
  		dev_err(wm8350->dev, "block read starting from R%d failed
  ",
  			start_reg);
19d57ed5a   Mark Brown   mfd: Remove custo...
113

89b4012be   Mark Brown   mfd: Core support...
114
115
116
117
118
119
120
121
  	return err;
  }
  EXPORT_SYMBOL_GPL(wm8350_block_read);
  
  int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
  		       u16 *src)
  {
  	int ret = 0;
19d57ed5a   Mark Brown   mfd: Remove custo...
122
  	ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs);
89b4012be   Mark Brown   mfd: Core support...
123
124
125
126
  	if (ret)
  		dev_err(wm8350->dev, "block write starting at R%d failed
  ",
  			start_reg);
19d57ed5a   Mark Brown   mfd: Remove custo...
127

89b4012be   Mark Brown   mfd: Core support...
128
129
130
  	return ret;
  }
  EXPORT_SYMBOL_GPL(wm8350_block_write);
858e67446   Mark Brown   mfd: Add some doc...
131
132
133
134
135
136
137
  /**
   * wm8350_reg_lock()
   *
   * The WM8350 has a hardware lock which can be used to prevent writes to
   * some registers (generally those which can cause particularly serious
   * problems if misused).  This function enables that lock.
   */
89b4012be   Mark Brown   mfd: Core support...
138
139
  int wm8350_reg_lock(struct wm8350 *wm8350)
  {
89b4012be   Mark Brown   mfd: Core support...
140
  	int ret;
52b461b86   Mark Brown   mfd: Add regmap c...
141
  	mutex_lock(&reg_lock_mutex);
89b4012be   Mark Brown   mfd: Core support...
142
  	ldbg(__func__);
52b461b86   Mark Brown   mfd: Add regmap c...
143
144
  
  	ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY);
89b4012be   Mark Brown   mfd: Core support...
145
146
147
  	if (ret)
  		dev_err(wm8350->dev, "lock failed
  ");
52b461b86   Mark Brown   mfd: Add regmap c...
148
149
150
151
  
  	wm8350->unlocked = false;
  
  	mutex_unlock(&reg_lock_mutex);
89b4012be   Mark Brown   mfd: Core support...
152
153
154
  	return ret;
  }
  EXPORT_SYMBOL_GPL(wm8350_reg_lock);
858e67446   Mark Brown   mfd: Add some doc...
155
156
157
158
159
160
161
162
163
  /**
   * wm8350_reg_unlock()
   *
   * The WM8350 has a hardware lock which can be used to prevent writes to
   * some registers (generally those which can cause particularly serious
   * problems if misused).  This function disables that lock so updates
   * can be performed.  For maximum safety this should be done only when
   * required.
   */
89b4012be   Mark Brown   mfd: Core support...
164
165
  int wm8350_reg_unlock(struct wm8350 *wm8350)
  {
89b4012be   Mark Brown   mfd: Core support...
166
  	int ret;
52b461b86   Mark Brown   mfd: Add regmap c...
167
  	mutex_lock(&reg_lock_mutex);
89b4012be   Mark Brown   mfd: Core support...
168
  	ldbg(__func__);
52b461b86   Mark Brown   mfd: Add regmap c...
169
170
  
  	ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY);
89b4012be   Mark Brown   mfd: Core support...
171
172
173
  	if (ret)
  		dev_err(wm8350->dev, "unlock failed
  ");
52b461b86   Mark Brown   mfd: Add regmap c...
174
175
176
177
  
  	wm8350->unlocked = true;
  
  	mutex_unlock(&reg_lock_mutex);
89b4012be   Mark Brown   mfd: Core support...
178
179
180
  	return ret;
  }
  EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
674885263   Mark Brown   mfd: Add AUXADC s...
181
182
183
  int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
  {
  	u16 reg, result = 0;
674885263   Mark Brown   mfd: Add AUXADC s...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  
  	if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
  		return -EINVAL;
  	if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP
  	    && (scale != 0 || vref != 0))
  		return -EINVAL;
  
  	mutex_lock(&wm8350->auxadc_mutex);
  
  	/* Turn on the ADC */
  	reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
  	wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA);
  
  	if (scale || vref) {
  		reg = scale << 13;
  		reg |= vref << 12;
  		wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg);
  	}
  
  	reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
  	reg |= 1 << channel | WM8350_AUXADC_POLL;
  	wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
5051d411e   Mark Brown   mfd: Clean up aft...
206
207
208
  	/* If a late IRQ left the completion signalled then consume
  	 * the completion. */
  	try_wait_for_completion(&wm8350->auxadc_done);
d19663ac6   Mark Brown   mfd: Use completi...
209
210
211
212
  	/* We ignore the result of the completion and just check for a
  	 * conversion result, allowing us to soldier on if the IRQ
  	 * infrastructure is not set up for the chip. */
  	wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5));
674885263   Mark Brown   mfd: Add AUXADC s...
213

d19663ac6   Mark Brown   mfd: Use completi...
214
215
  	reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
  	if (reg & WM8350_AUXADC_POLL)
674885263   Mark Brown   mfd: Add AUXADC s...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  		dev_err(wm8350->dev, "adc chn %d read timeout
  ", channel);
  	else
  		result = wm8350_reg_read(wm8350,
  					 WM8350_AUX1_READBACK + channel);
  
  	/* Turn off the ADC */
  	reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
  	wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5,
  			 reg & ~WM8350_AUXADC_ENA);
  
  	mutex_unlock(&wm8350->auxadc_mutex);
  
  	return result & WM8350_AUXADC_DATA1_MASK;
  }
  EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
d19663ac6   Mark Brown   mfd: Use completi...
232
233
234
235
236
237
238
239
  static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
  {
  	struct wm8350 *wm8350 = irq_data;
  
  	complete(&wm8350->auxadc_done);
  
  	return IRQ_HANDLED;
  }
89b4012be   Mark Brown   mfd: Core support...
240
  /*
9201d38b9   Mark Brown   mfd: Add WM8350 s...
241
242
243
244
245
246
247
248
249
250
   * Register a client device.  This is non-fatal since there is no need to
   * fail the entire device init due to a single platform device failing.
   */
  static void wm8350_client_dev_register(struct wm8350 *wm8350,
  				       const char *name,
  				       struct platform_device **pdev)
  {
  	int ret;
  
  	*pdev = platform_device_alloc(name, -1);
cb9b22450   Dan Carpenter   mfd: Fix incorrec...
251
  	if (*pdev == NULL) {
9201d38b9   Mark Brown   mfd: Add WM8350 s...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  		dev_err(wm8350->dev, "Failed to allocate %s
  ", name);
  		return;
  	}
  
  	(*pdev)->dev.parent = wm8350->dev;
  	platform_set_drvdata(*pdev, wm8350);
  	ret = platform_device_add(*pdev);
  	if (ret != 0) {
  		dev_err(wm8350->dev, "Failed to register %s: %d
  ", name, ret);
  		platform_device_put(*pdev);
  		*pdev = NULL;
  	}
  }
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
267
  int wm8350_device_init(struct wm8350 *wm8350, int irq,
bcdd4efc1   Mark Brown   mfd: Add initiali...
268
  		       struct wm8350_platform_data *pdata)
89b4012be   Mark Brown   mfd: Core support...
269
  {
85c93ea7d   Mark Brown   mfd: Improve diag...
270
  	int ret;
b7b142d9f   Mark Brown   mfd: Convert wm83...
271
272
  	unsigned int id1, id2, mask_rev;
  	unsigned int cust_id, mode, chip_rev;
89b4012be   Mark Brown   mfd: Core support...
273

18bf50a37   Mark Brown   mfd: Store wm8350...
274
  	dev_set_drvdata(wm8350->dev, wm8350);
89b4012be   Mark Brown   mfd: Core support...
275
  	/* get WM8350 revision and config mode */
b7b142d9f   Mark Brown   mfd: Convert wm83...
276
  	ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1);
85c93ea7d   Mark Brown   mfd: Improve diag...
277
278
279
280
281
  	if (ret != 0) {
  		dev_err(wm8350->dev, "Failed to read ID: %d
  ", ret);
  		goto err;
  	}
b7b142d9f   Mark Brown   mfd: Convert wm83...
282
  	ret = regmap_read(wm8350->regmap, WM8350_ID, &id2);
85c93ea7d   Mark Brown   mfd: Improve diag...
283
284
285
286
287
  	if (ret != 0) {
  		dev_err(wm8350->dev, "Failed to read ID: %d
  ", ret);
  		goto err;
  	}
b7b142d9f   Mark Brown   mfd: Convert wm83...
288
  	ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev);
85c93ea7d   Mark Brown   mfd: Improve diag...
289
290
291
292
293
  	if (ret != 0) {
  		dev_err(wm8350->dev, "Failed to read revision: %d
  ", ret);
  		goto err;
  	}
89b4012be   Mark Brown   mfd: Core support...
294

b797a5551   Mark Brown   mfd: Refactor WM8...
295
296
297
298
299
300
301
  	if (id1 != 0x6143) {
  		dev_err(wm8350->dev,
  			"Device with ID %x is not a WM8350
  ", id1);
  		ret = -ENODEV;
  		goto err;
  	}
1753b40f5   Joe Perches   mfd: wm8350-core:...
302
  	mode = (id2 & WM8350_CONF_STS_MASK) >> 10;
b797a5551   Mark Brown   mfd: Refactor WM8...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  	cust_id = id2 & WM8350_CUST_ID_MASK;
  	chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12;
  	dev_info(wm8350->dev,
  		 "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d
  ",
  		 mode, cust_id, mask_rev, chip_rev);
  
  	if (cust_id != 0) {
  		dev_err(wm8350->dev, "Unsupported CUST_ID
  ");
  		ret = -ENODEV;
  		goto err;
  	}
  
  	switch (mask_rev) {
  	case 0:
645524a9c   Mark Brown   mfd: Support conf...
319
320
  		wm8350->pmic.max_dcdc = WM8350_DCDC_6;
  		wm8350->pmic.max_isink = WM8350_ISINK_B;
b797a5551   Mark Brown   mfd: Refactor WM8...
321
  		switch (chip_rev) {
89b4012be   Mark Brown   mfd: Core support...
322
  		case WM8350_REV_E:
b797a5551   Mark Brown   mfd: Refactor WM8...
323
324
  			dev_info(wm8350->dev, "WM8350 Rev E
  ");
89b4012be   Mark Brown   mfd: Core support...
325
326
  			break;
  		case WM8350_REV_F:
b797a5551   Mark Brown   mfd: Refactor WM8...
327
328
  			dev_info(wm8350->dev, "WM8350 Rev F
  ");
89b4012be   Mark Brown   mfd: Core support...
329
330
  			break;
  		case WM8350_REV_G:
b797a5551   Mark Brown   mfd: Refactor WM8...
331
332
  			dev_info(wm8350->dev, "WM8350 Rev G
  ");
d756f4a44   Mark Brown   mfd: Switch WM835...
333
  			wm8350->power.rev_g_coeff = 1;
89b4012be   Mark Brown   mfd: Core support...
334
  			break;
0c8a60167   Mark Brown   mfd: Add WM8350 r...
335
  		case WM8350_REV_H:
b797a5551   Mark Brown   mfd: Refactor WM8...
336
337
  			dev_info(wm8350->dev, "WM8350 Rev H
  ");
d756f4a44   Mark Brown   mfd: Switch WM835...
338
  			wm8350->power.rev_g_coeff = 1;
0c8a60167   Mark Brown   mfd: Add WM8350 r...
339
  			break;
89b4012be   Mark Brown   mfd: Core support...
340
341
  		default:
  			/* For safety we refuse to run on unknown hardware */
b797a5551   Mark Brown   mfd: Refactor WM8...
342
343
  			dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV
  ");
89b4012be   Mark Brown   mfd: Core support...
344
345
346
  			ret = -ENODEV;
  			goto err;
  		}
b797a5551   Mark Brown   mfd: Refactor WM8...
347
  		break;
ca23f8c1b   Mark Brown   mfd: Add WM8351 s...
348
349
350
351
352
353
354
355
356
357
  	case 1:
  		wm8350->pmic.max_dcdc = WM8350_DCDC_4;
  		wm8350->pmic.max_isink = WM8350_ISINK_A;
  
  		switch (chip_rev) {
  		case 0:
  			dev_info(wm8350->dev, "WM8351 Rev A
  ");
  			wm8350->power.rev_g_coeff = 1;
  			break;
02d46e07e   Mark Brown   mfd: add support ...
358
359
360
361
362
  		case 1:
  			dev_info(wm8350->dev, "WM8351 Rev B
  ");
  			wm8350->power.rev_g_coeff = 1;
  			break;
ca23f8c1b   Mark Brown   mfd: Add WM8351 s...
363
364
365
366
367
368
369
  		default:
  			dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV
  ");
  			ret = -ENODEV;
  			goto err;
  		}
  		break;
969206306   Mark Brown   mfd: Add WM8352 s...
370
  	case 2:
645524a9c   Mark Brown   mfd: Support conf...
371
372
  		wm8350->pmic.max_dcdc = WM8350_DCDC_6;
  		wm8350->pmic.max_isink = WM8350_ISINK_B;
969206306   Mark Brown   mfd: Add WM8352 s...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  		switch (chip_rev) {
  		case 0:
  			dev_info(wm8350->dev, "WM8352 Rev A
  ");
  			wm8350->power.rev_g_coeff = 1;
  			break;
  
  		default:
  			dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV
  ");
  			ret = -ENODEV;
  			goto err;
  		}
  		break;
b797a5551   Mark Brown   mfd: Refactor WM8...
387
388
389
  	default:
  		dev_err(wm8350->dev, "Unknown MASK_REV
  ");
89b4012be   Mark Brown   mfd: Core support...
390
391
392
  		ret = -ENODEV;
  		goto err;
  	}
674885263   Mark Brown   mfd: Add AUXADC s...
393
  	mutex_init(&wm8350->auxadc_mutex);
d19663ac6   Mark Brown   mfd: Use completi...
394
  	init_completion(&wm8350->auxadc_done);
320645035   Mark Brown   mfd: Support acti...
395

e0a3389ab   Mark Brown   mfd: Split wm8350...
396
397
  	ret = wm8350_irq_init(wm8350, irq, pdata);
  	if (ret < 0)
19d57ed5a   Mark Brown   mfd: Remove custo...
398
  		goto err;
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
399

d19663ac6   Mark Brown   mfd: Use completi...
400
401
402
  	if (wm8350->irq_base) {
  		ret = request_threaded_irq(wm8350->irq_base +
  					   WM8350_IRQ_AUXADC_DATARDY,
b3c8c82b4   Fabio Estevam   mfd: wm8350-core:...
403
404
  					   NULL, wm8350_auxadc_irq,
  					   IRQF_ONESHOT,
d19663ac6   Mark Brown   mfd: Use completi...
405
406
407
408
409
410
  					   "auxadc", wm8350);
  		if (ret < 0)
  			dev_warn(wm8350->dev,
  				 "Failed to request AUXADC IRQ: %d
  ", ret);
  	}
62571c29a   Mark Brown   mfd: Initialise W...
411
412
413
414
415
416
  	if (pdata && pdata->init) {
  		ret = pdata->init(wm8350);
  		if (ret != 0) {
  			dev_err(wm8350->dev, "Platform init() failed: %d
  ",
  				ret);
e0a3389ab   Mark Brown   mfd: Split wm8350...
417
  			goto err_irq;
62571c29a   Mark Brown   mfd: Initialise W...
418
419
  		}
  	}
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
420
  	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
add41cb46   Mark Brown   mfd: Add placehol...
421
422
423
424
  	wm8350_client_dev_register(wm8350, "wm8350-codec",
  				   &(wm8350->codec.pdev));
  	wm8350_client_dev_register(wm8350, "wm8350-gpio",
  				   &(wm8350->gpio.pdev));
fb6c023a2   Mark Brown   hwmon: Add WM835x...
425
426
  	wm8350_client_dev_register(wm8350, "wm8350-hwmon",
  				   &(wm8350->hwmon.pdev));
add41cb46   Mark Brown   mfd: Add placehol...
427
428
429
430
  	wm8350_client_dev_register(wm8350, "wm8350-power",
  				   &(wm8350->power.pdev));
  	wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev));
  	wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev));
89b4012be   Mark Brown   mfd: Core support...
431
  	return 0;
e0a3389ab   Mark Brown   mfd: Split wm8350...
432
433
  err_irq:
  	wm8350_irq_exit(wm8350);
8c46cf30f   Axel Lin   mfd: Fix wrong wm...
434
  err:
89b4012be   Mark Brown   mfd: Core support...
435
436
437
438
439
440
  	return ret;
  }
  EXPORT_SYMBOL_GPL(wm8350_device_init);
  
  void wm8350_device_exit(struct wm8350 *wm8350)
  {
da09155ac   Mark Brown   regulator: Add WM...
441
  	int i;
0081e8020   Mark Brown   leds: Add WM8350 ...
442
443
  	for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
  		platform_device_unregister(wm8350->pmic.led[i].pdev);
da09155ac   Mark Brown   regulator: Add WM...
444
  	for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
add41cb46   Mark Brown   mfd: Add placehol...
445
446
447
448
449
  		platform_device_unregister(wm8350->pmic.pdev[i]);
  
  	platform_device_unregister(wm8350->wdt.pdev);
  	platform_device_unregister(wm8350->rtc.pdev);
  	platform_device_unregister(wm8350->power.pdev);
fb6c023a2   Mark Brown   hwmon: Add WM835x...
450
  	platform_device_unregister(wm8350->hwmon.pdev);
add41cb46   Mark Brown   mfd: Add placehol...
451
452
  	platform_device_unregister(wm8350->gpio.pdev);
  	platform_device_unregister(wm8350->codec.pdev);
da09155ac   Mark Brown   regulator: Add WM...
453

d19663ac6   Mark Brown   mfd: Use completi...
454
455
  	if (wm8350->irq_base)
  		free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
e0a3389ab   Mark Brown   mfd: Split wm8350...
456
  	wm8350_irq_exit(wm8350);
89b4012be   Mark Brown   mfd: Core support...
457
458
  }
  EXPORT_SYMBOL_GPL(wm8350_device_exit);
ebccec0fa   Mark Brown   mfd: Add WM8350 i...
459
  MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver");
89b4012be   Mark Brown   mfd: Core support...
460
  MODULE_LICENSE("GPL");