Merge 3.7-rc1 into tty-linus
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / power / lp8727_charger.c
index 6a364f4798f78765b40298451474a876e10f71da..c628224b7f58788b94df718fb087c1b56e5a03ed 100644 (file)
 #include <linux/power_supply.h>
 #include <linux/platform_data/lp8727.h>
 
-#define DEBOUNCE_MSEC  270
+#define LP8788_NUM_INTREGS     2
+#define DEFAULT_DEBOUNCE_MSEC  270
 
 /* Registers */
-#define CTRL1          0x1
-#define CTRL2          0x2
-#define        SWCTRL          0x3
-#define INT1           0x4
-#define INT2           0x5
-#define STATUS1                0x6
-#define STATUS2                0x7
-#define CHGCTRL2       0x9
+#define LP8727_CTRL1           0x1
+#define LP8727_CTRL2           0x2
+#define LP8727_SWCTRL          0x3
+#define LP8727_INT1            0x4
+#define LP8727_INT2            0x5
+#define LP8727_STATUS1         0x6
+#define LP8727_STATUS2         0x7
+#define LP8727_CHGCTRL2                0x9
 
 /* CTRL1 register */
-#define CP_EN          (1 << 0)
-#define ADC_EN         (1 << 1)
-#define ID200_EN       (1 << 4)
+#define LP8727_CP_EN           BIT(0)
+#define LP8727_ADC_EN          BIT(1)
+#define LP8727_ID200_EN                BIT(4)
 
 /* CTRL2 register */
-#define CHGDET_EN      (1 << 1)
-#define INT_EN         (1 << 6)
+#define LP8727_CHGDET_EN       BIT(1)
+#define LP8727_INT_EN          BIT(6)
 
 /* SWCTRL register */
-#define SW_DM1_DM      (0x0 << 0)
-#define SW_DM1_U1      (0x1 << 0)
-#define SW_DM1_HiZ     (0x7 << 0)
-#define SW_DP2_DP      (0x0 << 3)
-#define SW_DP2_U2      (0x1 << 3)
-#define SW_DP2_HiZ     (0x7 << 3)
+#define LP8727_SW_DM1_DM       (0x0 << 0)
+#define LP8727_SW_DM1_HiZ      (0x7 << 0)
+#define LP8727_SW_DP2_DP       (0x0 << 3)
+#define LP8727_SW_DP2_HiZ      (0x7 << 3)
 
 /* INT1 register */
-#define IDNO           (0xF << 0)
-#define VBUS           (1 << 4)
+#define LP8727_IDNO            (0xF << 0)
+#define LP8727_VBUS            BIT(4)
 
 /* STATUS1 register */
-#define CHGSTAT                (3 << 4)
-#define CHPORT         (1 << 6)
-#define DCPORT         (1 << 7)
+#define LP8727_CHGSTAT         (3 << 4)
+#define LP8727_CHPORT          BIT(6)
+#define LP8727_DCPORT          BIT(7)
+#define LP8727_STAT_EOC                0x30
 
 /* STATUS2 register */
-#define TEMP_STAT      (3 << 5)
+#define LP8727_TEMP_STAT       (3 << 5)
+#define LP8727_TEMP_SHIFT      5
+
+/* CHGCTRL2 register */
+#define LP8727_ICHG_SHIFT      4
 
 enum lp8727_dev_id {
-       ID_NONE,
-       ID_TA,
-       ID_DEDICATED_CHG,
-       ID_USB_CHG,
-       ID_USB_DS,
-       ID_MAX,
+       LP8727_ID_NONE,
+       LP8727_ID_TA,
+       LP8727_ID_DEDICATED_CHG,
+       LP8727_ID_USB_CHG,
+       LP8727_ID_USB_DS,
+       LP8727_ID_MAX,
 };
 
