power: smb349: Addition of interrupt support
Syed Rafiuddin [Thu, 1 Mar 2012 15:40:44 +0000 (20:40 +0530)]
-Addition of interrupt support to update charger
properties to fuel-gauge driver.

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

drivers/power/smb349-charger.c
include/linux/smb349-charger.h

index b3db989..6a4c738 100644 (file)
@@ -32,6 +32,9 @@
 #include <linux/regulator/machine.h>
 #include <linux/smb349-charger.h>
 #include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
 
 #define SMB349_CHARGE          0x00
 #define SMB349_CHRG_CRNTS      0x01
 #define SMB349_STS_REG_D       0x3E
 #define SMB349_STS_REG_E       0x3F
 
+#define SMB349_ENABLE_WRITE    1
+#define SMB349_DISABLE_WRITE   0
 #define ENABLE_WRT_ACCESS      0x80
 #define THERM_CTRL             0x10
 #define BATTERY_MISSING                0x10
 #define CHARGING               0x06
 #define DEDICATED_CHARGER      0x04
+#define CHRG_DOWNSTRM_PORT     0x08
 #define ENABLE_CHARGE          0x02
 
 static struct smb349_charger *charger;
@@ -120,21 +126,145 @@ static int smb349_update_reg(struct i2c_client *client, int reg, u8 value)
        return ret;
 }
 
-int smb349_volatile_writes(struct i2c_client *client)
+int smb349_volatile_writes(struct i2c_client *client, uint8_t value)
 {
        int ret = 0;
 
-       /* Enable volatile write to config registers*/
-       ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_WRT_ACCESS);
+       if (value == SMB349_ENABLE_WRITE) {
+               /* Enable volatile write to config registers */
+               ret = smb349_update_reg(client, SMB349_CMD_REG,
+                                               ENABLE_WRT_ACCESS);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s(): Failed in writing"
+                               "register 0x%02x\n", __func__, SMB349_CMD_REG);
+                       return ret;
+               }
+       } else {
+               ret = smb349_read(client, SMB349_CMD_REG);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       return ret;
+               }
+
+               ret = smb349_write(client, SMB349_CMD_REG, ret & (~(1<<7)));
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+static void smb349_clear_interrupts(struct i2c_client *client)
+{
+       uint8_t val, buf[6];
+
+       val = i2c_smbus_read_i2c_block_data(client, SMB349_INTR_STS_A, 6, buf);
+       if (val < 0)
+               dev_err(&client->dev, "%s(): Failed in clearing interrupts\n",
+                                                               __func__);
+}
+
+static int smb349_configure_charger(struct i2c_client *client)
+{
+       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;
+       }
+
+        /* 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);
-               return ret;
+               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;
+       }
+
+       /* Disable volatile writes to registers */
+       ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE);
+       if (ret < 0) {
+               dev_err(&client->dev, "%s() error in configuring charger..\n",
+                                                               __func__);
+               goto error;
+       }
+error:
        return ret;
 }
 
+static irqreturn_t smb349_status_isr(int irq, void *dev_id)
+{
+       struct i2c_client *client = charger->client;
+       int ret, val;
+
+       val =  smb349_read(client, SMB349_STS_REG_D);
+       if (val < 0) {
+               dev_err(&client->dev, "%s(): Failed in reading register"
+                               "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);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s() error in configuring"
+                               "charger..\n", __func__);
+                       goto irq_error;
+               }
+
+               charger->state = progress;
+       } else {
+               charger->state = stopped;
+
+               ret = smb349_read(client, SMB349_CMD_REG);
+               if (ret < 0) {
+                       dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+                       goto irq_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 irq_error;
+               }
+       }
+
+       if (charger->charger_cb)
+               charger->charger_cb(charger->state, charger->chrg_type,
+                                               charger->charger_cb_data);
+irq_error:
+       smb349_clear_interrupts(client);
+       return IRQ_HANDLED;
+}
+
+int register_callback(charging_callback_t cb, void *args)
+{
+       struct smb349_charger *charger_data = charger;
+       if (!charger_data)
+               return -ENODEV;
+
+       charger_data->charger_cb = cb;
+       charger_data->charger_cb_data = args;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_callback);
+
 int smb349_battery_online(void)
 {
        int val;
@@ -151,50 +281,49 @@ int smb349_battery_online(void)
        else
                return 1;
 }
