xref: /openbmc/linux/drivers/mfd/88pm860x-core.c (revision eb6e8ddf)
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 data;
432 	int ret;
433 
434 	/* initialize GPADC without activating it */
435 
436 	if (!pdata || !pdata->touch)
437 		return -EINVAL;
438 
439 	/* set GPADC MISC1 register */
440 	data = 0;
441 	data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
442 	data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
443 	data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
444 	data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
445 	if (data) {
446 		ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
447 		if (ret < 0)
448 			goto out;
449 	}
450 	/* set tsi prebias time */
451 	if (pdata->touch->tsi_prebias) {
452 		data = pdata->touch->tsi_prebias;
453 		ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
454 		if (ret < 0)
455 			goto out;
456 	}
457 	/* set prebias & prechg time of pen detect */
458 	data = 0;
459 	data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
460 	data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
461 	if (data) {
462 		ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
463 		if (ret < 0)
464 			goto out;
465 	}
466 
467 	ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
468 			      PM8607_GPADC_EN, PM8607_GPADC_EN);
469 out:
470 	return ret;
471 }
472 
473 static int __devinit device_irq_init(struct pm860x_chip *chip,
474 				     struct pm860x_platform_data *pdata)
475 {
476 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
477 				: chip->companion;
478 	unsigned char status_buf[INT_STATUS_NUM];
479 	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
480 	struct irq_desc *desc;
481 	int i, data, mask, ret = -EINVAL;
482 	int __irq;
483 
484 	if (!pdata || !pdata->irq_base) {
485 		dev_warn(chip->dev, "No interrupt support on IRQ base\n");
486 		return -EINVAL;
487 	}
488 
489 	mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
490 		| PM8607_B0_MISC1_INT_MASK;
491 	data = 0;
492 	chip->irq_mode = 0;
493 	if (pdata && pdata->irq_mode) {
494 		/*
495 		 * irq_mode defines the way of clearing interrupt. If it's 1,
496 		 * clear IRQ by write. Otherwise, clear it by read.
497 		 * This control bit is valid from 88PM8607 B0 steping.
498 		 */
499 		data |= PM8607_B0_MISC1_INT_CLEAR;
500 		chip->irq_mode = 1;
501 	}
502 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
503 	if (ret < 0)
504 		goto out;
505 
506 	/* mask all IRQs */
507 	memset(status_buf, 0, INT_STATUS_NUM);
508 	ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
509 				INT_STATUS_NUM, status_buf);
510 	if (ret < 0)
511 		goto out;
512 
513 	if (chip->irq_mode) {
514 		/* clear interrupt status by write */
515 		memset(status_buf, 0xFF, INT_STATUS_NUM);
516 		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
517 					INT_STATUS_NUM, status_buf);
518 	} else {
519 		/* clear interrupt status by read */
520 		ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
521 					INT_STATUS_NUM, status_buf);
522 	}
523 	if (ret < 0)
524 		goto out;
525 
526 	mutex_init(&chip->irq_lock);
527 	chip->irq_base = pdata->irq_base;
528 	chip->core_irq = i2c->irq;
529 	if (!chip->core_irq)
530 		goto out;
531 
532 	desc = irq_to_desc(chip->core_irq);
533 
534 	/* register IRQ by genirq */
535 	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
536 		__irq = i + chip->irq_base;
537 		set_irq_chip_data(__irq, chip);
538 		set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
539 					 handle_edge_irq);
540 		set_irq_nested_thread(__irq, 1);
541 #ifdef CONFIG_ARM
542 		set_irq_flags(__irq, IRQF_VALID);
543 #else
544 		set_irq_noprobe(__irq);
545 #endif
546 	}
547 
548 	ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
549 				   "88pm860x", chip);
550 	if (ret) {
551 		dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
552 		chip->core_irq = 0;
553 	}
554 
555 	return 0;
556 out:
557 	chip->core_irq = 0;
558 	return ret;
559 }
560 
561 static void device_irq_exit(struct pm860x_chip *chip)
562 {
563 	if (chip->core_irq)
564 		free_irq(chip->core_irq, chip);
565 }
566 
567 static void __devinit device_8606_init(struct pm860x_chip *chip,
568 				       struct i2c_client *i2c,
569 				       struct pm860x_platform_data *pdata)
570 {
571 	int ret;
572 
573 	if (pdata && pdata->backlight) {
574 		ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
575 				      ARRAY_SIZE(backlight_devs),
576 				      &backlight_resources[0], 0);
577 		if (ret < 0) {
578 			dev_err(chip->dev, "Failed to add backlight "
579 				"subdev\n");
580 			goto out_dev;
581 		}
582 	}
583 
584 	if (pdata && pdata->led) {
585 		ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
586 				      ARRAY_SIZE(led_devs),
587 				      &led_resources[0], 0);
588 		if (ret < 0) {
589 			dev_err(chip->dev, "Failed to add led "
590 				"subdev\n");
591 			goto out_dev;
592 		}
593 	}
594 	return;
595 out_dev:
596 	mfd_remove_devices(chip->dev);
597 	device_irq_exit(chip);
598 }
599 
600 static void __devinit device_8607_init(struct pm860x_chip *chip,
601 				       struct i2c_client *i2c,
602 				       struct pm860x_platform_data *pdata)
603 {
604 	int data, ret;
605 
606 	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
607 	if (ret < 0) {
608 		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
609 		goto out;
610 	}
611 	if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
612 		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
613 			 ret);
614 	else {
615 		dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
616 			"Chip ID: %02x\n", ret);
617 		goto out;
618 	}
619 
620 	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
621 	if (ret < 0) {
622 		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
623 		goto out;
624 	}
625 	if (ret & PM8607_BUCK3_DOUBLE)
626 		chip->buck3_double = 1;
627 
628 	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
629 	if (ret < 0) {
630 		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
631 		goto out;
632 	}
633 
634 	if (pdata && (pdata->i2c_port == PI2C_PORT))
635 		data = PM8607_B0_MISC1_PI2C;
636 	else
637 		data = 0;
638 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
639 	if (ret < 0) {
640 		dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
641 		goto out;
642 	}
643 
644 	ret = device_gpadc_init(chip, pdata);
645 	if (ret < 0)
646 		goto out;
647 
648 	ret = device_irq_init(chip, pdata);
649 	if (ret < 0)
650 		goto out;
651 
652 	ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
653 			      ARRAY_SIZE(regulator_devs),
654 			      &regulator_resources[0], 0);
655 	if (ret < 0) {
656 		dev_err(chip->dev, "Failed to add regulator subdev\n");
657 		goto out_dev;
658 	}
659 
660 	if (pdata && pdata->touch) {
661 		ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
662 				      ARRAY_SIZE(touch_devs),
663 				      &touch_resources[0], 0);
664 		if (ret < 0) {
665 			dev_err(chip->dev, "Failed to add touch "
666 				"subdev\n");
667 			goto out_dev;
668 		}
669 	}
670 
671 	if (pdata && pdata->power) {
672 		ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
673 				      ARRAY_SIZE(power_devs),
674 				      &power_supply_resources[0], 0);
675 		if (ret < 0) {
676 			dev_err(chip->dev, "Failed to add power supply "
677 				"subdev\n");
678 			goto out_dev;
679 		}
680 	}
681 
682 	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
683 			      ARRAY_SIZE(onkey_devs),
684 			      &onkey_resources[0], 0);
685 	if (ret < 0) {
686 		dev_err(chip->dev, "Failed to add onkey subdev\n");
687 		goto out_dev;
688 	}
689 
690 	return;
691 out_dev:
692 	mfd_remove_devices(chip->dev);
693 	device_irq_exit(chip);
694 out:
695 	return;
696 }
697 
698 int __devinit pm860x_device_init(struct pm860x_chip *chip,
699 		       struct pm860x_platform_data *pdata)
700 {
701 	chip->core_irq = 0;
702 
703 	switch (chip->id) {
704 	case CHIP_PM8606:
705 		device_8606_init(chip, chip->client, pdata);
706 		break;
707 	case CHIP_PM8607:
708 		device_8607_init(chip, chip->client, pdata);
709 		break;
710 	}
711 
712 	if (chip->companion) {
713 		switch (chip->id) {
714 		case CHIP_PM8607:
715 			device_8606_init(chip, chip->companion, pdata);
716 			break;
717 		case CHIP_PM8606:
718 			device_8607_init(chip, chip->companion, pdata);
719 			break;
720 		}
721 	}
722 
723 	return 0;
724 }
725 
726 void __devexit pm860x_device_exit(struct pm860x_chip *chip)
727 {
728 	device_irq_exit(chip);
729 	mfd_remove_devices(chip->dev);
730 }
731 
732 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
733 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
734 MODULE_LICENSE("GPL");
735