power: smb349: Enable OTG support
Syed Rafiuddin [Wed, 7 Mar 2012 09:54:14 +0000 (14:54 +0530)]
Addition of OTG support in smb349 charger driver

Change-Id: Ib38c9f4c06285ae07d93cfa3c6f5e1637aaa9460
Signed-off-by: Syed Rafiuddin <srafiuddin@nvidia.com>
Reviewed-on: http://git-master/r/86936
Reviewed-by: Sachin Nikam <snikam@nvidia.com>

drivers/power/smb349-charger.c
drivers/usb/otg/tegra-otg.c
include/linux/smb349-charger.h

index 6a4c738..880fa04 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/usb/otg.h>
 
 #define SMB349_CHARGE          0x00
 #define SMB349_CHRG_CRNTS      0x01
@@ -82,6 +83,8 @@
 #define ENABLE_CHARGE          0x02
 
 static struct smb349_charger *charger;
+static int smb349_configure_charger(struct i2c_client *client, int value);
+static int smb349_configure_interrupts(struct i2c_client *client);
 
 static int smb349_read(struct i2c_client *client, int reg)
 {
@@ -165,33 +168,93 @@ static void smb349_clear_interrupts(struct i2c_client *client)
                                                                __func__);
 }
 
-static int smb349_configure_charger(struct i2c_client *client)
+static int smb349_configure_otg(struct i2c_client *client, int enable)
 {
        int ret = 0;
 
-       /* Enable volatile writes to registers */
+       /*Enable volatile writes to registers*/
        ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE);
        if (ret < 0) {
-               dev_err(&client->dev, "%s() error in configuring charger..\n",
+               dev_err(&client->dev, "%s error in configuring otg..\n",
                                                                __func__);
                goto error;
        }
 
-        /* Enable charging */
-       ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE);
-       if (ret < 0) {
-               dev_err(&client->dev, "%s(): Failed in writing register"
+       if (enable) {
+               /* Enable OTG */
+              ret = smb349_update_reg(client, SMB349_CMD_REG, 0x10);
+              if (ret < 0) {
+                      dev_err(&client->dev, "%s: Failed in writing register"
                                "0x%02x\n", __func__, SMB349_CMD_REG);
-               goto error;
+                       goto error;
+              }
+
+       } else {
+              /* Disable OTG */
+              ret = smb349_read(client, SMB349_CMD_REG);
+              if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto error;
+              }
+
+              ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<4))));
+              if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto error;
+              }
        }
 
-       /* Configure THERM ctrl */
-       ret = smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL);
+       /* Disable volatile writes to registers */
+       ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE);
        if (ret < 0) {
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+               dev_err(&client->dev, "%s error in configuring OTG..\n",
+                                                               __func__);
+              goto error;
+       }
+error:
+       return ret;
+}
+
+static int smb349_configure_charger(struct i2c_client *client, int value)
+{
+       int ret = 0;
+
+       /* Enable volatile writes to registers */
+       ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE);
+       if (ret < 0) {
+               dev_err(&client->dev, "%s() error in configuring charger..\n",
+                                                               __func__);
                goto error;
        }
 
+       if (value) {
+                /* Enable charging */
+               ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s(): Failed in writing register"
+                                       "0x%02x\n", __func__, SMB349_CMD_REG);
+                       goto error;
+               }
+
+               /* Configure THERM ctrl */
+               ret = smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto error;
+               }
+       } else {
+               ret = smb349_read(client, SMB349_CMD_REG);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto error;
+               }
+
+               ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<1))));
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto error;
+               }
+       }
        /* Disable volatile writes to registers */
        ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE);
        if (ret < 0) {
@@ -214,14 +277,13 @@ static irqreturn_t smb349_status_isr(int irq, void *dev_id)
                                "0x%02x\n", __func__, SMB349_STS_REG_D);
                goto irq_error;
        } else if (val != 0) {
-
                if (val & DEDICATED_CHARGER)
                        charger->chrg_type = AC;
                else if (val & CHRG_DOWNSTRM_PORT)
                        charger->chrg_type = USB;
 
                /* configure charger */
-               ret = smb349_configure_charger(client);
+               ret = smb349_configure_charger(client, 1);
                if (ret < 0) {
                        dev_err(&client->dev, "%s() error in configuring"
                                "charger..\n", __func__);
@@ -232,17 +294,21 @@ static irqreturn_t smb349_status_isr(int irq, void *dev_id)
        } else {
                charger->state = stopped;
 
-               ret = smb349_read(client, SMB349_CMD_REG);
+               /* Disable charger */
+               ret = smb349_configure_charger(client, 0);
                if (ret < 0) {
-                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "charger..\n", __func__);
                        goto irq_error;
                }
 
-               ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<1))));
+               ret = smb349_configure_interrupts(client);
                if (ret < 0) {
-                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "charger..\n", __func__);
                        goto irq_error;
                }
