- interrupts: Interrupt specifier for each name in interrupt-names
 - interrupt-names: Should contain the following entries:
                   "chrg_det", "rvrs_chrg", "chrg_se1b", "se0conn",
-                  "rvrs_mode", "chrgcurr1", "vbusvld", "battdetb"
+                  "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
+                  "battdetb"
 - io-channels: IIO ADC channel specifier for each name in io-channel-names
 - io-channel-names: Should contain the following entries:
                    "battdetb", "battp", "vbus", "chg_isense", "batti"
        compatible = "motorola,mapphone-cpcap-charger";
        interrupts-extended = <
                &cpcap 13 0 &cpcap 12 0 &cpcap 29 0 &cpcap 28 0
-               &cpcap 22 0 &cpcap 20 0 &cpcap 19 0 &cpcap 54 0
+               &cpcap 22 0 &cpcap 21 0 &cpcap 20 0 &cpcap 19 0
+               &cpcap 54 0
        >;
        interrupt-names =
                "chrg_det", "rvrs_chrg", "chrg_se1b", "se0conn",
-               "rvrs_mode", "chrgcurr1", "vbusvld", "battdetb";
+               "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
+               "battdetb";
        mode-gpios = <&gpio3 29 GPIO_ACTIVE_LOW
                      &gpio3 23 GPIO_ACTIVE_LOW>;
        io-channels = <&cpcap_adc 0 &cpcap_adc 1
 
        CPCAP_CHARGER_IIO_NR,
 };
 
+enum {
+       CPCAP_CHARGER_DISCONNECTED,
+       CPCAP_CHARGER_DETECTING,
+       CPCAP_CHARGER_CHARGING,
+       CPCAP_CHARGER_DONE,
+};
+
 struct cpcap_charger_ddata {
        struct device *dev;
        struct regmap *reg;
        atomic_t active;
 
        int status;
+       int state;
+       int voltage;
 };
 
 struct cpcap_interrupt_desc {
 
        bool chrg_se1b;
        bool rvrs_mode;
+       bool chrgcurr2;
        bool chrgcurr1;
        bool vbusvld;
 
 
        s->chrg_se1b = val & BIT(13);
        s->rvrs_mode = val & BIT(6);
+       s->chrgcurr2 = val & BIT(5);
        s->chrgcurr1 = val & BIT(4);
        s->vbusvld = val & BIT(3);
 
        return 0;
 }
 
+static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata,
+                                      int state)
+{
+       const char *status;
+
+       if (state > CPCAP_CHARGER_DONE) {
+               dev_warn(ddata->dev, "unknown state: %i\n", state);
+
+               return;
+       }
+
+       ddata->state = state;
+
+       switch (state) {
+       case CPCAP_CHARGER_DISCONNECTED:
+               status = "DISCONNECTED";
+               break;
+       case CPCAP_CHARGER_DETECTING:
+               status = "DETECTING";
+               break;
+       case CPCAP_CHARGER_CHARGING:
+               status = "CHARGING";
+               break;
+       case CPCAP_CHARGER_DONE:
+               status = "DONE";
+               break;
+       default:
+               return;
+       }
+
+       dev_dbg(ddata->dev, "state: %s\n", status);
+}
+
+int cpcap_charger_voltage_to_regval(int voltage)
+{
+       int offset;
+
+       switch (voltage) {
+       case 0 ... 4100000 - 1:
+               return 0;
+       case 4100000 ... 4200000 - 1:
+               offset = 1;
+               break;
+       case 4200000 ... 4300000 - 1:
+               offset = 0;
+               break;
+       case 4300000 ... 4380000 - 1:
+               offset = -1;
+               break;
+       case 4380000 ... 4440000:
+               offset = -2;
+               break;
+       default:
+               return 0;
+       }
+
+       return ((voltage - 4100000) / 20000) + offset;
+}
+
+static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata,
+                                    int state, unsigned long delay)
+{
+       int error;
+
+       error = cpcap_charger_set_state(ddata, 0, 0, 0);
+       if (error)
+               return;
+
+       cpcap_charger_update_state(ddata, state);
+       power_supply_changed(ddata->usb);
+       schedule_delayed_work(&ddata->detect_work, delay);
+}
+
 static void cpcap_usb_detect(struct work_struct *work)
 {
        struct cpcap_charger_ddata *ddata;
        if (error)
                return;
 
+       /* Just init the state if a charger is connected with no chrg_det set */
+       if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) {
+               cpcap_charger_update_state(ddata, CPCAP_CHARGER_DETECTING);
+
+               return;
+       }
+
+       /*
+        * If battery voltage is higher than charge voltage, it may have been
+        * charged to 4.35V by Android. Try again in 10 minutes.
+        */
+       if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) {
+               cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING,
+                                        HZ * 60 * 10);
+
+               return;
+       }
+
+       /* Throttle chrgcurr2 interrupt for charger done and retry */
+       switch (ddata->state) {
+       case CPCAP_CHARGER_CHARGING:
+               if (s.chrgcurr2)
+                       break;
+               if (s.chrgcurr1 && s.vbusvld) {
+                       cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DONE,
+                                                HZ * 5);
+                       return;
+               }
+               break;
+       case CPCAP_CHARGER_DONE:
+               if (!s.chrgcurr2)
+                       break;
+               cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING,
+                                        HZ * 5);
+               return;
+       default:
+               break;
+       }
+
        if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) &&
            s.chrgcurr1) {
                int max_current;
+               int vchrg;
 
                if (cpcap_charger_battery_found(ddata))
                        max_current = CPCAP_REG_CRM_ICHRG_1A596;
                else
                        max_current = CPCAP_REG_CRM_ICHRG_0A532;
 
+               vchrg = cpcap_charger_voltage_to_regval(ddata->voltage);
                error = cpcap_charger_set_state(ddata,
-                                               CPCAP_REG_CRM_VCHRG_4V35,
+                                               CPCAP_REG_CRM_VCHRG(vchrg),
                                                max_current, 0);
                if (error)
                        goto out_err;
+               cpcap_charger_update_state(ddata, CPCAP_CHARGER_CHARGING);
        } else {
                error = cpcap_charger_set_state(ddata, 0, 0, 0);
                if (error)
                        goto out_err;
+               cpcap_charger_update_state(ddata, CPCAP_CHARGER_DISCONNECTED);
        }
 
        power_supply_changed(ddata->usb);
        "chrg_det", "rvrs_chrg",
 
        /* REG_INT1 */
-       "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr1", "vbusvld",
+       "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
 
        /* REG_INT_3 */
        "battdetb",
                return -ENOMEM;
 
        ddata->dev = &pdev->dev;
+       ddata->voltage = 4200000;
 
        ddata->reg = dev_get_regmap(ddata->dev->parent, NULL);
        if (!ddata->reg)