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