]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/mfd/88pm860x-core.c
kbuild: Fix computing srcversion for modules
[mv-sheeva.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 irqreturn_t pm860x_irq(int irq, void *data)
365 {
366         struct pm860x_chip *chip = data;
367         struct pm860x_irq_data *irq_data;
368         struct i2c_client *i2c;
369         int read_reg = -1, value = 0;
370         int i;
371
372         i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
373         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
374                 irq_data = &pm860x_irqs[i];
375                 if (read_reg != irq_data->reg) {
376                         read_reg = irq_data->reg;
377                         value = pm860x_reg_read(i2c, irq_data->reg);
378                 }
379                 if (value & irq_data->enable)
380                         handle_nested_irq(chip->irq_base + i);
381         }
382         return IRQ_HANDLED;
383 }
384
385 static void pm860x_irq_lock(struct irq_data *data)
386 {
387         struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
388
389         mutex_lock(&chip->irq_lock);
390 }
391
392 static void pm860x_irq_sync_unlock(struct irq_data *data)
393 {
394         struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
395         struct pm860x_irq_data *irq_data;
396         struct i2c_client *i2c;
397         static unsigned char cached[3] = {0x0, 0x0, 0x0};
398         unsigned char mask[3];
399         int i;
400
401         i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
402         /* Load cached value. In initial, all IRQs are masked */
403         for (i = 0; i < 3; i++)
404                 mask[i] = cached[i];
405         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
406                 irq_data = &pm860x_irqs[i];
407                 switch (irq_data->mask_reg) {
408                 case PM8607_INT_MASK_1:
409                         mask[0] &= ~irq_data->offs;
410                         mask[0] |= irq_data->enable;
411                         break;
412                 case PM8607_INT_MASK_2:
413                         mask[1] &= ~irq_data->offs;
414                         mask[1] |= irq_data->enable;
415                         break;
416                 case PM8607_INT_MASK_3:
417                         mask[2] &= ~irq_data->offs;
418                         mask[2] |= irq_data->enable;
419                         break;
420                 default:
421                         dev_err(chip->dev, "wrong IRQ\n");
422                         break;
423                 }
424         }
425         /* update mask into registers */
426         for (i = 0; i < 3; i++) {
427                 if (mask[i] != cached[i]) {
428                         cached[i] = mask[i];
429                         pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
430                 }
431         }
432
433         mutex_unlock(&chip->irq_lock);
434 }
435
436 static void pm860x_irq_enable(struct irq_data *data)
437 {
438         struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
439         pm860x_irqs[data->irq - chip->irq_base].enable
440                 = pm860x_irqs[data->irq - chip->irq_base].offs;
441 }
442
443 static void pm860x_irq_disable(struct irq_data *data)
444 {
445         struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
446         pm860x_irqs[data->irq - chip->irq_base].enable = 0;
447 }
448
449 static struct irq_chip pm860x_irq_chip = {
450         .name           = "88pm860x",
451         .irq_bus_lock   = pm860x_irq_lock,
452         .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
453         .irq_enable     = pm860x_irq_enable,
454         .irq_disable    = pm860x_irq_disable,
455 };
456
457 static int __devinit device_gpadc_init(struct pm860x_chip *chip,
458                                        struct pm860x_platform_data *pdata)
459 {
460         struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
461                                 : chip->companion;
462         int data;
463         int ret;
464
465         /* initialize GPADC without activating it */
466
467         if (!pdata || !pdata->touch)
468                 return -EINVAL;
469
470         /* set GPADC MISC1 register */
471         data = 0;
472         data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
473         data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
474         data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
475         data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
476         if (data) {
477                 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
478                 if (ret < 0)
479                         goto out;
480         }
481         /* set tsi prebias time */
482         if (pdata->touch->tsi_prebias) {
483                 data = pdata->touch->tsi_prebias;
484                 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
485                 if (ret < 0)
486                         goto out;
487         }
488         /* set prebias & prechg time of pen detect */
489         data = 0;
490         data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
491         data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
492         if (data) {
493                 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
494                 if (ret < 0)
495                         goto out;
496         }
497
498         ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
499                               PM8607_GPADC_EN, PM8607_GPADC_EN);
500 out:
501         return ret;
502 }
503
504 static int __devinit device_irq_init(struct pm860x_chip *chip,
505                                      struct pm860x_platform_data *pdata)
506 {
507         struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
508                                 : chip->companion;
509         unsigned char status_buf[INT_STATUS_NUM];
510         unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
511         struct irq_desc *desc;
512         int i, data, mask, ret = -EINVAL;
513         int __irq;
514
515         if (!pdata || !pdata->irq_base) {
516                 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
517                 return -EINVAL;
518         }
519
520         mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
521                 | PM8607_B0_MISC1_INT_MASK;
522         data = 0;
523         chip->irq_mode = 0;
524         if (pdata && pdata->irq_mode) {
525                 /*
526                  * irq_mode defines the way of clearing interrupt. If it's 1,
527                  * clear IRQ by write. Otherwise, clear it by read.
528                  * This control bit is valid from 88PM8607 B0 steping.
529                  */
530                 data |= PM8607_B0_MISC1_INT_CLEAR;
531                 chip->irq_mode = 1;
532         }
533         ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
534         if (ret < 0)
535                 goto out;
536
537         /* mask all IRQs */
538         memset(status_buf, 0, INT_STATUS_NUM);
539         ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
540                                 INT_STATUS_NUM, status_buf);
541         if (ret < 0)
542                 goto out;
543
544         if (chip->irq_mode) {
545                 /* clear interrupt status by write */
546                 memset(status_buf, 0xFF, INT_STATUS_NUM);
547                 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
548                                         INT_STATUS_NUM, status_buf);
549         } else {
550                 /* clear interrupt status by read */
551                 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
552                                         INT_STATUS_NUM, status_buf);
553         }
554         if (ret < 0)
555                 goto out;
556
557         mutex_init(&chip->irq_lock);
558         chip->irq_base = pdata->irq_base;
559         chip->core_irq = i2c->irq;
560         if (!chip->core_irq)
561                 goto out;
562
563         desc = irq_to_desc(chip->core_irq);
564
565         /* register IRQ by genirq */
566         for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
567                 __irq = i + chip->irq_base;
568                 set_irq_chip_data(__irq, chip);
569                 set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
570                                          handle_edge_irq);
571                 set_irq_nested_thread(__irq, 1);
572 #ifdef CONFIG_ARM
573                 set_irq_flags(__irq, IRQF_VALID);
574 #else
575                 set_irq_noprobe(__irq);
576 #endif
577         }
578
579         ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
580                                    "88pm860x", chip);
581         if (ret) {
582                 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
583                 chip->core_irq = 0;
584         }
585
586         return 0;
587 out:
588         chip->core_irq = 0;
589         return ret;
590 }
591
592 static void device_irq_exit(struct pm860x_chip *chip)
593 {
594         if (chip->core_irq)
595                 free_irq(chip->core_irq, chip);
596 }
597
598 static void __devinit device_8606_init(struct pm860x_chip *chip,
599                                        struct i2c_client *i2c,
600                                        struct pm860x_platform_data *pdata)
601 {
602         int ret;
603
604         if (pdata && pdata->backlight) {
605                 ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
606                                       ARRAY_SIZE(backlight_devs),
607                                       &backlight_resources[0], 0);
608                 if (ret < 0) {
609                         dev_err(chip->dev, "Failed to add backlight "
610                                 "subdev\n");
611                         goto out_dev;
612                 }
613         }
614
615         if (pdata && pdata->led) {
616                 ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
617                                       ARRAY_SIZE(led_devs),
618                                       &led_resources[0], 0);
619                 if (ret < 0) {
620                         dev_err(chip->dev, "Failed to add led "
621                                 "subdev\n");
622                         goto out_dev;
623                 }
624         }
625         return;
626 out_dev:
627         mfd_remove_devices(chip->dev);
628         device_irq_exit(chip);
629 }
630
631 static void __devinit device_8607_init(struct pm860x_chip *chip,
632                                        struct i2c_client *i2c,
633                                        struct pm860x_platform_data *pdata)
634 {
635         int data, ret;
636
637         ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
638         if (ret < 0) {
639                 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
640                 goto out;
641         }
642         switch (ret & PM8607_VERSION_MASK) {
643         case 0x40:
644         case 0x50:
645                 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
646                          ret);
647                 break;
648         default:
649                 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
650                         "Chip ID: %02x\n", ret);
651                 goto out;
652         }
653
654         ret = pm860x_reg_read(i2c, PM8607_BUCK3);
655         if (ret < 0) {
656                 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
657                 goto out;
658         }
659         if (ret & PM8607_BUCK3_DOUBLE)
660                 chip->buck3_double = 1;
661
662         ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
663         if (ret < 0) {
664                 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
665                 goto out;
666         }
667
668         if (pdata && (pdata->i2c_port == PI2C_PORT))
669                 data = PM8607_B0_MISC1_PI2C;
670         else
671                 data = 0;
672         ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
673         if (ret < 0) {
674                 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
675                 goto out;
676         }
677
678         ret = device_gpadc_init(chip, pdata);
679         if (ret < 0)
680                 goto out;
681
682         ret = device_irq_init(chip, pdata);
683         if (ret < 0)
684                 goto out;
685
686         ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
687                               ARRAY_SIZE(regulator_devs),
688                               &regulator_resources[0], 0);
689         if (ret < 0) {
690                 dev_err(chip->dev, "Failed to add regulator subdev\n");
691                 goto out_dev;
692         }
693
694         if (pdata && pdata->touch) {
695                 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
696                                       ARRAY_SIZE(touch_devs),
697                                       &touch_resources[0], 0);
698                 if (ret < 0) {
699                         dev_err(chip->dev, "Failed to add touch "
700                                 "subdev\n");
701                         goto out_dev;
702                 }
703         }
704
705         if (pdata && pdata->power) {
706                 ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
707                                       ARRAY_SIZE(power_devs),
708                                       &power_supply_resources[0], 0);
709                 if (ret < 0) {
710                         dev_err(chip->dev, "Failed to add power supply "
711                                 "subdev\n");
712                         goto out_dev;
713                 }
714         }
715
716         ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
717                               ARRAY_SIZE(onkey_devs),
718                               &onkey_resources[0], 0);
719         if (ret < 0) {
720                 dev_err(chip->dev, "Failed to add onkey subdev\n");
721                 goto out_dev;
722         }
723
724         ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
725                               ARRAY_SIZE(codec_devs),
726                               &codec_resources[0], 0);
727         if (ret < 0) {
728                 dev_err(chip->dev, "Failed to add codec subdev\n");
729                 goto out_dev;
730         }
731         return;
732 out_dev:
733         mfd_remove_devices(chip->dev);
734         device_irq_exit(chip);
735 out:
736         return;
737 }
738
739 int __devinit pm860x_device_init(struct pm860x_chip *chip,
740                        struct pm860x_platform_data *pdata)
741 {
742         chip->core_irq = 0;
743
744         switch (chip->id) {
745         case CHIP_PM8606:
746                 device_8606_init(chip, chip->client, pdata);
747                 break;
748         case CHIP_PM8607:
749                 device_8607_init(chip, chip->client, pdata);
750                 break;
751         }
752
753         if (chip->companion) {
754                 switch (chip->id) {
755                 case CHIP_PM8607:
756                         device_8606_init(chip, chip->companion, pdata);
757                         break;
758                 case CHIP_PM8606:
759                         device_8607_init(chip, chip->companion, pdata);
760                         break;
761                 }
762         }
763
764         return 0;
765 }
766
767 void __devexit pm860x_device_exit(struct pm860x_chip *chip)
768 {
769         device_irq_exit(chip);
770         mfd_remove_devices(chip->dev);
771 }
772
773 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
774 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
775 MODULE_LICENSE("GPL");