-enum lp8727_chg_stat {
-       PRECHG,
-       CC,
-       CV,
-       EOC,
+enum lp8727_die_temp {
+       LP8788_TEMP_75C,
+       LP8788_TEMP_95C,
+       LP8788_TEMP_115C,
+       LP8788_TEMP_135C,
 };
 
 struct lp8727_psy {
@@ -84,12 +88,17 @@ struct lp8727_chg {
        struct device *dev;
        struct i2c_client *client;
        struct mutex xfer_lock;
-       struct delayed_work work;
-       struct workqueue_struct *irqthread;
-       struct lp8727_platform_data *pdata;
        struct lp8727_psy *psy;
-       struct lp8727_chg_param *chg_parm;
+       struct lp8727_platform_data *pdata;
+
+       /* Charger Data */
        enum lp8727_dev_id devid;
+       struct lp8727_chg_param *chg_param;
+
+       /* Interrupt Handling */
+       int irq;
+       struct delayed_work work;
+       unsigned long debounce_jiffies;
 };
 
 static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
@@ -119,81 +128,84 @@ static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
        return ret;
 }
 
-static int lp8727_is_charger_attached(const char *name, int id)
+static bool lp8727_is_charger_attached(const char *name, int id)
 {
-       if (name) {
-               if (!strcmp(name, "ac"))
-                       return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0;
-               else if (!strcmp(name, "usb"))
-                       return (id == ID_USB_CHG) ? 1 : 0;
-       }
+       if (!strcmp(name, "ac"))
+               return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG;
+       else if (!strcmp(name, "usb"))
+               return id == LP8727_ID_USB_CHG;
 
-       return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
+       return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG;
 }
 
 static int lp8727_init_device(struct lp8727_chg *pchg)
 {
        u8 val;
        int ret;
+       u8 intstat[LP8788_NUM_INTREGS];
 
-       val = ID200_EN | ADC_EN | CP_EN;
-       ret = lp8727_write_byte(pchg, CTRL1, val);
+       /* clear interrupts */
+       ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS);
        if (ret)
                return ret;
 
-       val = INT_EN | CHGDET_EN;
-       ret = lp8727_write_byte(pchg, CTRL2, val);
+       val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN;
+       ret = lp8727_write_byte(pchg, LP8727_CTRL1, val);
        if (ret)
                return ret;
 
-       return 0;
+       val = LP8727_INT_EN | LP8727_CHGDET_EN;
+       return lp8727_write_byte(pchg, LP8727_CTRL2, val);
 }
 
 static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
 {
        u8 val;
-       lp8727_read_byte(pchg, STATUS1, &val);
-       return val & DCPORT;
+
+       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
+       return val & LP8727_DCPORT;
 }
 
 static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
 {
        u8 val;
-       lp8727_read_byte(pchg, STATUS1, &val);
-       return val & CHPORT;
+
+       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
+       return val & LP8727_CHPORT;
 }
 