-EXPORT_SYMBOL_GPL(smb349_battery_online);
 
-int smb349_charging_status(void)
+static int smb349_configure_interrupts(struct i2c_client *client)
 {
-       int val;
-       struct i2c_client *client = charger->client;
+       int ret = 0;
 
-       val = smb349_read(charger->client, SMB349_STS_REG_C);
-       if (val < 0) {
-               dev_err(&client->dev, "%s(): Failed in reading register"
-                               "0x%02x\n", __func__, SMB349_STS_REG_C);
-               return val;
+       /* 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 (val & CHARGING)
-               return 1;
-       else
-               return 0;
-}
-EXPORT_SYMBOL_GPL(smb349_charging_status);
 
-int smb349_charger_type(void)
-{
-       int val;
-       struct i2c_client *client = charger->client;
+       ret = smb349_update_reg(client, SMB349_FAULT_INTR, 0xff);
+       if (ret < 0) {
+               dev_err(&client->dev, "%s(): Failed in writing register"
+                               "0x%02x\n", __func__, SMB349_CMD_REG);
+               goto error;
+       }
 
-       val = smb349_read(charger->client, SMB349_STS_REG_D);
-       if (val < 0) {
-               dev_err(&client->dev, "%s(): Failed in reading register"
-                               "0x%02x\n", __func__, SMB349_STS_REG_D);
-               return val;
+       ret = smb349_update_reg(client, SMB349_STS_INTR_1, 0xff);
+       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) {
+               dev_err(&client->dev, "%s() error in configuring charger..\n",
+                                                               __func__);
+               goto error;
        }
-       if (val & DEDICATED_CHARGER)
-               return 1;
-       else
-               return 0;
 
+error:
+       return ret;
 }
-EXPORT_SYMBOL_GPL(smb349_charger_type);
 
 static int __devinit smb349_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       int ret;
+       int ret, irq_num;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
                return -EIO;
@@ -207,33 +336,27 @@ static int __devinit smb349_probe(struct i2c_client *client,
        charger->dev = &client->dev;
        i2c_set_clientdata(client, charger);
 
-       /* Check battery presence*/
+       /* Check battery presence */
        if (!smb349_battery_online()) {
                dev_err(&client->dev, "%s() No Battery present, exiting..\n",
                                        __func__);
                goto error;
        }
 
-       /*Enable volatile writes to registers*/
-       ret = smb349_volatile_writes(client);
+       ret = smb349_configure_interrupts(client);
        if (ret < 0) {
                dev_err(&client->dev, "%s() error in configuring charger..\n",
                                                                __func__);
                goto error;
        }
 
-       /* Configure THERM ctrl*/
-       smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL);
-       if (ret < 0) {
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-               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"
-                               "0x%02x\n", __func__, SMB349_CMD_REG);
+       irq_num = gpio_to_irq(client->irq);
+       ret = request_threaded_irq(irq_num,
+                       NULL, smb349_status_isr, IRQ_TYPE_EDGE_FALLING,
+                               "smb349", charger);
+       if (ret) {
+               dev_err(&client->dev, "%s(): Failed in requesting isr\n",
+                               __func__);
                goto error;
        }
 
index 7c48a73..e83a678 100644 (file)
 
 #include <linux/regulator/machine.h>
 
+enum charging_states {
+       idle,
+       progress,
+       completed,
+       stopped,
+};
+
+enum charger_type {
+       AC,
+       USB,
+};
+
+typedef void (*charging_callback_t)(enum charging_states state,
+enum charger_type chrg_type, void *args);
+
 struct smb349_charger {
        struct i2c_client       *client;
        struct device   *dev;
+       void    *charger_cb_data;
+       enum charging_states state;
+       enum charger_type chrg_type;
+       charging_callback_t     charger_cb;
 };
 
+int smb349_battery_online(void);
 /*
- * Register the callback function for the client.
+ * Register callback function for the client.
+ * Used by fuel-gauge driver to get battery charging properties.
  */
-extern int smb349_battery_online(void);
-extern int smb349_charging_status(void);
-extern int smb349_charger_type(void);
+extern int register_callback(charging_callback_t cb, void *args);
 
 #endif /*__LINUX_SMB349_CHARGER_H */