Commit 23de435a59b37eda468472ac67179eee5ef10a07

Authored by Jett.Zhou
Committed by Samuel Ortiz
1 parent fe2afaa541

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>

Showing 2 changed files with 117 additions and 0 deletions Side-by-side Diff

drivers/mfd/88pm860x-core.c
... ... @@ -503,6 +503,99 @@
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 {
... ... @@ -774,6 +867,7 @@
774 867  
775 868 switch (chip->id) {
776 869 case CHIP_PM8606:
  870 + device_osc_init(chip->client);
777 871 device_bk_init(chip, pdata);
778 872 device_led_init(chip, pdata);
779 873 break;
... ... @@ -785,6 +879,7 @@
785 879 if (chip->companion) {
786 880 switch (chip->id) {
787 881 case CHIP_PM8607:
  882 + device_osc_init(chip->companion);
788 883 device_bk_init(chip, pdata);
789 884 device_led_init(chip, pdata);
790 885 break;
include/linux/mfd/88pm860x.h
... ... @@ -263,6 +263,22 @@
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,
... ... @@ -298,6 +314,7 @@
298 314 struct pm860x_chip {
299 315 struct device *dev;
300 316 struct mutex irq_lock;
  317 + struct mutex osc_lock;
301 318 struct i2c_client *client;
302 319 struct i2c_client *companion; /* companion chip client */
303 320 struct regmap *regmap;
304 321  
... ... @@ -305,11 +322,13 @@
305 322  
306 323 int buck3_double; /* DVC ramp slope double */
307 324 unsigned short companion_addr;
  325 + unsigned short osc_vote;
308 326 int id;
309 327 int irq_mode;
310 328 int irq_base;
311 329 int core_irq;
312 330 unsigned char chip_version;
  331 + unsigned char osc_status;
313 332  
314 333 unsigned int wakeup_flag;
315 334 };
... ... @@ -369,6 +388,9 @@
369 388 int num_backlights;
370 389 int num_regulators;
371 390 };
  391 +
  392 +extern int pm8606_osc_enable(struct pm860x_chip *, unsigned short);
  393 +extern int pm8606_osc_disable(struct pm860x_chip *, unsigned short);
372 394  
373 395 extern int pm860x_reg_read(struct i2c_client *, int);
374 396 extern int pm860x_reg_write(struct i2c_client *, int, unsigned char);