-static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
+static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
 {
-       lp8727_write_byte(pchg, SWCTRL, sw);
+       lp8727_write_byte(pchg, LP8727_SWCTRL, sw);
 }
 
 static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
 {
-       u8 devid = ID_NONE;
-       u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ;
+       struct lp8727_platform_data *pdata = pchg->pdata;
+       u8 devid = LP8727_ID_NONE;
+       u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ;
 
        switch (id) {
        case 0x5:
-               devid = ID_TA;
-               pchg->chg_parm = &pchg->pdata->ac;
+               devid = LP8727_ID_TA;
+               pchg->chg_param = pdata ? pdata->ac : NULL;
                break;
        case 0xB:
                if (lp8727_is_dedicated_charger(pchg)) {
-                       pchg->chg_parm = &pchg->pdata->ac;
-                       devid = ID_DEDICATED_CHG;
+                       pchg->chg_param = pdata ? pdata->ac : NULL;
+                       devid = LP8727_ID_DEDICATED_CHG;
                } else if (lp8727_is_usb_charger(pchg)) {
-                       pchg->chg_parm = &pchg->pdata->usb;
-                       devid = ID_USB_CHG;
-                       swctrl = SW_DM1_DM | SW_DP2_DP;
+                       pchg->chg_param = pdata ? pdata->usb : NULL;
+                       devid = LP8727_ID_USB_CHG;
+                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
                } else if (vbusin) {
-                       devid = ID_USB_DS;
-                       swctrl = SW_DM1_DM | SW_DP2_DP;
+                       devid = LP8727_ID_USB_DS;
+                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
                }
                break;
        default:
-               devid = ID_NONE;
-               pchg->chg_parm = NULL;
+               devid = LP8727_ID_NONE;
+               pchg->chg_param = NULL;
                break;
        }
 
@@ -205,24 +217,26 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
 {
        u8 val;
 
-       lp8727_read_byte(pchg, CTRL2, &val);
-       val |= CHGDET_EN;
-       lp8727_write_byte(pchg, CTRL2, val);
+       lp8727_read_byte(pchg, LP8727_CTRL2, &val);
+       val |= LP8727_CHGDET_EN;
+       lp8727_write_byte(pchg, LP8727_CTRL2, val);
 }
 
 static void lp8727_delayed_func(struct work_struct *_work)
 {
-       u8 intstat[2], idno, vbus;
-       struct lp8727_chg *pchg =
-           container_of(_work, struct lp8727_chg, work.work);
+       struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg,
+                                               work.work);
+       u8 intstat[LP8788_NUM_INTREGS];
+       u8 idno;
+       u8 vbus;
 
-       if (lp8727_read_bytes(pchg, INT1, intstat, 2)) {
+       if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) {
                dev_err(pchg->dev, "can not read INT registers\n");
                return;
        }
 
-       idno = intstat[0] & IDNO;
-       vbus = intstat[0] & VBUS;
+       idno = intstat[0] & LP8727_IDNO;
+       vbus = intstat[0] & LP8727_VBUS;
 
        lp8727_id_detection(pchg, idno, vbus);
        lp8727_enable_chgdet(pchg);
@@ -235,29 +249,44 @@ static void lp8727_delayed_func(struct work_struct *_work)
 static irqreturn_t lp8727_isr_func(int irq, void *ptr)
 {
        struct lp8727_chg *pchg = ptr;
-       unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC);
-
-       queue_delayed_work(pchg->irqthread, &pchg->work, delay);
 
+       schedule_delayed_work(&pchg->work, pchg->debounce_jiffies);
        return IRQ_HANDLED;
 }
 
