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