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