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 = ®ulator_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, ®ulator_devs[0], 682 ARRAY_SIZE(regulator_devs), 683 ®ulator_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"); 773