-static int lp8727_intr_config(struct lp8727_chg *pchg)
+static int lp8727_setup_irq(struct lp8727_chg *pchg)
 {
+       int ret;
+       int irq = pchg->client->irq;
+       unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec :
+                                               DEFAULT_DEBOUNCE_MSEC;
+
        INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
 
-       pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
-       if (!pchg->irqthread) {
-               dev_err(pchg->dev, "can not create thread for lp8727\n");
-               return -ENOMEM;
+       if (irq <= 0) {
+               dev_warn(pchg->dev, "invalid irq number: %d\n", irq);
+               return 0;
        }
 
-       return request_threaded_irq(pchg->client->irq,
-                               NULL,
-                               lp8727_isr_func,
-                               IRQF_TRIGGER_FALLING,
-                               "lp8727_irq",
-                               pchg);
+       ret = request_threaded_irq(irq, NULL, lp8727_isr_func,
+                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                               "lp8727_irq", pchg);
+
+       if (ret)
+               return ret;
+
+       pchg->irq = irq;
+       pchg->debounce_jiffies = msecs_to_jiffies(delay_msec);
+
+       return 0;
+}
+
+static void lp8727_release_irq(struct lp8727_chg *pchg)
+{
+       cancel_delayed_work_sync(&pchg->work);
+
+       if (pchg->irq)
+               free_irq(pchg->irq, pchg);
 }
 
 static enum power_supply_property lp8727_charger_prop[] = {
@@ -283,54 +312,82 @@ static int lp8727_charger_get_property(struct power_supply *psy,
 {
        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
 
-       if (psp == POWER_SUPPLY_PROP_ONLINE)
-               val->intval = lp8727_is_charger_attached(psy->name,
-                                                        pchg->devid);
+       if (psp != POWER_SUPPLY_PROP_ONLINE)
+               return -EINVAL;
+
+       val->intval = lp8727_is_charger_attached(psy->name, pchg->devid);
 
        return 0;
 }
 
+static bool lp8727_is_high_temperature(enum lp8727_die_temp temp)
+{
+       switch (temp) {
+       case LP8788_TEMP_95C:
+       case LP8788_TEMP_115C:
+       case LP8788_TEMP_135C:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int lp8727_battery_get_property(struct power_supply *psy,
                                       enum power_supply_property psp,
                                       union power_supply_propval *val)
 {
        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
+       struct lp8727_platform_data *pdata = pchg->pdata;
+       enum lp8727_die_temp temp;
        u8 read;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
-                       lp8727_read_byte(pchg, STATUS1, &read);
-                       if (((read & CHGSTAT) >> 4) == EOC)
-                               val->intval = POWER_SUPPLY_STATUS_FULL;
-                       else
-                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               } else {
+               if (!lp8727_is_charger_attached(psy->name, pchg->devid)) {
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+                       return 0;
                }
+
+               lp8727_read_byte(pchg, LP8727_STATUS1, &read);
+
+               val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ?
+                               POWER_SUPPLY_STATUS_FULL :
+                               POWER_SUPPLY_STATUS_CHARGING;
                break;
        case POWER_SUPPLY_PROP_HEALTH:
-               lp8727_read_byte(pchg, STATUS2, &read);
-               read = (read & TEMP_STAT) >> 5;
-               if (read >= 0x1 && read <= 0x3)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               lp8727_read_byte(pchg, LP8727_STATUS2, &read);
+               temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT;
+
+               val->intval = lp8727_is_high_temperature(temp) ?
+                       POWER_SUPPLY_HEALTH_OVERHEAT :
+                       POWER_SUPPLY_HEALTH_GOOD;
                break;
        case POWER_SUPPLY_PROP_PRESENT:
-               if (pchg->pdata->get_batt_present)
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_present)
                        val->intval = pchg->pdata->get_batt_present();
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (pchg->pdata->get_batt_level)
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_level)
                        val->intval = pchg->pdata->get_batt_level();
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               if (pchg->pdata->get_batt_capacity)
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_capacity)
                        val->intval = pchg->pdata->get_batt_capacity();
                break;
        case POWER_SUPPLY_PROP_TEMP:
-               if (pchg->pdata->get_batt_temp)
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_temp)
                        val->intval = pchg->pdata->get_batt_temp();
                break;
        default:
@@ -343,16 +400,20 @@ static int lp8727_battery_get_property(struct power_supply *psy,
 static void lp8727_charger_changed(struct power_supply *psy)
 {
        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
+       u8 eoc_level;
+       u8 ichg;
        u8 val;
-       u8 eoc_level, ichg;
-
-       if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
-               if (pchg->chg_parm) {
-                       eoc_level = pchg->chg_parm->eoc_level;
-                       ichg = pchg->chg_parm->ichg;
-                       val = (ichg << 4) | eoc_level;
-                       lp8727_write_byte(pchg, CHGCTRL2, val);
-               }
+
+       /* skip if no charger exists */
+       if (!lp8727_is_charger_attached(psy->name, pchg->devid))
+               return;
+
+       /* update charging parameters */
+       if (pchg->chg_param) {
+               eoc_level = pchg->chg_param->eoc_level;
+               ichg = pchg->chg_param->ichg;
+               val = (ichg << LP8727_ICHG_SHIFT) | eoc_level;
+               lp8727_write_byte(pchg, LP8727_CHGCTRL2, val);
        }
 }
 
@@ -360,9 +421,9 @@ static int lp8727_register_psy(struct lp8727_chg *pchg)
 {
        struct lp8727_psy *psy;
 
-       psy = kzalloc(sizeof(*psy), GFP_KERNEL);
+       psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL);
        if (!psy)
-               goto err_mem;
+               return -ENOMEM;
 
        pchg->psy = psy;
 
@@ -375,7 +436,7 @@ static int lp8727_register_psy(struct lp8727_chg *pchg)
        psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to);
 
        if (power_supply_register(pchg->dev, &psy->ac))
-               goto err_psy;
+               goto err_psy_ac;
 
        psy->usb.name = "usb";
        psy->usb.type = POWER_SUPPLY_TYPE_USB;
@@ -386,7 +447,7 @@ static int lp8727_register_psy(struct lp8727_chg *pchg)
        psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to);
 
        if (power_supply_register(pchg->dev, &psy->usb))
-               goto err_psy;
+               goto err_psy_usb;
 
        psy->batt.name = "main_batt";
        psy->batt.type = POWER_SUPPLY_TYPE_BATTERY;
@@ -396,14 +457,15 @@ static int lp8727_register_psy(struct lp8727_chg *pchg)
        psy->batt.external_power_changed = lp8727_charger_changed;
 
        if (power_supply_register(pchg->dev, &psy->batt))
-               goto err_psy;
+               goto err_psy_batt;
 
        return 0;
 
-err_mem:
-       return -ENOMEM;
-err_psy:
-       kfree(psy);
+err_psy_batt:
+       power_supply_unregister(&psy->usb);
+err_psy_usb:
+       power_supply_unregister(&psy->ac);
+err_psy_ac:
        return -EPERM;
 }
 
