Merge branch 'master' of ../mine
[pandora-kernel.git] / drivers / mfd / 88pm860x-core.c
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 codec_resources[] = {
162         {
163                 /* Headset microphone insertion or removal */
164                 .name           = "micin",
165                 .start          = PM8607_IRQ_MICIN,
166                 .end            = PM8607_IRQ_MICIN,
167                 .flags          = IORESOURCE_IRQ,
168         }, {
169                 /* Hook-switch press or release */
170                 .name           = "hook",
171                 .start          = PM8607_IRQ_HOOK,
172                 .end            = PM8607_IRQ_HOOK,
173                 .flags          = IORESOURCE_IRQ,
174         }, {
175                 /* Headset insertion or removal */
176                 .name           = "headset",
177                 .start          = PM8607_IRQ_HEADSET,
178                 .end            = PM8607_IRQ_HEADSET,
179                 .flags          = IORESOURCE_IRQ,
180         }, {
181                 /* Audio short */
182                 .name           = "audio-short",
183                 .start          = PM8607_IRQ_AUDIO_SHORT,
184                 .end            = PM8607_IRQ_AUDIO_SHORT,
185                 .flags          = IORESOURCE_IRQ,
186         },
187 };
188
189 static struct mfd_cell codec_devs[] = {
190         {
191                 .name           = "88pm860x-codec",
192                 .num_resources  = ARRAY_SIZE(codec_resources),
193                 .resources      = &codec_resources[0],
194                 .id             = -1,
195         },
196 };
197
198 static struct resource regulator_resources[] = {
199         PM8607_REG_RESOURCE(BUCK1, BUCK1),
200         PM8607_REG_RESOURCE(BUCK2, BUCK2),
201         PM8607_REG_RESOURCE(BUCK3, BUCK3),
202         PM8607_REG_RESOURCE(LDO1,  LDO1),
203         PM8607_REG_RESOURCE(LDO2,  LDO2),
204         PM8607_REG_RESOURCE(LDO3,  LDO3),
205         PM8607_REG_RESOURCE(LDO4,  LDO4),
206         PM8607_REG_RESOURCE(LDO5,  LDO5),
207         PM8607_REG_RESOURCE(LDO6,  LDO6),
208         PM8607_REG_RESOURCE(LDO7,  LDO7),
209         PM8607_REG_RESOURCE(LDO8,  LDO8),
210         PM8607_REG_RESOURCE(LDO9,  LDO9),
211         PM8607_REG_RESOURCE(LDO10, LDO10),
212         PM8607_REG_RESOURCE(LDO12, LDO12),
213         PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET),
214         PM8607_REG_RESOURCE(LDO14, LDO14),
215 };
216
217 #define PM8607_REG_DEVS(_id)                                            \
218 {                                                                       \
219         .name           = "88pm860x-regulator",                         \
220         .num_resources  = 1,                                            \
221         .resources      = &regulator_resources[PM8607_ID_##_id],        \
222         .id             = PM8607_ID_##_id,                              \
223 }
224
225 static struct mfd_cell regulator_devs[] = {
226         PM8607_REG_DEVS(BUCK1),
227         PM8607_REG_DEVS(BUCK2),
228         PM8607_REG_DEVS(BUCK3),
229         PM8607_REG_DEVS(LDO1),
230         PM8607_REG_DEVS(LDO2),
231         PM8607_REG_DEVS(LDO3),
232         PM8607_REG_DEVS(LDO4),
233         PM8607_REG_DEVS(LDO5),
234         PM8607_REG_DEVS(LDO6),
235         PM8607_REG_DEVS(LDO7),
236         PM8607_REG_DEVS(LDO8),
237         PM8607_REG_DEVS(LDO9),
238         PM8607_REG_DEVS(LDO10),
239         PM8607_REG_DEVS(LDO12),
240         PM8607_REG_DEVS(LDO13),
241         PM8607_REG_DEVS(LDO14),
242 };
243
244 struct pm860x_irq_data {
245         int     reg;
246         int     mask_reg;
247         int     enable;         /* enable or not */
248         int     offs;           /* bit offset in mask register */
249 };
250
251 static struct pm860x_irq_data pm860x_irqs[] = {
252         [PM8607_IRQ_ONKEY] = {
253                 .reg            = PM8607_INT_STATUS1,
254                 .mask_reg       = PM8607_INT_MASK_1,
255                 .offs           = 1 << 0,
256         },
257         [PM8607_IRQ_EXTON] = {
258                 .reg            = PM8607_INT_STATUS1,
259                 .mask_reg       = PM8607_INT_MASK_1,
260                 .offs           = 1 << 1,
261         },
262         [PM8607_IRQ_CHG] = {
263                 .reg            = PM8607_INT_STATUS1,
264                 .mask_reg       = PM8607_INT_MASK_1,
265                 .offs           = 1 << 2,
266         },
267         [PM8607_IRQ_BAT] = {
268                 .reg            = PM8607_INT_STATUS1,
269                 .mask_reg       = PM8607_INT_MASK_1,
270                 .offs           = 1 << 3,
271         },
272         [PM8607_IRQ_RTC] = {
273                 .reg            = PM8607_INT_STATUS1,
274                 .mask_reg       = PM8607_INT_MASK_1,
275                 .offs           = 1 << 4,
276         },
277         [PM8607_IRQ_CC] = {
278                 .reg            = PM8607_INT_STATUS1,
279                 .mask_reg       = PM8607_INT_MASK_1,
280                 .offs           = 1 << 5,
281         },
282         [PM8607_IRQ_VBAT] = {
283                 .reg            = PM8607_INT_STATUS2,
284                 .mask_reg       = PM8607_INT_MASK_2,
285                 .offs           = 1 << 0,
286         },
287         [PM8607_IRQ_VCHG] = {
288                 .reg            = PM8607_INT_STATUS2,
289                 .mask_reg       = PM8607_INT_MASK_2,
290                 .offs           = 1 << 1,
291         },
292         [PM8607_IRQ_VSYS] = {
293                 .reg            = PM8607_INT_STATUS2,
294                 .mask_reg       = PM8607_INT_MASK_2,
295                 .offs           = 1 << 2,
296         },
297         [PM8607_IRQ_TINT] = {
298                 .reg            = PM8607_INT_STATUS2,
299                 .mask_reg       = PM8607_INT_MASK_2,
300                 .offs           = 1 << 3,
301         },
302         [PM8607_IRQ_GPADC0] = {
303                 .reg            = PM8607_INT_STATUS2,
304                 .mask_reg       = PM8607_INT_MASK_2,
305                 .offs           = 1 << 4,
306         },
307         [PM8607_IRQ_GPADC1] = {
308                 .reg            = PM8607_INT_STATUS2,
309                 .mask_reg       = PM8607_INT_MASK_2,
310                 .offs           = 1 << 5,
311         },
312         [PM8607_IRQ_GPADC2] = {
313                 .reg            = PM8607_INT_STATUS2,
314                 .mask_reg       = PM8607_INT_MASK_2,
315                 .offs           = 1 << 6,
316         },
317         [PM8607_IRQ_GPADC3] = {
318                 .reg            = PM8607_INT_STATUS2,
319                 .mask_reg       = PM8607_INT_MASK_2,
320                 .offs           = 1 << 7,
321         },
322         [PM8607_IRQ_AUDIO_SHORT] = {
323                 .reg            = PM8607_INT_STATUS3,
324                 .mask_reg       = PM8607_INT_MASK_3,
325                 .offs           = 1 << 0,
326         },
327         [PM8607_IRQ_PEN] = {
328                 .reg            = PM8607_INT_STATUS3,
329                 .mask_reg       = PM8607_INT_MASK_3,
330                 .offs           = 1 << 1,
331         },
332         [PM8607_IRQ_HEADSET] = {
333                 .reg            = PM8607_INT_STATUS3,
334                 .mask_reg       = PM8607_INT_MASK_3,
335                 .offs           = 1 << 2,
336         },
337         [PM8607_IRQ_HOOK] = {
338                 .reg            = PM8607_INT_STATUS3,
339                 .mask_reg       = PM8607_INT_MASK_3,
340                 .offs           = 1 << 3,
341         },
342         [PM8607_IRQ_MICIN] = {
343                 .reg            = PM8607_INT_STATUS3,
344                 .mask_reg       = PM8607_INT_MASK_3,
345                 .offs           = 1 << 4,
346         },
347         [PM8607_IRQ_CHG_FAIL] = {
348                 .reg            = PM8607_INT_STATUS3,
349                 .mask_reg       = PM8607_INT_MASK_3,
350                 .offs           = 1 << 5,
351         },
352         [PM8607_IRQ_CHG_DONE] = {
353                 .reg            = PM8607_INT_STATUS3,
354                 .mask_reg       = PM8607_INT_MASK_3,
355                 .offs           = 1 << 6,
356         },
357         [PM8607_IRQ_CHG_FAULT] = {
358                 .reg            = PM8607_INT_STATUS3,
359                 .mask_reg       = PM8607_INT_MASK_3,
360                 .offs           = 1 << 7,
361         },
362 };
363
364 static inline struct pm860x_irq_data *irq_to_pm860x(struct pm860x_chip *chip,
365                                                     int irq)
366 {
367         return &pm860x_irqs[irq - chip->irq_base];
368 }
369
370 static irqreturn_t pm860x_irq(int irq, void *data)
371 {
372         struct pm860x_chip *chip = data;
373         struct pm860x_irq_data *irq_data;
374         struct i2c_client *i2c;
375         int read_reg = -1, value = 0;
376         int i;
377
378         i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
379         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
380                 irq_data = &pm860x_irqs[i];
381                 if (read_reg != irq_data->reg) {
382                         read_reg = irq_data->reg;
383                         value = pm860x_reg_read(i2c, irq_data->reg);
384                 }
385                 if (value & irq_data->enable)
386                         handle_nested_irq(chip->irq_base + i);
387         }
388         return IRQ_HANDLED;
389 }
390
391 static void pm860x_irq_lock(unsigned int irq)
392 {
393         struct pm860x_chip *chip = get_irq_chip_data(irq);
394
395         mutex_lock(&chip->irq_lock);
396 }
397
398 static void pm860x_irq_sync_unlock(unsigned int irq)
399 {
400         struct pm860x_chip *chip = get_irq_chip_data(irq);
401         struct pm860x_irq_data *irq_data;
402         struct i2c_client *i2c;
403         static unsigned char cached[3] = {0x0, 0x0, 0x0};
404         unsigned char mask[3];
405         int i;
406
407         i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
408         /* Load cached value. In initial, all IRQs are masked */
409         for (i = 0; i < 3; i++)
410                 mask[i] = cached[i];
411         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
412                 irq_data = &pm860x_irqs[i];
413                 switch (irq_data->mask_reg) {
414                 case PM8607_INT_MASK_1:
415                         mask[0] &= ~irq_data->offs;
416                         mask[0] |= irq_data->enable;
417                         break;
418                 case PM8607_INT_MASK_2:
419                         mask[1] &= ~irq_data->offs;
420                         mask[1] |= irq_data->enable;
421                         break;
422                 case PM8607_INT_MASK_3:
423                         mask[2] &= ~irq_data->offs;
424                         mask[2] |= irq_data->enable;
425                         break;
426                 default:
427                         dev_err(chip->dev, "wrong IRQ\n");
428                         break;
429                 }
430         }
431         /* update mask into registers */
432         for (i = 0; i < 3; i++) {
433                 if (mask[i] != cached[i]) {
434                         cached[i] = mask[i];
435                         pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
436                 }
437         }
438
439         mutex_unlock(&chip->irq_lock);
440 }
441
442 static void pm860x_irq_enable(unsigned int irq)
443 {
444         struct pm860x_chip *chip = get_irq_chip_data(irq);
445         pm860x_irqs[irq - chip->irq_base].enable
446                 = pm860x_irqs[irq - chip->irq_base].offs;
447 }
448
449 static void pm860x_irq_disable(unsigned int irq)
450 {
451         struct pm860x_chip *chip = get_irq_chip_data(irq);
452         pm860x_irqs[irq - chip->irq_base].enable = 0;
453 }
454
455 static struct irq_chip pm860x_irq_chip = {
456         .name           = "88pm860x",
457         .bus_lock       = pm860x_irq_lock,
458         .bus_sync_unlock = pm860x_irq_sync_unlock,
459         .enable         = pm860x_irq_enable,
460         .disable        = pm860x_irq_disable,
461 };
462
463 static int __devinit device_gpadc_init(struct pm860x_chip *chip,
464                                        struct pm860x_platform_data *pdata)
465 {
466         struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
467                                 : chip->companion;
468         int data;
469         int ret;
470
471         /* initialize GPADC without activating it */
472
473         if (!pdata || !pdata->touch)
474                 return -EINVAL;
475
476         /* set GPADC MISC1 register */
477         data = 0;
478         data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
479         data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
480         data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
481         data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
482         if (data) {
483                 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
484                 if (ret < 0)
485                         goto out;
486         }
487         /* set tsi prebias time */
488         if (pdata->touch->tsi_prebias) {
489                 data = pdata->touch->tsi_prebias;
490                 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
491                 if (ret < 0)
492                         goto out;
493         }
494         /* set prebias & prechg time of pen detect */
495         data = 0;
496         data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
497         data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
498         if (data) {
499                 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
500                 if (ret < 0)
501                         goto out;
502         }
503
504         ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
505                               PM8607_GPADC_EN, PM8607_GPADC_EN);
506 out:
507         return ret;
508 }
509
510 static int __devinit device_irq_init(struct pm860x_chip *chip,
511                                      struct pm860x_platform_data *pdata)
512 {
513         struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
514                                 : chip->companion;
515         unsigned char status_buf[INT_STATUS_NUM];
516         unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
517         struct irq_desc *desc;
518         int i, data, mask, ret = -EINVAL;
519         int __irq;
520
521         if (!pdata || !pdata->irq_base) {
522                 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
523                 return -EINVAL;
524         }
525
526         mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
527                 | PM8607_B0_MISC1_INT_MASK;
528         data = 0;
529         chip->irq_mode = 0;
530         if (pdata && pdata->irq_mode) {
531                 /*
532                  * irq_mode defines the way of clearing interrupt. If it's 1,
533                  * clear IRQ by write. Otherwise, clear it by read.
534                  * This control bit is valid from 88PM8607 B0 steping.
535                  */
536                 data |= PM8607_B0_MISC1_INT_CLEAR;
537                 chip->irq_mode = 1;
538         }
539         ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
540         if (ret < 0)
541                 goto out;
542
543         /* mask all IRQs */
544         memset(status_buf, 0, INT_STATUS_NUM);
545         ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
546                                 INT_STATUS_NUM, status_buf);
547         if (ret < 0)
548                 goto out;
549
550         if (chip->irq_mode) {
551                 /* clear interrupt status by write */
552                 memset(status_buf, 0xFF, INT_STATUS_NUM);
553                 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
554                                         INT_STATUS_NUM, status_buf);
555         } else {
556                 /* clear interrupt status by read */
557                 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
558                                         INT_STATUS_NUM, status_buf);
559         }
560         if (ret < 0)
561                 goto out;
562
563         mutex_init(&chip->irq_lock);
564         chip->irq_base = pdata->irq_base;
565         chip->core_irq = i2c->irq;
566         if (!chip->core_irq)
567                 goto out;
568
569         desc = irq_to_desc(chip->core_irq);
570
571         /* register IRQ by genirq */
572         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
573                 __irq = i + chip->irq_base;
574                 set_irq_chip_data(__irq, chip);
575                 set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
576                                          handle_edge_irq);
577                 set_irq_nested_thread(__irq, 1);
578 #ifdef CONFIG_ARM
579                 set_irq_flags(__irq, IRQF_VALID);
580 #else
581                 set_irq_noprobe(__irq);
582 #endif
583         }
584
585         ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
586                                    "88pm860x", chip);
587         if (ret) {
588                 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
589                 chip->core_irq = 0;
590         }
591
592         return 0;
593 out:
594         chip->core_irq = 0;
595         return ret;
596 }
597
598 static void device_irq_exit(struct pm860x_chip *chip)
599 {
600         if (chip->core_irq)
601                 free_irq(chip->core_irq, chip);
602 }
603
604 static void __devinit device_8606_init(struct pm860x_chip *chip,
605                                        struct i2c_client *i2c,
606                                        struct pm860x_platform_data *pdata)
607 {
608         int ret;
609
610         if (pdata && pdata->backlight) {
611                 ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
612                                       ARRAY_SIZE(backlight_devs),
613                                       &backlight_resources[0], 0);
614                 if (ret < 0) {
615                         dev_err(chip->dev, "Failed to add backlight "
616                                 "subdev\n");
617                         goto out_dev;
618                 }
619         }
620
621         if (pdata && pdata->led) {
622                 ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
623                                       ARRAY_SIZE(led_devs),
624                                       &led_resources[0], 0);
625                 if (ret < 0) {
626                         dev_err(chip->dev, "Failed to add led "
627                                 "subdev\n");
628                         goto out_dev;
629                 }
630         }
631         return;
632 out_dev:
633         mfd_remove_devices(chip->dev);
634         device_irq_exit(chip);
635 }
636
637 static void __devinit device_8607_init(struct pm860x_chip *chip,
638                                        struct i2c_client *i2c,
639                                        struct pm860x_platform_data *pdata)
640 {
641         int data, ret;
642
643         ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
644         if (ret < 0) {
645                 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
646                 goto out;
647         }
648         switch (ret & PM8607_VERSION_MASK) {
649         case 0x40:
650         case 0x50:
651                 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
652                          ret);
653                 break;
654         default:
655                 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
656                         "Chip ID: %02x\n", ret);
657                 goto out;
658         }
659
660         ret = pm860x_reg_read(i2c, PM8607_BUCK3);
661         if (ret < 0) {
662                 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
663                 goto out;
664         }
665         if (ret & PM8607_BUCK3_DOUBLE)
666                 chip->buck3_double = 1;
667
668         ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
669         if (ret < 0) {
670                 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
671                 goto out;
672         }
673
674         if (pdata && (pdata->i2c_port == PI2C_PORT))
675                 data = PM8607_B0_MISC1_PI2C;
676         else
677                 data = 0;
678         ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
679         if (ret < 0) {
680                 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
681                 goto out;
682         }
683
684         ret = device_gpadc_init(chip, pdata);
685         if (ret < 0)
686                 goto out;
687
688         ret = device_irq_init(chip, pdata);
689         if (ret < 0)
690                 goto out;
691
692         ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
693                               ARRAY_SIZE(regulator_devs),
694                               &regulator_resources[0], 0);
695         if (ret < 0) {
696                 dev_err(chip->dev, "Failed to add regulator subdev\n");
697                 goto out_dev;
698         }
699
700         if (pdata && pdata->touch) {
701                 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
702                                       ARRAY_SIZE(touch_devs),
703                                       &touch_resources[0], 0);
704                 if (ret < 0) {
705                         dev_err(chip->dev, "Failed to add touch "
706                                 "subdev\n");
707                         goto out_dev;
708                 }
709         }
710
711         if (pdata && pdata->power) {
712                 ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
713                                       ARRAY_SIZE(power_devs),
714                                       &power_supply_resources[0], 0);
715                 if (ret < 0) {
716                         dev_err(chip->dev, "Failed to add power supply "
717                                 "subdev\n");
718                         goto out_dev;
719                 }
720         }
721
722         ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
723                               ARRAY_SIZE(onkey_devs),
724                               &onkey_resources[0], 0);
725         if (ret < 0) {
726                 dev_err(chip->dev, "Failed to add onkey subdev\n");
727                 goto out_dev;
728         }
729
730         ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
731                               ARRAY_SIZE(codec_devs),
732                               &codec_resources[0], 0);
733         if (ret < 0) {
734                 dev_err(chip->dev, "Failed to add codec subdev\n");
735                 goto out_dev;
736         }
737         return;
738 out_dev:
739         mfd_remove_devices(chip->dev);
740         device_irq_exit(chip);
741 out:
742         return;
743 }
744
745 int __devinit pm860x_device_init(struct pm860x_chip *chip,
746                        struct pm860x_platform_data *pdata)
747 {
748         chip->core_irq = 0;
749
750         switch (chip->id) {
751         case CHIP_PM8606:
752                 device_8606_init(chip, chip->client, pdata);
753                 break;
754         case CHIP_PM8607:
755                 device_8607_init(chip, chip->client, pdata);
756                 break;
757         }
758
759         if (chip->companion) {
760                 switch (chip->id) {
761                 case CHIP_PM8607:
762                         device_8606_init(chip, chip->companion, pdata);
763                         break;
764                 case CHIP_PM8606:
765                         device_8607_init(chip, chip->companion, pdata);
766                         break;
767                 }
768         }
769
770         return 0;
771 }
772
773 void __devexit pm860x_device_exit(struct pm860x_chip *chip)
774 {
775         device_irq_exit(chip);
776         mfd_remove_devices(chip->dev);
777 }
778
779 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
780 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
781 MODULE_LICENSE("GPL");