]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/mfd/88pm860x-core.c
mfd: Adopt mfd_data in 88pm860x led
[mirror_ubuntu-artful-kernel.git] / drivers / mfd / 88pm860x-core.c
CommitLineData
bbd51b1f
HZ
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>
5c42e8c4 14#include <linux/i2c.h>
2afa62ea 15#include <linux/irq.h>
bbd51b1f
HZ
16#include <linux/interrupt.h>
17#include <linux/platform_device.h>
18#include <linux/mfd/core.h>
53dbab7a 19#include <linux/mfd/88pm860x.h>
bbd51b1f 20
2afa62ea
HZ
21#define INT_STATUS_NUM 3
22
adb70483
HZ
23static 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,},
a16122bc 27};
adb70483 28
3154c344
HZ
29static 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
adb70483
HZ
38static struct mfd_cell bk_devs[] __initdata = {
39 {"88pm860x-backlight", 0,},
40 {"88pm860x-backlight", 1,},
41 {"88pm860x-backlight", 2,},
42};
43
3154c344
HZ
44static 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,},
a16122bc
HZ
51};
52
3154c344
HZ
53static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)];
54static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)];
a16122bc
HZ
55
56static struct resource touch_resources[] = {
57 {
58 .start = PM8607_IRQ_PEN,
59 .end = PM8607_IRQ_PEN,
60 .flags = IORESOURCE_IRQ,
61 },
62};
63
64static struct mfd_cell touch_devs[] = {
65 {
66 .name = "88pm860x-touch",
67 .num_resources = 1,
68 .resources = &touch_resources[0],
69 },
70};
bbd51b1f
HZ
71
72#define PM8607_REG_RESOURCE(_start, _end) \
73{ \
74 .start = PM8607_##_start, \
75 .end = PM8607_##_end, \
76 .flags = IORESOURCE_IO, \
77}
78
2afa62ea
HZ
79static 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
88static 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
97static 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
106static 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
2c36af7b
HZ
115static 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
143static 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
a16122bc 152static struct resource regulator_resources[] = {
bbd51b1f
HZ
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),
9f79e9db 167 PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET),
bbd51b1f
HZ
168 PM8607_REG_RESOURCE(LDO14, LDO14),
169};
170
192bbb95 171#define PM8607_REG_DEVS(_id) \
bbd51b1f 172{ \
192bbb95 173 .name = "88pm860x-regulator", \
bbd51b1f 174 .num_resources = 1, \
a16122bc
HZ
175 .resources = &regulator_resources[PM8607_ID_##_id], \
176 .id = PM8607_ID_##_id, \
bbd51b1f
HZ
177}
178
a16122bc 179static struct mfd_cell regulator_devs[] = {
192bbb95
HZ
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),
9f79e9db 194 PM8607_REG_DEVS(LDO13),
192bbb95 195 PM8607_REG_DEVS(LDO14),
bbd51b1f
HZ
196};
197
2afa62ea
HZ
198struct 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};
5c42e8c4 204
2afa62ea
HZ
205static 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};
5c42e8c4 317
2afa62ea 318static irqreturn_t pm860x_irq(int irq, void *data)
5c42e8c4 319{
5c42e8c4 320 struct pm860x_chip *chip = data;
2afa62ea
HZ
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);
5c42e8c4 332 }
2afa62ea
HZ
333 if (value & irq_data->enable)
334 handle_nested_irq(chip->irq_base + i);
5c42e8c4 335 }
5c42e8c4
HZ
336 return IRQ_HANDLED;
337}
338
49f89d9a 339static void pm860x_irq_lock(struct irq_data *data)
53dbab7a 340{
49f89d9a 341 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
5c42e8c4
HZ
342
343 mutex_lock(&chip->irq_lock);
53dbab7a
HZ
344}
345
49f89d9a 346static void pm860x_irq_sync_unlock(struct irq_data *data)
bbd51b1f 347{
49f89d9a 348 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
2afa62ea
HZ
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 }
5c42e8c4 386
5c42e8c4 387 mutex_unlock(&chip->irq_lock);
2afa62ea 388}
5c42e8c4 389
49f89d9a 390static void pm860x_irq_enable(struct irq_data *data)
2afa62ea 391{
49f89d9a
MB
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;
5c42e8c4 395}
2afa62ea 396
49f89d9a 397static void pm860x_irq_disable(struct irq_data *data)
2afa62ea 398{
49f89d9a
MB
399 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
400 pm860x_irqs[data->irq - chip->irq_base].enable = 0;
2afa62ea
HZ
401}
402
403static struct irq_chip pm860x_irq_chip = {
404 .name = "88pm860x",
49f89d9a
MB
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,
2afa62ea 409};
5c42e8c4 410
a16122bc
HZ
411static 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;
eb6e8ddf
DC
416 int data;
417 int ret;
a16122bc
HZ
418
419 /* initialize GPADC without activating it */
420
eb6e8ddf
DC
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;
a16122bc 434 }
eb6e8ddf
DC
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;
a16122bc 450 }
eb6e8ddf
DC
451
452 ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
453 PM8607_GPADC_EN, PM8607_GPADC_EN);
a16122bc
HZ
454out:
455 return ret;
456}
457
5c42e8c4
HZ
458static 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];
2afa62ea
HZ
464 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
465 struct irq_desc *desc;
466 int i, data, mask, ret = -EINVAL;
467 int __irq;
5c42e8c4 468
2afa62ea
HZ
469 if (!pdata || !pdata->irq_base) {
470 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
471 return -EINVAL;
472 }
5c42e8c4
HZ
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
2afa62ea
HZ
511 mutex_init(&chip->irq_lock);
512 chip->irq_base = pdata->irq_base;
513 chip->core_irq = i2c->irq;
514 if (!chip->core_irq)
5c42e8c4 515 goto out;
2afa62ea
HZ
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
5c42e8c4 531 }
2afa62ea
HZ
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
5c42e8c4
HZ
540 return 0;
541out:
2afa62ea 542 chip->core_irq = 0;
5c42e8c4
HZ
543 return ret;
544}
545
872c1b14 546static void device_irq_exit(struct pm860x_chip *chip)
5c42e8c4 547{
2afa62ea
HZ
548 if (chip->core_irq)
549 free_irq(chip->core_irq, chip);
5c42e8c4
HZ
550}
551
adb70483
HZ
552static 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
3154c344
HZ
589static void __devinit device_led_init(struct pm860x_chip *chip,
590 struct i2c_client *i2c,
591 struct pm860x_platform_data *pdata)
5c42e8c4 592{
a16122bc 593 int ret;
3154c344 594 int i, j, id;
a16122bc 595
3154c344
HZ
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 }
a16122bc
HZ
622 }
623 }
5c42e8c4
HZ
624}
625
626static void __devinit device_8607_init(struct pm860x_chip *chip,
627 struct i2c_client *i2c,
628 struct pm860x_platform_data *pdata)
629{
a16122bc 630 int data, ret;
bbd51b1f 631
53dbab7a 632 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
bbd51b1f
HZ
633 if (ret < 0) {
634 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
635 goto out;
636 }
38b34052
HZ
637 switch (ret & PM8607_VERSION_MASK) {
638 case 0x40:
639 case 0x50:
bbd51b1f
HZ
640 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
641 ret);
38b34052
HZ
642 break;
643 default:
bbd51b1f
HZ
644 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
645 "Chip ID: %02x\n", ret);
646 goto out;
647 }
bbd51b1f 648
53dbab7a 649 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
bbd51b1f
HZ
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
5c42e8c4 657 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
bbd51b1f
HZ
658 if (ret < 0) {
659 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
660 goto out;
661 }
bbd51b1f 662
5c42e8c4
HZ
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
a16122bc
HZ
673 ret = device_gpadc_init(chip, pdata);
674 if (ret < 0)
675 goto out;
676
5c42e8c4
HZ
677 ret = device_irq_init(chip, pdata);
678 if (ret < 0)
679 goto out;
680
a16122bc
HZ
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;
bbd51b1f
HZ
697 }
698 }
2afa62ea
HZ
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
2c36af7b
HZ
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 }
a16122bc
HZ
726 return;
727out_dev:
728 mfd_remove_devices(chip->dev);
729 device_irq_exit(chip);
bbd51b1f 730out:
53dbab7a
HZ
731 return;
732}
733
872c1b14 734int __devinit pm860x_device_init(struct pm860x_chip *chip,
53dbab7a
HZ
735 struct pm860x_platform_data *pdata)
736{
2afa62ea 737 chip->core_irq = 0;
5c42e8c4 738
53dbab7a
HZ
739 switch (chip->id) {
740 case CHIP_PM8606:
adb70483 741 device_bk_init(chip, chip->client, pdata);
3154c344 742 device_led_init(chip, chip->client, pdata);
53dbab7a
HZ
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:
adb70483 752 device_bk_init(chip, chip->companion, pdata);
3154c344 753 device_led_init(chip, chip->companion, pdata);
53dbab7a
HZ
754 break;
755 case CHIP_PM8606:
756 device_8607_init(chip, chip->companion, pdata);
757 break;
758 }
759 }
5c42e8c4 760
53dbab7a 761 return 0;
bbd51b1f
HZ
762}
763
872c1b14 764void __devexit pm860x_device_exit(struct pm860x_chip *chip)
bbd51b1f 765{
5c42e8c4 766 device_irq_exit(chip);
bbd51b1f
HZ
767 mfd_remove_devices(chip->dev);
768}
769
53dbab7a 770MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
bbd51b1f
HZ
771MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
772MODULE_LICENSE("GPL");