xref: /openbmc/linux/drivers/mfd/88pm860x-core.c (revision 21f1fc38)
1 /*
2  * Base driver for Marvell 88PM8607
3  *
4  * Copyright (C) 2009 Marvell International Ltd.
5  * 	Haojian Zhuang <haojian.zhuang@marvell.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/i2c.h>
15 #include <linux/irq.h>
16 #include <linux/interrupt.h>
17 #include <linux/platform_device.h>
18 #include <linux/mfd/core.h>
19 #include <linux/mfd/88pm860x.h>
20 
21 #define INT_STATUS_NUM			3
22 
23 char pm860x_backlight_name[][MFD_NAME_SIZE] = {
24 	"backlight-0",
25 	"backlight-1",
26 	"backlight-2",
27 };
28 EXPORT_SYMBOL(pm860x_backlight_name);
29 
30 char pm860x_led_name[][MFD_NAME_SIZE] = {
31 	"led0-red",
32 	"led0-green",
33 	"led0-blue",
34 	"led1-red",
35 	"led1-green",
36 	"led1-blue",
37 };
38 EXPORT_SYMBOL(pm860x_led_name);
39 
40 #define PM8606_BACKLIGHT_RESOURCE(_i, _x)		\
41 {							\
42 	.name	= pm860x_backlight_name[_i],		\
43 	.start	= PM8606_##_x,				\
44 	.end	= PM8606_##_x,				\
45 	.flags	= IORESOURCE_IO,			\
46 }
47 
48 static struct resource backlight_resources[] = {
49 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT1, WLED1A),
50 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT2, WLED2A),
51 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT3, WLED3A),
52 };
53 
54 #define PM8606_BACKLIGHT_DEVS(_i)			\
55 {							\
56 	.name		= "88pm860x-backlight",		\
57 	.num_resources	= 1,				\
58 	.resources	= &backlight_resources[_i],	\
59 	.id		= _i,				\
60 }
61 
62 static struct mfd_cell backlight_devs[] = {
63 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT1),
64 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT2),
65 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT3),
66 };
67 
68 #define PM8606_LED_RESOURCE(_i, _x)			\
69 {							\
70 	.name	= pm860x_led_name[_i],			\
71 	.start	= PM8606_##_x,				\
72 	.end	= PM8606_##_x,				\
73 	.flags	= IORESOURCE_IO,			\
74 }
75 
76 static struct resource led_resources[] = {
77 	PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
78 	PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
79 	PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
80 	PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
81 	PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
82 	PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
83 };
84 
85 #define PM8606_LED_DEVS(_i)				\
86 {							\
87 	.name		= "88pm860x-led",		\
88 	.num_resources	= 1,				\
89 	.resources	= &led_resources[_i],		\
90 	.id		= _i,				\
91 }
92 
93 static struct mfd_cell led_devs[] = {
94 	PM8606_LED_DEVS(PM8606_LED1_RED),
95 	PM8606_LED_DEVS(PM8606_LED1_GREEN),
96 	PM8606_LED_DEVS(PM8606_LED1_BLUE),
97 	PM8606_LED_DEVS(PM8606_LED2_RED),
98 	PM8606_LED_DEVS(PM8606_LED2_GREEN),
99 	PM8606_LED_DEVS(PM8606_LED2_BLUE),
100 };
101 
102 static struct resource touch_resources[] = {
103 	{
104 		.start	= PM8607_IRQ_PEN,
105 		.end	= PM8607_IRQ_PEN,
106 		.flags	= IORESOURCE_IRQ,
107 	},
108 };
109 
110 static struct mfd_cell touch_devs[] = {
111 	{
112 		.name		= "88pm860x-touch",
113 		.num_resources	= 1,
114 		.resources	= &touch_resources[0],
115 	},
116 };
117 
118 #define PM8607_REG_RESOURCE(_start, _end)		\
119 {							\
120 	.start	= PM8607_##_start,			\
121 	.end	= PM8607_##_end,			\
122 	.flags	= IORESOURCE_IO,			\
123 }
124 
125 static struct resource power_supply_resources[] = {
126 	{
127 		.name		= "88pm860x-power",
128 		.start		= PM8607_IRQ_CHG,
129 		.end		= PM8607_IRQ_CHG,
130 		.flags		= IORESOURCE_IRQ,
131 	},
132 };
133 
134 static struct mfd_cell power_devs[] = {
135 	{
136 		.name		= "88pm860x-power",
137 		.num_resources	= 1,
138 		.resources	= &power_supply_resources[0],
139 		.id		= -1,
140 	},
141 };
142 
143 static struct resource onkey_resources[] = {
144 	{
145 		.name		= "88pm860x-onkey",
146 		.start		= PM8607_IRQ_ONKEY,
147 		.end		= PM8607_IRQ_ONKEY,
148 		.flags		= IORESOURCE_IRQ,
149 	},
150 };
151 
152 static struct mfd_cell onkey_devs[] = {
153 	{
154 		.name		= "88pm860x-onkey",
155 		.num_resources	= 1,
156 		.resources	= &onkey_resources[0],
157 		.id		= -1,
158 	},
159 };
160 
161 static struct resource regulator_resources[] = {
162 	PM8607_REG_RESOURCE(BUCK1, BUCK1),
163 	PM8607_REG_RESOURCE(BUCK2, BUCK2),
164 	PM8607_REG_RESOURCE(BUCK3, BUCK3),
165 	PM8607_REG_RESOURCE(LDO1,  LDO1),
166 	PM8607_REG_RESOURCE(LDO2,  LDO2),
167 	PM8607_REG_RESOURCE(LDO3,  LDO3),
168 	PM8607_REG_RESOURCE(LDO4,  LDO4),
169 	PM8607_REG_RESOURCE(LDO5,  LDO5),
170 	PM8607_REG_RESOURCE(LDO6,  LDO6),
171 	PM8607_REG_RESOURCE(LDO7,  LDO7),
172 	PM8607_REG_RESOURCE(LDO8,  LDO8),
173 	PM8607_REG_RESOURCE(LDO9,  LDO9),
174 	PM8607_REG_RESOURCE(LDO10, LDO10),
175 	PM8607_REG_RESOURCE(LDO12, LDO12),
176 	PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET),
177 	PM8607_REG_RESOURCE(LDO14, LDO14),
178 };
179 
180 #define PM8607_REG_DEVS(_id)						\
181 {									\
182 	.name		= "88pm860x-regulator",				\
183 	.num_resources	= 1,						\
184 	.resources	= &regulator_resources[PM8607_ID_##_id],	\
185 	.id		= PM8607_ID_##_id,				\
186 }
187 
188 static struct mfd_cell regulator_devs[] = {
189 	PM8607_REG_DEVS(BUCK1),
190 	PM8607_REG_DEVS(BUCK2),
191 	PM8607_REG_DEVS(BUCK3),
192 	PM8607_REG_DEVS(LDO1),
193 	PM8607_REG_DEVS(LDO2),
194 	PM8607_REG_DEVS(LDO3),
195 	PM8607_REG_DEVS(LDO4),
196 	PM8607_REG_DEVS(LDO5),
197 	PM8607_REG_DEVS(LDO6),
198 	PM8607_REG_DEVS(LDO7),
199 	PM8607_REG_DEVS(LDO8),
200 	PM8607_REG_DEVS(LDO9),
201 	PM8607_REG_DEVS(LDO10),
202 	PM8607_REG_DEVS(LDO12),
203 	PM8607_REG_DEVS(LDO13),
204 	PM8607_REG_DEVS(LDO14),
205 };
206 
207 struct pm860x_irq_data {
208 	int	reg;
209 	int	mask_reg;
210 	int	enable;		/* enable or not */
211 	int	offs;		/* bit offset in mask register */
212 };
213 
214 static struct pm860x_irq_data pm860x_irqs[] = {
215 	[PM8607_IRQ_ONKEY] = {
216 		.reg		= PM8607_INT_STATUS1,
217 		.mask_reg	= PM8607_INT_MASK_1,
218 		.offs		= 1 << 0,
219 	},
220 	[PM8607_IRQ_EXTON] = {
221 		.reg		= PM8607_INT_STATUS1,
222 		.mask_reg	= PM8607_INT_MASK_1,
223 		.offs		= 1 << 1,
224 	},
225 	[PM8607_IRQ_CHG] = {
226 		.reg		= PM8607_INT_STATUS1,
227 		.mask_reg	= PM8607_INT_MASK_1,
228 		.offs		= 1 << 2,
229 	},
230 	[PM8607_IRQ_BAT] = {
231 		.reg		= PM8607_INT_STATUS1,
232 		.mask_reg	= PM8607_INT_MASK_1,
233 		.offs		= 1 << 3,
234 	},
235 	[PM8607_IRQ_RTC] = {
236 		.reg		= PM8607_INT_STATUS1,
237 		.mask_reg	= PM8607_INT_MASK_1,
238 		.offs		= 1 << 4,
239 	},
240 	[PM8607_IRQ_CC] = {
241 		.reg		= PM8607_INT_STATUS1,
242 		.mask_reg	= PM8607_INT_MASK_1,
243 		.offs		= 1 << 5,
244 	},
245 	[PM8607_IRQ_VBAT] = {
246 		.reg		= PM8607_INT_STATUS2,
247 		.mask_reg	= PM8607_INT_MASK_2,
248 		.offs		= 1 << 0,
249 	},
250 	[PM8607_IRQ_VCHG] = {
251 		.reg		= PM8607_INT_STATUS2,
252 		.mask_reg	= PM8607_INT_MASK_2,
253 		.offs		= 1 << 1,
254 	},
255 	[PM8607_IRQ_VSYS] = {
256 		.reg		= PM8607_INT_STATUS2,
257 		.mask_reg	= PM8607_INT_MASK_2,
258 		.offs		= 1 << 2,
259 	},
260 	[PM8607_IRQ_TINT] = {
261 		.reg		= PM8607_INT_STATUS2,
262 		.mask_reg	= PM8607_INT_MASK_2,
263 		.offs		= 1 << 3,
264 	},
265 	[PM8607_IRQ_GPADC0] = {
266 		.reg		= PM8607_INT_STATUS2,
267 		.mask_reg	= PM8607_INT_MASK_2,
268 		.offs		= 1 << 4,
269 	},
270 	[PM8607_IRQ_GPADC1] = {
271 		.reg		= PM8607_INT_STATUS2,
272 		.mask_reg	= PM8607_INT_MASK_2,
273 		.offs		= 1 << 5,
274 	},
275 	[PM8607_IRQ_GPADC2] = {
276 		.reg		= PM8607_INT_STATUS2,
277 		.mask_reg	= PM8607_INT_MASK_2,
278 		.offs		= 1 << 6,
279 	},
280 	[PM8607_IRQ_GPADC3] = {
281 		.reg		= PM8607_INT_STATUS2,
282 		.mask_reg	= PM8607_INT_MASK_2,
283 		.offs		= 1 << 7,
284 	},
285 	[PM8607_IRQ_AUDIO_SHORT] = {
286 		.reg		= PM8607_INT_STATUS3,
287 		.mask_reg	= PM8607_INT_MASK_3,
288 		.offs		= 1 << 0,
289 	},
290 	[PM8607_IRQ_PEN] = {
291 		.reg		= PM8607_INT_STATUS3,
292 		.mask_reg	= PM8607_INT_MASK_3,
293 		.offs		= 1 << 1,
294 	},
295 	[PM8607_IRQ_HEADSET] = {
296 		.reg		= PM8607_INT_STATUS3,
297 		.mask_reg	= PM8607_INT_MASK_3,
298 		.offs		= 1 << 2,
299 	},
300 	[PM8607_IRQ_HOOK] = {
301 		.reg		= PM8607_INT_STATUS3,
302 		.mask_reg	= PM8607_INT_MASK_3,
303 		.offs		= 1 << 3,
304 	},
305 	[PM8607_IRQ_MICIN] = {
306 		.reg		= PM8607_INT_STATUS3,
307 		.mask_reg	= PM8607_INT_MASK_3,
308 		.offs		= 1 << 4,
309 	},
310 	[PM8607_IRQ_CHG_FAIL] = {
311 		.reg		= PM8607_INT_STATUS3,
312 		.mask_reg	= PM8607_INT_MASK_3,
313 		.offs		= 1 << 5,
314 	},
315 	[PM8607_IRQ_CHG_DONE] = {
316 		.reg		= PM8607_INT_STATUS3,
317 		.mask_reg	= PM8607_INT_MASK_3,
318 		.offs		= 1 << 6,
319 	},
320 	[PM8607_IRQ_CHG_FAULT] = {
321 		.reg		= PM8607_INT_STATUS3,
322 		.mask_reg	= PM8607_INT_MASK_3,
323 		.offs		= 1 << 7,
324 	},
325 };
326 
327 static inline struct pm860x_irq_data *irq_to_pm860x(struct pm860x_chip *chip,
328 						    int irq)
329 {
330 	return &pm860x_irqs[irq - chip->irq_base];
331 }
332 
333 static irqreturn_t pm860x_irq(int irq, void *data)
334 {
335 	struct pm860x_chip *chip = data;
336 	struct pm860x_irq_data *irq_data;
337 	struct i2c_client *i2c;
338 	int read_reg = -1, value = 0;
339 	int i;
340 
341 	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
342 	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
343 		irq_data = &pm860x_irqs[i];
344 		if (read_reg != irq_data->reg) {
345 			read_reg = irq_data->reg;
346 			value = pm860x_reg_read(i2c, irq_data->reg);
347 		}
348 		if (value & irq_data->enable)
349 			handle_nested_irq(chip->irq_base + i);
350 	}
351 	return IRQ_HANDLED;
352 }
353 
354 static void pm860x_irq_lock(unsigned int irq)
355 {
356 	struct pm860x_chip *chip = get_irq_chip_data(irq);
357 
358 	mutex_lock(&chip->irq_lock);
359 }
360 
361 static void pm860x_irq_sync_unlock(unsigned int irq)
362 {
363 	struct pm860x_chip *chip = get_irq_chip_data(irq);
364 	struct pm860x_irq_data *irq_data;
365 	struct i2c_client *i2c;
366 	static unsigned char cached[3] = {0x0, 0x0, 0x0};
367 	unsigned char mask[3];
368 	int i;
369 
370 	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
371 	/* Load cached value. In initial, all IRQs are masked */
372 	for (i = 0; i < 3; i++)
373 		mask[i] = cached[i];
374 	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
375 		irq_data = &pm860x_irqs[i];
376 		switch (irq_data->mask_reg) {
377 		case PM8607_INT_MASK_1:
378 			mask[0] &= ~irq_data->offs;
379 			mask[0] |= irq_data->enable;
380 			break;
381 		case PM8607_INT_MASK_2:
382 			mask[1] &= ~irq_data->offs;
383 			mask[1] |= irq_data->enable;
384 			break;
385 		case PM8607_INT_MASK_3:
386 			mask[2] &= ~irq_data->offs;
387 			mask[2] |= irq_data->enable;
388 			break;
389 		default:
390 			dev_err(chip->dev, "wrong IRQ\n");
391 			break;
392 		}
393 	}
394 	/* update mask into registers */
395 	for (i = 0; i < 3; i++) {
396 		if (mask[i] != cached[i]) {
397 			cached[i] = mask[i];
398 			pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
399 		}
400 	}
401 
402 	mutex_unlock(&chip->irq_lock);
403 }
404 
405 static void pm860x_irq_enable(unsigned int irq)
406 {
407 	struct pm860x_chip *chip = get_irq_chip_data(irq);
408 	pm860x_irqs[irq - chip->irq_base].enable
409 		= pm860x_irqs[irq - chip->irq_base].offs;
410 }
411 
412 static void pm860x_irq_disable(unsigned int irq)
413 {
414 	struct pm860x_chip *chip = get_irq_chip_data(irq);
415 	pm860x_irqs[irq - chip->irq_base].enable = 0;
416 }
417 
418 static struct irq_chip pm860x_irq_chip = {
419 	.name		= "88pm860x",
420 	.bus_lock	= pm860x_irq_lock,
421 	.bus_sync_unlock = pm860x_irq_sync_unlock,
422 	.enable		= pm860x_irq_enable,
423 	.disable	= pm860x_irq_disable,
424 };
425 
426 static int __devinit device_gpadc_init(struct pm860x_chip *chip,
427 				       struct pm860x_platform_data *pdata)
428 {
429 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
430 				: chip->companion;
431 	int use_gpadc = 0, data, ret;
432 
433 	/* initialize GPADC without activating it */
434 
435 	if (pdata && pdata->touch) {
436 		/* set GPADC MISC1 register */
437 		data = 0;
438 		data |= (pdata->touch->gpadc_prebias << 1)
439 			& PM8607_GPADC_PREBIAS_MASK;
440 		data |= (pdata->touch->slot_cycle << 3)
441 			& PM8607_GPADC_SLOT_CYCLE_MASK;
442 		data |= (pdata->touch->off_scale << 5)
443 			& PM8607_GPADC_OFF_SCALE_MASK;
444 		data |= (pdata->touch->sw_cal << 7)
445 			& PM8607_GPADC_SW_CAL_MASK;
446 		if (data) {
447 			ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
448 			if (ret < 0)
449 				goto out;
450 		}
451 		/* set tsi prebias time */
452 		if (pdata->touch->tsi_prebias) {
453 			data = pdata->touch->tsi_prebias;
454 			ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
455 			if (ret < 0)
456 				goto out;
457 		}
458 		/* set prebias & prechg time of pen detect */
459 		data = 0;
460 		data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
461 		data |= (pdata->touch->pen_prechg << 5)
462 			& PM8607_PD_PRECHG_MASK;
463 		if (data) {
464 			ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
465 			if (ret < 0)
466 				goto out;
467 		}
468 
469 		use_gpadc = 1;
470 	}
471 
472 	/* turn on GPADC */
473 	if (use_gpadc) {
474 		ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
475 				      PM8607_GPADC_EN, PM8607_GPADC_EN);
476 	}
477 out:
478 	return ret;
479 }
480 
481 static int __devinit device_irq_init(struct pm860x_chip *chip,
482 				     struct pm860x_platform_data *pdata)
483 {
484 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
485 				: chip->companion;
486 	unsigned char status_buf[INT_STATUS_NUM];
487 	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
488 	struct irq_desc *desc;
489 	int i, data, mask, ret = -EINVAL;
490 	int __irq;
491 
492 	if (!pdata || !pdata->irq_base) {
493 		dev_warn(chip->dev, "No interrupt support on IRQ base\n");
494 		return -EINVAL;
495 	}
496 
497 	mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
498 		| PM8607_B0_MISC1_INT_MASK;
499 	data = 0;
500 	chip->irq_mode = 0;
501 	if (pdata && pdata->irq_mode) {
502 		/*
503 		 * irq_mode defines the way of clearing interrupt. If it's 1,
504 		 * clear IRQ by write. Otherwise, clear it by read.
505 		 * This control bit is valid from 88PM8607 B0 steping.
506 		 */
507 		data |= PM8607_B0_MISC1_INT_CLEAR;
508 		chip->irq_mode = 1;
509 	}
510 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
511 	if (ret < 0)
512 		goto out;
513 
514 	/* mask all IRQs */
515 	memset(status_buf, 0, INT_STATUS_NUM);
516 	ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
517 				INT_STATUS_NUM, status_buf);
518 	if (ret < 0)
519 		goto out;
520 
521 	if (chip->irq_mode) {
522 		/* clear interrupt status by write */
523 		memset(status_buf, 0xFF, INT_STATUS_NUM);
524 		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
525 					INT_STATUS_NUM, status_buf);
526 	} else {
527 		/* clear interrupt status by read */
528 		ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
529 					INT_STATUS_NUM, status_buf);
530 	}
531 	if (ret < 0)
532 		goto out;
533 
534 	mutex_init(&chip->irq_lock);
535 	chip->irq_base = pdata->irq_base;
536 	chip->core_irq = i2c->irq;
537 	if (!chip->core_irq)
538 		goto out;
539 
540 	desc = irq_to_desc(chip->core_irq);
541 
542 	/* register IRQ by genirq */
543 	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
544 		__irq = i + chip->irq_base;
545 		set_irq_chip_data(__irq, chip);
546 		set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
547 					 handle_edge_irq);
548 		set_irq_nested_thread(__irq, 1);
549 #ifdef CONFIG_ARM
550 		set_irq_flags(__irq, IRQF_VALID);
551 #else
552 		set_irq_noprobe(__irq);
553 #endif
554 	}
555 
556 	ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
557 				   "88pm860x", chip);
558 	if (ret) {
559 		dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
560 		chip->core_irq = 0;
561 	}
562 
563 	return 0;
564 out:
565 	chip->core_irq = 0;
566 	return ret;
567 }
568 
569 static void device_irq_exit(struct pm860x_chip *chip)
570 {
571 	if (chip->core_irq)
572 		free_irq(chip->core_irq, chip);
573 }
574 
575 static void __devinit device_8606_init(struct pm860x_chip *chip,
576 				       struct i2c_client *i2c,
577 				       struct pm860x_platform_data *pdata)
578 {
579 	int ret;
580 
581 	if (pdata && pdata->backlight) {
582 		ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
583 				      ARRAY_SIZE(backlight_devs),
584 				      &backlight_resources[0], 0);
585 		if (ret < 0) {
586 			dev_err(chip->dev, "Failed to add backlight "
587 				"subdev\n");
588 			goto out_dev;
589 		}
590 	}
591 
592 	if (pdata && pdata->led) {
593 		ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
594 				      ARRAY_SIZE(led_devs),
595 				      &led_resources[0], 0);
596 		if (ret < 0) {
597 			dev_err(chip->dev, "Failed to add led "
598 				"subdev\n");
599 			goto out_dev;
600 		}
601 	}
602 	return;
603 out_dev:
604 	mfd_remove_devices(chip->dev);
605 	device_irq_exit(chip);
606 }
607 
608 static void __devinit device_8607_init(struct pm860x_chip *chip,
609 				       struct i2c_client *i2c,
610 				       struct pm860x_platform_data *pdata)
611 {
612 	int data, ret;
613 
614 	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
615 	if (ret < 0) {
616 		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
617 		goto out;
618 	}
619 	if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
620 		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
621 			 ret);
622 	else {
623 		dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
624 			"Chip ID: %02x\n", ret);
625 		goto out;
626 	}
627 
628 	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
629 	if (ret < 0) {
630 		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
631 		goto out;
632 	}
633 	if (ret & PM8607_BUCK3_DOUBLE)
634 		chip->buck3_double = 1;
635 
636 	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
637 	if (ret < 0) {
638 		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
639 		goto out;
640 	}
641 
642 	if (pdata && (pdata->i2c_port == PI2C_PORT))
643 		data = PM8607_B0_MISC1_PI2C;
644 	else
645 		data = 0;
646 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
647 	if (ret < 0) {
648 		dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
649 		goto out;
650 	}
651 
652 	ret = device_gpadc_init(chip, pdata);
653 	if (ret < 0)
654 		goto out;
655 
656 	ret = device_irq_init(chip, pdata);
657 	if (ret < 0)
658 		goto out;
659 
660 	ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
661 			      ARRAY_SIZE(regulator_devs),
662 			      &regulator_resources[0], 0);
663 	if (ret < 0) {
664 		dev_err(chip->dev, "Failed to add regulator subdev\n");
665 		goto out_dev;
666 	}
667 
668 	if (pdata && pdata->touch) {
669 		ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
670 				      ARRAY_SIZE(touch_devs),
671 				      &touch_resources[0], 0);
672 		if (ret < 0) {
673 			dev_err(chip->dev, "Failed to add touch "
674 				"subdev\n");
675 			goto out_dev;
676 		}
677 	}
678 
679 	if (pdata && pdata->power) {
680 		ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
681 				      ARRAY_SIZE(power_devs),
682 				      &power_supply_resources[0], 0);
683 		if (ret < 0) {
684 			dev_err(chip->dev, "Failed to add power supply "
685 				"subdev\n");
686 			goto out_dev;
687 		}
688 	}
689 
690 	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
691 			      ARRAY_SIZE(onkey_devs),
692 			      &onkey_resources[0], 0);
693 	if (ret < 0) {
694 		dev_err(chip->dev, "Failed to add onkey subdev\n");
695 		goto out_dev;
696 	}
697 
698 	return;
699 out_dev:
700 	mfd_remove_devices(chip->dev);
701 	device_irq_exit(chip);
702 out:
703 	return;
704 }
705 
706 int __devinit pm860x_device_init(struct pm860x_chip *chip,
707 		       struct pm860x_platform_data *pdata)
708 {
709 	chip->core_irq = 0;
710 
711 	switch (chip->id) {
712 	case CHIP_PM8606:
713 		device_8606_init(chip, chip->client, pdata);
714 		break;
715 	case CHIP_PM8607:
716 		device_8607_init(chip, chip->client, pdata);
717 		break;
718 	}
719 
720 	if (chip->companion) {
721 		switch (chip->id) {
722 		case CHIP_PM8607:
723 			device_8606_init(chip, chip->companion, pdata);
724 			break;
725 		case CHIP_PM8606:
726 			device_8607_init(chip, chip->companion, pdata);
727 			break;
728 		}
729 	}
730 
731 	return 0;
732 }
733 
734 void __devexit pm860x_device_exit(struct pm860x_chip *chip)
735 {
736 	device_irq_exit(chip);
737 	mfd_remove_devices(chip->dev);
738 }
739 
740 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
741 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
742 MODULE_LICENSE("GPL");
743