Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: Add power control interface for pm8606 chip

The reference group and internal oscillator are shared by sub-devs
like led, backlight and vibrator in PM8606 chip. Now introduce a
voting mechanism to enable/disable it.

Add pm8606_osc_enable() and pm8606_osc_disable() interface and
related defines to support this. This interface will be called by
vibrator led and backlight driver.The refernce group and internal
oscillator are enabled only when at least one of it's clients holds
it on or disabled only all the clients don't use it any more based
on the above mechanism.

Signed-off-by: Jett.Zhou <jtzhou@marvell.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Jett.Zhou and committed by
Samuel Ortiz
23de435a fe2afaa5

+117
+95
drivers/mfd/88pm860x-core.c
··· 503 503 free_irq(chip->core_irq, chip); 504 504 } 505 505 506 + int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client) 507 + { 508 + int ret = -EIO; 509 + struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? 510 + chip->client : chip->companion; 511 + 512 + dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); 513 + dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", 514 + __func__, chip->osc_vote, 515 + chip->osc_status); 516 + 517 + mutex_lock(&chip->osc_lock); 518 + /* Update voting status */ 519 + chip->osc_vote |= client; 520 + /* If reference group is off - turn on*/ 521 + if (chip->osc_status != PM8606_REF_GP_OSC_ON) { 522 + chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; 523 + /* Enable Reference group Vsys */ 524 + if (pm860x_set_bits(i2c, PM8606_VSYS, 525 + PM8606_VSYS_EN, PM8606_VSYS_EN)) 526 + goto out; 527 + 528 + /*Enable Internal Oscillator */ 529 + if (pm860x_set_bits(i2c, PM8606_MISC, 530 + PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN)) 531 + goto out; 532 + /* Update status (only if writes succeed) */ 533 + chip->osc_status = PM8606_REF_GP_OSC_ON; 534 + } 535 + mutex_unlock(&chip->osc_lock); 536 + 537 + dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", 538 + __func__, chip->osc_vote, 539 + chip->osc_status, ret); 540 + return 0; 541 + out: 542 + mutex_unlock(&chip->osc_lock); 543 + return ret; 544 + } 545 + 546 + int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client) 547 + { 548 + int ret = -EIO; 549 + struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? 550 + chip->client : chip->companion; 551 + 552 + dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); 553 + dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", 554 + __func__, chip->osc_vote, 555 + chip->osc_status); 556 + 557 + mutex_lock(&chip->osc_lock); 558 + /*Update voting status */ 559 + chip->osc_vote &= ~(client); 560 + /* If reference group is off and this is the last client to release 561 + * - turn off */ 562 + if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) && 563 + (chip->osc_vote == REF_GP_NO_CLIENTS)) { 564 + chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; 565 + /* Disable Reference group Vsys */ 566 + if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0)) 567 + goto out; 568 + /* Disable Internal Oscillator */ 569 + if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0)) 570 + goto out; 571 + chip->osc_status = PM8606_REF_GP_OSC_OFF; 572 + } 573 + mutex_unlock(&chip->osc_lock); 574 + 575 + dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", 576 + __func__, chip->osc_vote, 577 + chip->osc_status, ret); 578 + return 0; 579 + out: 580 + mutex_unlock(&chip->osc_lock); 581 + return ret; 582 + } 583 + 584 + static void __devinit device_osc_init(struct i2c_client *i2c) 585 + { 586 + struct pm860x_chip *chip = i2c_get_clientdata(i2c); 587 + 588 + mutex_init(&chip->osc_lock); 589 + /* init portofino reference group voting and status */ 590 + /* Disable Reference group Vsys */ 591 + pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0); 592 + /* Disable Internal Oscillator */ 593 + pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0); 594 + 595 + chip->osc_vote = REF_GP_NO_CLIENTS; 596 + chip->osc_status = PM8606_REF_GP_OSC_OFF; 597 + } 598 + 506 599 static void __devinit device_bk_init(struct pm860x_chip *chip, 507 600 struct pm860x_platform_data *pdata) 508 601 { ··· 867 774 868 775 switch (chip->id) { 869 776 case CHIP_PM8606: 777 + device_osc_init(chip->client); 870 778 device_bk_init(chip, pdata); 871 779 device_led_init(chip, pdata); 872 780 break; ··· 879 785 if (chip->companion) { 880 786 switch (chip->id) { 881 787 case CHIP_PM8607: 788 + device_osc_init(chip->companion); 882 789 device_bk_init(chip, pdata); 883 790 device_led_init(chip, pdata); 884 791 break;
+22
include/linux/mfd/88pm860x.h
··· 263 263 #define PM8607_PD_PREBIAS_MASK (0x1F << 0) 264 264 #define PM8607_PD_PRECHG_MASK (7 << 5) 265 265 266 + #define PM8606_REF_GP_OSC_OFF 0 267 + #define PM8606_REF_GP_OSC_ON 1 268 + #define PM8606_REF_GP_OSC_UNKNOWN 2 269 + 270 + /* Clients of reference group and 8MHz oscillator in 88PM8606 */ 271 + enum pm8606_ref_gp_and_osc_clients { 272 + REF_GP_NO_CLIENTS = 0, 273 + WLED1_DUTY = (1<<0), /*PF 0x02.7:0*/ 274 + WLED2_DUTY = (1<<1), /*PF 0x04.7:0*/ 275 + WLED3_DUTY = (1<<2), /*PF 0x06.7:0*/ 276 + RGB1_ENABLE = (1<<3), /*PF 0x07.1*/ 277 + RGB2_ENABLE = (1<<4), /*PF 0x07.2*/ 278 + LDO_VBR_EN = (1<<5), /*PF 0x12.0*/ 279 + REF_GP_MAX_CLIENT = 0xFFFF 280 + }; 281 + 266 282 /* Interrupt Number in 88PM8607 */ 267 283 enum { 268 284 PM8607_IRQ_ONKEY, ··· 314 298 struct pm860x_chip { 315 299 struct device *dev; 316 300 struct mutex irq_lock; 301 + struct mutex osc_lock; 317 302 struct i2c_client *client; 318 303 struct i2c_client *companion; /* companion chip client */ 319 304 struct regmap *regmap; ··· 322 305 323 306 int buck3_double; /* DVC ramp slope double */ 324 307 unsigned short companion_addr; 308 + unsigned short osc_vote; 325 309 int id; 326 310 int irq_mode; 327 311 int irq_base; 328 312 int core_irq; 329 313 unsigned char chip_version; 314 + unsigned char osc_status; 330 315 331 316 unsigned int wakeup_flag; 332 317 }; ··· 388 369 int num_backlights; 389 370 int num_regulators; 390 371 }; 372 + 373 + extern int pm8606_osc_enable(struct pm860x_chip *, unsigned short); 374 + extern int pm8606_osc_disable(struct pm860x_chip *, unsigned short); 391 375 392 376 extern int pm860x_reg_read(struct i2c_client *, int); 393 377 extern int pm860x_reg_write(struct i2c_client *, int, unsigned char);