@@ -417,7 +479,6 @@ static void lp8727_unregister_psy(struct lp8727_chg *pchg)
        power_supply_unregister(&psy->ac);
        power_supply_unregister(&psy->usb);
        power_supply_unregister(&psy->batt);
-       kfree(psy);
 }
 
 static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
@@ -428,7 +489,7 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
        if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
                return -EIO;
 
-       pchg = kzalloc(sizeof(*pchg), GFP_KERNEL);
+       pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
        if (!pchg)
                return -ENOMEM;
 
@@ -442,37 +503,31 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
        ret = lp8727_init_device(pchg);
        if (ret) {
                dev_err(pchg->dev, "i2c communication err: %d", ret);
-               goto error;
+               return ret;
        }
 
-       ret = lp8727_intr_config(pchg);
+       ret = lp8727_register_psy(pchg);
        if (ret) {
-               dev_err(pchg->dev, "irq handler err: %d", ret);
-               goto error;
+               dev_err(pchg->dev, "power supplies register err: %d", ret);
+               return ret;
        }
 
-       ret = lp8727_register_psy(pchg);
+       ret = lp8727_setup_irq(pchg);
        if (ret) {
-               dev_err(pchg->dev, "power supplies register err: %d", ret);
-               goto error;
+               dev_err(pchg->dev, "irq handler err: %d", ret);
+               lp8727_unregister_psy(pchg);
+               return ret;
        }
 
        return 0;
-
-error:
-       kfree(pchg);
-       return ret;
 }
 
 static int __devexit lp8727_remove(struct i2c_client *cl)
 {
        struct lp8727_chg *pchg = i2c_get_clientdata(cl);
 
+       lp8727_release_irq(pchg);
        lp8727_unregister_psy(pchg);
-       free_irq(pchg->client->irq, pchg);
-       flush_workqueue(pchg->irqthread);
-       destroy_workqueue(pchg->irqthread);
-       kfree(pchg);
        return 0;
 }
 
@@ -493,6 +548,5 @@ static struct i2c_driver lp8727_driver = {
 module_i2c_driver(lp8727_driver);
 
 MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
-MODULE_AUTHOR("Woogyom Kim <milo.kim@ti.com>, "
-             "Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
 MODULE_LICENSE("GPL");