Skip to content

Commit

Permalink
mfd: Add power control interface for pm8606 chip
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jett.Zhou authored and Samuel Ortiz committed Mar 6, 2012
1 parent fe2afaa commit 23de435
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
95 changes: 95 additions & 0 deletions drivers/mfd/88pm860x-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,99 @@ static void device_irq_exit(struct pm860x_chip *chip)
free_irq(chip->core_irq, chip);
}

int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
{
int ret = -EIO;
struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
chip->client : chip->companion;

dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
__func__, chip->osc_vote,
chip->osc_status);

mutex_lock(&chip->osc_lock);
/* Update voting status */
chip->osc_vote |= client;
/* If reference group is off - turn on*/
if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
/* Enable Reference group Vsys */
if (pm860x_set_bits(i2c, PM8606_VSYS,
PM8606_VSYS_EN, PM8606_VSYS_EN))
goto out;

/*Enable Internal Oscillator */
if (pm860x_set_bits(i2c, PM8606_MISC,
PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
goto out;
/* Update status (only if writes succeed) */
chip->osc_status = PM8606_REF_GP_OSC_ON;
}
mutex_unlock(&chip->osc_lock);

dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
__func__, chip->osc_vote,
chip->osc_status, ret);
return 0;
out:
mutex_unlock(&chip->osc_lock);
return ret;
}

int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
{
int ret = -EIO;
struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
chip->client : chip->companion;

dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
__func__, chip->osc_vote,
chip->osc_status);

mutex_lock(&chip->osc_lock);
/*Update voting status */
chip->osc_vote &= ~(client);
/* If reference group is off and this is the last client to release
* - turn off */
if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
(chip->osc_vote == REF_GP_NO_CLIENTS)) {
chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
/* Disable Reference group Vsys */
if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
goto out;
/* Disable Internal Oscillator */
if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
goto out;
chip->osc_status = PM8606_REF_GP_OSC_OFF;
}
mutex_unlock(&chip->osc_lock);

dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
__func__, chip->osc_vote,
chip->osc_status, ret);
return 0;
out:
mutex_unlock(&chip->osc_lock);
return ret;
}

static void __devinit device_osc_init(struct i2c_client *i2c)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);

mutex_init(&chip->osc_lock);
/* init portofino reference group voting and status */
/* Disable Reference group Vsys */
pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
/* Disable Internal Oscillator */
pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);

chip->osc_vote = REF_GP_NO_CLIENTS;
chip->osc_status = PM8606_REF_GP_OSC_OFF;
}

static void __devinit device_bk_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
Expand Down Expand Up @@ -774,6 +867,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,

switch (chip->id) {
case CHIP_PM8606:
device_osc_init(chip->client);
device_bk_init(chip, pdata);
device_led_init(chip, pdata);
break;
Expand All @@ -785,6 +879,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,
if (chip->companion) {
switch (chip->id) {
case CHIP_PM8607:
device_osc_init(chip->companion);
device_bk_init(chip, pdata);
device_led_init(chip, pdata);
break;
Expand Down
22 changes: 22 additions & 0 deletions include/linux/mfd/88pm860x.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,22 @@ enum {
#define PM8607_PD_PREBIAS_MASK (0x1F << 0)
#define PM8607_PD_PRECHG_MASK (7 << 5)

#define PM8606_REF_GP_OSC_OFF 0
#define PM8606_REF_GP_OSC_ON 1
#define PM8606_REF_GP_OSC_UNKNOWN 2

/* Clients of reference group and 8MHz oscillator in 88PM8606 */
enum pm8606_ref_gp_and_osc_clients {
REF_GP_NO_CLIENTS = 0,
WLED1_DUTY = (1<<0), /*PF 0x02.7:0*/
WLED2_DUTY = (1<<1), /*PF 0x04.7:0*/
WLED3_DUTY = (1<<2), /*PF 0x06.7:0*/
RGB1_ENABLE = (1<<3), /*PF 0x07.1*/
RGB2_ENABLE = (1<<4), /*PF 0x07.2*/
LDO_VBR_EN = (1<<5), /*PF 0x12.0*/
REF_GP_MAX_CLIENT = 0xFFFF
};

/* Interrupt Number in 88PM8607 */
enum {
PM8607_IRQ_ONKEY,
Expand Down Expand Up @@ -298,18 +314,21 @@ enum {
struct pm860x_chip {
struct device *dev;
struct mutex irq_lock;
struct mutex osc_lock;
struct i2c_client *client;
struct i2c_client *companion; /* companion chip client */
struct regmap *regmap;
struct regmap *regmap_companion;

int buck3_double; /* DVC ramp slope double */
unsigned short companion_addr;
unsigned short osc_vote;
int id;
int irq_mode;
int irq_base;
int core_irq;
unsigned char chip_version;
unsigned char osc_status;

unsigned int wakeup_flag;
};
Expand Down Expand Up @@ -370,6 +389,9 @@ struct pm860x_platform_data {
int num_regulators;
};

extern int pm8606_osc_enable(struct pm860x_chip *, unsigned short);
extern int pm8606_osc_disable(struct pm860x_chip *, unsigned short);

extern int pm860x_reg_read(struct i2c_client *, int);
extern int pm860x_reg_write(struct i2c_client *, int, unsigned char);
extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *);
Expand Down

0 comments on commit 23de435

Please sign in to comment.