summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)
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)
{
@@ -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;
@@ -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;
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 {
#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,
@@ -298,6 +314,7 @@ 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;
@@ -305,11 +322,13 @@ struct pm860x_chip {
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;
};
@@ -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 *);