+
        }
 
        if (charger->charger_cb)
@@ -319,6 +385,46 @@ error:
        return ret;
 }
 
+static void smb349_otg_status(enum usb_otg_state otg_state, void *data)
+{
+       struct i2c_client *client = charger->client;
+       int ret;
+
+       if (otg_state == OTG_STATE_A_HOST) {
+
+               /* configure charger */
+               ret = smb349_configure_charger(client, 0);
+               if (ret < 0)
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "otg..\n", __func__);
+
+               /* ENABLE OTG */
+               ret = smb349_configure_otg(client, 1);
+               if (ret < 0)
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "otg..\n", __func__);
+
+       } else if (otg_state == OTG_STATE_A_SUSPEND) {
+
+               /* Disable OTG */
+               ret = smb349_configure_otg(client, 0);
+               if (ret < 0)
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "otg..\n", __func__);
+
+               /* configure charger */
+               ret = smb349_configure_charger(client, 1);
+               if (ret < 0)
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "otg..\n", __func__);
+
+               ret = smb349_configure_interrupts(client);
+               if (ret < 0)
+                       dev_err(&client->dev, "%s() error in configuring"
+                                               "otg..\n", __func__);
+       }
+}
+
 static int __devinit smb349_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
@@ -343,6 +449,14 @@ static int __devinit smb349_probe(struct i2c_client *client,
                goto error;
        }
 
+       ret = register_otg_callback(smb349_otg_status, charger);
+       if (ret < 0)
+               goto error;
+
+       ret = smb349_configure_charger(client, 1);
+       if (ret < 0)
+               return ret;
+
        ret = smb349_configure_interrupts(client);
        if (ret < 0) {
                dev_err(&client->dev, "%s() error in configuring charger..\n",
@@ -359,7 +473,6 @@ static int __devinit smb349_probe(struct i2c_client *client,
                                __func__);
                goto error;
        }
-
        return 0;
 error:
        kfree(charger);
index c13c9b6..ffbeede 100644 (file)
@@ -43,6 +43,8 @@
 #define  USB_VBUS_STATUS       (1 << 10)
 #define  USB_INTS              (USB_VBUS_INT_STATUS | USB_ID_INT_STATUS)
 
+typedef void (*callback_t)(enum usb_otg_state otg_state, void *args);
+
 struct tegra_otg_data {
        struct otg_transceiver otg;
        unsigned long int_status;
@@ -55,6 +57,9 @@ struct tegra_otg_data {
        unsigned int intr_reg_data;
        bool detect_vbus;
        bool clk_enabled;
+       callback_t      charger_cb;
+       void    *charger_cb_data;
+
 };
 static struct tegra_otg_data *tegra_clone;
 
@@ -161,6 +166,16 @@ void tegra_stop_host(struct tegra_otg_data *tegra)
        }
 }
 
+int register_otg_callback(callback_t cb, void *args)
+{
+       if (!tegra_clone)
+               return -ENODEV;
+       tegra_clone->charger_cb = cb;
+       tegra_clone->charger_cb_data = args;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_otg_callback);
+
 static void irq_work(struct work_struct *work)
 {
        struct tegra_otg_data *tegra =
@@ -209,6 +224,9 @@ static void irq_work(struct work_struct *work)
                dev_info(tegra->otg.dev, "%s --> %s\n", tegra_state_name(from),
                                              tegra_state_name(to));
 
+               if (tegra->charger_cb)
+                       tegra->charger_cb(to, tegra->charger_cb_data);
+
                if (to == OTG_STATE_A_SUSPEND) {
                        if (from == OTG_STATE_A_HOST)
                                tegra_stop_host(tegra);
@@ -222,6 +240,8 @@ static void irq_work(struct work_struct *work)
                        tegra_start_host(tegra);
                }
        }
+
+
        clk_disable(tegra->clk);
        tegra_otg_disable_clk();
 }
index e83a678..311b2f4 100644 (file)
@@ -50,10 +50,12 @@ struct smb349_charger {
 };
 
 int smb349_battery_online(void);
+typedef void (*callback_t)(enum usb_otg_state otg_state, void *args);
 /*
  * Register callback function for the client.
  * Used by fuel-gauge driver to get battery charging properties.
  */
 extern int register_callback(charging_callback_t cb, void *args);
+extern int register_otg_callback(callback_t cb, void *args);
 
 #endif /*__LINUX_SMB349_CHARGER_H */