]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/power/abx500_chargalg.c
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[karo-tx-linux.git] / drivers / power / abx500_chargalg.c
index 8b69da0ae5af76ac983830db7e0427a7e638146a..f043c0851a7600d1417711b7f1ec4364e9331985 100644 (file)
@@ -33,9 +33,6 @@
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT                   10
 
-/* Recharge criteria counter */
-#define RCH_COND_CNT                   3
-
 #define to_abx500_chargalg_device_info(x) container_of((x), \
        struct abx500_chargalg, chargalg_psy);
 
@@ -196,7 +193,6 @@ enum maxim_ret {
  * @dev:               pointer to the structure device
  * @charge_status:     battery operating status
  * @eoc_cnt:           counter used to determine end-of_charge
- * @rch_cnt:           counter used to determine start of recharge
  * @maintenance_chg:   indicate if maintenance charge is active
  * @t_hyst_norm                temperature hysteresis when the temperature has been
  *                     over or under normal limits
@@ -223,7 +219,6 @@ struct abx500_chargalg {
        struct device *dev;
        int charge_status;
        int eoc_cnt;
-       int rch_cnt;
        bool maintenance_chg;
        int t_hyst_norm;
        int t_hyst_lowhigh;
@@ -450,8 +445,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 {
        /* Check if charger exists and kick watchdog if charging */
        if (di->ac_chg && di->ac_chg->ops.kick_wd &&
-                       di->chg_info.online_chg & AC_CHG)
+           di->chg_info.online_chg & AC_CHG) {
+               /*
+                * If AB charger watchdog expired, pm2xxx charging
+                * gets disabled. To be safe, kick both AB charger watchdog
+                * and pm2xxx watchdog.
+                */
+               if (di->ac_chg->external &&
+                   di->usb_chg && di->usb_chg->ops.kick_wd)
+                       di->usb_chg->ops.kick_wd(di->usb_chg);
+
                return di->ac_chg->ops.kick_wd(di->ac_chg);
+       }
        else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
                        di->chg_info.online_chg & USB_CHG)
                return di->usb_chg->ops.kick_wd(di->usb_chg);
@@ -608,6 +613,8 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
 static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
        int vset, int iset)
 {
+       bool start_chargalg_wd = true;
+
        switch (di->chg_info.charger_type) {
        case AC_CHG:
                dev_dbg(di->dev,
@@ -625,8 +632,12 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
 
        default:
                dev_err(di->dev, "Unknown charger to charge from\n");
+               start_chargalg_wd = false;
                break;
        }
+
+       if (start_chargalg_wd && !delayed_work_pending(&di->chargalg_wd_work))
+               queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
 }
 
 /**
@@ -858,6 +869,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
        union power_supply_propval ret;
        int i, j;
        bool psy_found = false;
+       bool capacity_updated = false;
 
        psy = (struct power_supply *)data;
        ext = dev_get_drvdata(dev);
@@ -870,6 +882,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
        if (!psy_found)
                return 0;
 
+       /*
+        *  If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
+        * property because of handling that sysfs entry on its own, this is
+        * the place to get the battery capacity.
+        */
+       if (!ext->get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
+               di->batt_data.percent = ret.intval;
+               capacity_updated = true;
+       }
+
        /* Go through all properties for the psy */
        for (j = 0; j < ext->num_properties; j++) {
                enum power_supply_property prop;
@@ -1154,7 +1176,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
                        }
                        break;
                case POWER_SUPPLY_PROP_CAPACITY:
-                       di->batt_data.percent = ret.intval;
+                       if (!capacity_updated)
+                               di->batt_data.percent = ret.intval;
                        break;
                default:
                        break;
@@ -1424,16 +1447,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
        case STATE_WAIT_FOR_RECHARGE_INIT:
                abx500_chargalg_hold_charging(di);
                abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
-               di->rch_cnt = RCH_COND_CNT;
                /* Intentional fallthrough */
 
        case STATE_WAIT_FOR_RECHARGE:
-               if (di->batt_data.volt <=
-                       di->bm->bat_type[di->bm->batt_id].recharge_vol) {
-                       if (di->rch_cnt-- == 0)
-                               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               } else
-                       di->rch_cnt = RCH_COND_CNT;
+               if (di->batt_data.percent <=
+                   di->bm->bat_type[di->bm->batt_id].
+                   recharge_cap)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
                break;
 
        case STATE_MAINTENANCE_A_INIT:
@@ -1618,6 +1638,9 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
                                val->intval = POWER_SUPPLY_HEALTH_COLD;
                        else
                                val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
+                          di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) {
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
                } else {
                        val->intval = POWER_SUPPLY_HEALTH_GOOD;
                }
@@ -1630,6 +1653,25 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
 
 /* Exposure to the sysfs interface */
 
+/**
+ * abx500_chargalg_sysfs_show() - sysfs show operations
+ * @kobj:      pointer to the struct kobject
+ * @attr:      pointer to the struct attribute
+ * @buf:       buffer that holds the parameter to send to userspace
+ *
+ * Returns a buffer to be displayed in user space
+ */
+static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
+                                         struct attribute *attr, char *buf)
+{
+       struct abx500_chargalg *di = container_of(kobj,
+               struct abx500_chargalg, chargalg_kobject);
+
+       return sprintf(buf, "%d\n",
+                      di->susp_status.ac_suspended &&
+                      di->susp_status.usb_suspended);
+}
+
 /**
  * abx500_chargalg_sysfs_charger() - sysfs store operations
  * @kobj:      pointer to the struct kobject
@@ -1698,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
 static struct attribute abx500_chargalg_en_charger = \
 {
        .name = "chargalg",
-       .mode = S_IWUGO,
+       .mode = S_IRUGO | S_IWUSR,
 };
 
 static struct attribute *abx500_chargalg_chg[] = {
@@ -1707,6 +1749,7 @@ static struct attribute *abx500_chargalg_chg[] = {
 };
 
 static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+       .show = abx500_chargalg_sysfs_show,
        .store = abx500_chargalg_sysfs_charger,
 };