aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJett.Zhou <jtzhou@marvell.com>2012-03-01 05:59:19 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2012-03-06 12:46:52 -0500
commit23de435a59b37eda468472ac67179eee5ef10a07 (patch)
treed0659611c578e0ae4779da35000ad510f1e426ba
parentfe2afaa5412126f7a41aec811228a1f439d232a0 (diff)
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>
-rw-r--r--drivers/mfd/88pm860x-core.c95
-rw-r--r--include/linux/mfd/88pm860x.h22
2 files changed, 117 insertions, 0 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 17dfe9bb6d27..78c3a9e1fa1e 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -503,6 +503,99 @@ static void device_irq_exit(struct pm860x_chip *chip)
503 free_irq(chip->core_irq, chip); 503 free_irq(chip->core_irq, chip);
504} 504}
505 505
506int 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;
541out:
542 mutex_unlock(&chip->osc_lock);
543 return ret;
544}
545
546int 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;
579out:
580 mutex_unlock(&chip->osc_lock);
581 return ret;
582}
583
584static 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
506static void __devinit device_bk_init(struct pm860x_chip *chip, 599static void __devinit device_bk_init(struct pm860x_chip *chip,
507 struct pm860x_platform_data *pdata) 600 struct pm860x_platform_data *pdata)
508{ 601{
@@ -774,6 +867,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,
774 867
775 switch (chip->id) { 868 switch (chip->id) {
776 case CHIP_PM8606: 869 case CHIP_PM8606:
870 device_osc_init(chip->client);
777 device_bk_init(chip, pdata); 871 device_bk_init(chip, pdata);
778 device_led_init(chip, pdata); 872 device_led_init(chip, pdata);
779 break; 873 break;
@@ -785,6 +879,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,
785 if (chip->companion) { 879 if (chip->companion) {
786 switch (chip->id) { 880 switch (chip->id) {
787 case CHIP_PM8607: 881 case CHIP_PM8607:
882 device_osc_init(chip->companion);
788 device_bk_init(chip, pdata); 883 device_bk_init(chip, pdata);
789 device_led_init(chip, pdata); 884 device_led_init(chip, pdata);
790 break; 885 break;
diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h
index 8b583f3f7926..84d071ade1d8 100644
--- a/include/linux/mfd/88pm860x.h
+++ b/include/linux/mfd/88pm860x.h
@@ -263,6 +263,22 @@ enum {
263#define PM8607_PD_PREBIAS_MASK (0x1F << 0) 263#define PM8607_PD_PREBIAS_MASK (0x1F << 0)
264#define PM8607_PD_PRECHG_MASK (7 << 5) 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 */
271enum 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/* Interrupt Number in 88PM8607 */ 282/* Interrupt Number in 88PM8607 */
267enum { 283enum {
268 PM8607_IRQ_ONKEY, 284 PM8607_IRQ_ONKEY,
@@ -298,6 +314,7 @@ enum {
298struct pm860x_chip { 314struct pm860x_chip {
299 struct device *dev; 315 struct device *dev;
300 struct mutex irq_lock; 316 struct mutex irq_lock;
317 struct mutex osc_lock;
301 struct i2c_client *client; 318 struct i2c_client *client;
302 struct i2c_client *companion; /* companion chip client */ 319 struct i2c_client *companion; /* companion chip client */
303 struct regmap *regmap; 320 struct regmap *regmap;
@@ -305,11 +322,13 @@ struct pm860x_chip {
305 322
306 int buck3_double; /* DVC ramp slope double */ 323 int buck3_double; /* DVC ramp slope double */
307 unsigned short companion_addr; 324 unsigned short companion_addr;
325 unsigned short osc_vote;
308 int id; 326 int id;
309 int irq_mode; 327 int irq_mode;
310 int irq_base; 328 int irq_base;
311 int core_irq; 329 int core_irq;
312 unsigned char chip_version; 330 unsigned char chip_version;
331 unsigned char osc_status;
313 332
314 unsigned int wakeup_flag; 333 unsigned int wakeup_flag;
315}; 334};
@@ -370,6 +389,9 @@ struct pm860x_platform_data {
370 int num_regulators; 389 int num_regulators;
371}; 390};
372 391
392extern int pm8606_osc_enable(struct pm860x_chip *, unsigned short);
393extern int pm8606_osc_disable(struct pm860x_chip *, unsigned short);
394
373extern int pm860x_reg_read(struct i2c_client *, int); 395extern int pm860x_reg_read(struct i2c_client *, int);
374extern int pm860x_reg_write(struct i2c_client *, int, unsigned char); 396extern int pm860x_reg_write(struct i2c_client *, int, unsigned char);
375extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *); 397extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *);