summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKalle Komierowski <karl.komierowski@stericsson.com>2011-11-10 14:17:57 +0100
committerPhilippe Langlais <philippe.langlais@linaro.org>2012-03-19 08:51:08 +0100
commit3f3eb688363b7fbb2e8e00502034573a3474ded0 (patch)
treed60eba80ff08a89cd72d38ea2642db4e4de78bd7
parent16c369647714d9278cd63a1ff97c9e5dd98d86c5 (diff)
power: ab8500_bm: Add sysfs node to force current bat capacity
This will be used by the user space application that will be rewritten to save and restore the current remaining battery capacity even after reboot. ST-Ericsson Linux next: - ST-Ericsson ID: 398179 ST-Ericsson FOSS-OUT ID Trivial Change-Id: I100f4ad7f227409b87cb1c5913947ab45a5232d4 Signed-off-by: Kalle Komierowski <karl.komierowski@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37313 Reviewed-by: QABUILD Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r--arch/arm/mach-ux500/board-mop500-bm.c1
-rw-r--r--drivers/power/ab8500_fg.c123
-rw-r--r--include/linux/mfd/ab8500/bm.h4
3 files changed, 115 insertions, 13 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-bm.c b/arch/arm/mach-ux500/board-mop500-bm.c
index 085661e7491..522a32c615d 100644
--- a/arch/arm/mach-ux500/board-mop500-bm.c
+++ b/arch/arm/mach-ux500/board-mop500-bm.c
@@ -374,6 +374,7 @@ static const struct ab8500_fg_parameters fg = {
.lowbat_threshold = 3100,
.battok_falling_th_sel0 = 2860,
.battok_raising_th_sel1 = 2860,
+ .user_cap_limit = 15,
};
static const struct ab8500_maxim_parameters maxi_params = {
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 2ca2ab877ca..14bc4f86ca1 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -64,6 +64,7 @@ enum ab8500_fg_discharge_state {
AB8500_FG_DISCHARGE_INITMEASURING,
AB8500_FG_DISCHARGE_INIT_RECOVERY,
AB8500_FG_DISCHARGE_RECOVERY,
+ AB8500_FG_DISCHARGE_READOUT_INIT,
AB8500_FG_DISCHARGE_READOUT,
AB8500_FG_DISCHARGE_WAKEUP,
};
@@ -73,6 +74,7 @@ static char *discharge_state[] = {
"DISCHARGE_INITMEASURING",
"DISCHARGE_INIT_RECOVERY",
"DISCHARGE_RECOVERY",
+ "DISCHARGE_READOUT_INIT",
"DISCHARGE_READOUT",
"DISCHARGE_WAKEUP",
};
@@ -111,6 +113,7 @@ struct ab8500_fg_battery_capacity {
int prev_mah;
int prev_percent;
int prev_level;
+ int user_mah;
};
struct ab8500_fg_flags {
@@ -123,6 +126,7 @@ struct ab8500_fg_flags {
bool bat_ovv;
bool batt_unknown;
bool calibrate;
+ bool user_cap;
};
struct inst_curr_result_list {
@@ -1116,6 +1120,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
di->flags.fully_charged);
sysfs_notify(&di->fg_kobject, NULL, "charge_full");
}
+ sysfs_notify(&di->fg_kobject, NULL, "charge_now");
}
}
@@ -1197,6 +1202,56 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
ab8500_fg_check_capacity_limits(di, false);
}
+static void force_capacity(struct ab8500_fg *di)
+{
+ int cap;
+
+ ab8500_fg_clear_cap_samples(di);
+ cap = di->bat_cap.user_mah;
+ if (cap > di->bat_cap.max_mah_design) {
+ dev_dbg(di->dev, "Remaining cap %d can't be bigger than total"
+ " %d\n", cap, di->bat_cap.max_mah_design);
+ cap = di->bat_cap.max_mah_design;
+ }
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah);
+ di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap);
+ di->bat_cap.mah = cap;
+ ab8500_fg_check_capacity_limits(di, true);
+}
+
+static bool check_sysfs_capacity(struct ab8500_fg *di)
+{
+ int cap, lower, upper;
+ int cap_permille;
+
+ cap = di->bat_cap.user_mah;
+
+ cap_permille = ab8500_fg_convert_mah_to_permille(di,
+ di->bat_cap.user_mah);
+
+ lower = di->bat_cap.permille - di->bat->fg_params->user_cap_limit * 10;
+ upper = di->bat_cap.permille + di->bat->fg_params->user_cap_limit * 10;
+
+ if (lower < 0)
+ lower = 0;
+ /* 1000 is permille, -> 100 percent */
+ if (upper > 1000)
+ upper = 1000;
+
+ dev_dbg(di->dev, "Capacity limits:"
+ " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n",
+ lower, cap_permille, upper, cap, di->bat_cap.mah);
+
+ /* If within limits, use the saved capacity and exit estimation...*/
+ if (cap_permille > lower && cap_permille < upper) {
+ dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap);
+ force_capacity(di);
+ return true;
+ }
+ dev_dbg(di->dev, "Capacity from user out of limits, ignoring");
+ return false;
+}
+
/**
* ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
* @di: pointer to the ab8500_fg structure
@@ -1233,7 +1288,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
/* Discard the first [x] seconds */
if (di->init_cnt >
di->bat->fg_params->init_discard_time) {
-
ab8500_fg_calc_cap_discharge_voltage(di, true);
ab8500_fg_check_capacity_limits(di, true);
@@ -1241,14 +1295,10 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
di->init_cnt += sleep_time;
if (di->init_cnt >
- di->bat->fg_params->init_total_time) {
+ di->bat->fg_params->init_total_time)
di->fg_samples = SEC_TO_SAMPLE(
- di->bat->fg_params->accu_high_curr);
-
+ di->bat->fg_params->accu_high_curr);
ab8500_fg_coulomb_counter(di, true);
- ab8500_fg_discharge_state_to(di,
- AB8500_FG_DISCHARGE_READOUT);
- }
break;
@@ -1287,13 +1337,17 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
}
di->recovery_cnt += sleep_time;
} else {
- di->fg_samples = SEC_TO_SAMPLE(
- di->bat->fg_params->accu_high_curr);
- ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
- AB8500_FG_DISCHARGE_READOUT);
+ AB8500_FG_DISCHARGE_READOUT_INIT);
}
+ break;
+ case AB8500_FG_DISCHARGE_READOUT_INIT:
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
break;
case AB8500_FG_DISCHARGE_READOUT:
@@ -1357,9 +1411,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
di->fg_samples = SEC_TO_SAMPLE(
di->bat->fg_params->accu_high_curr);
- /* Re-program number of samples set above */
ab8500_fg_coulomb_counter(di, true);
- ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
ab8500_fg_check_capacity_limits(di, false);
@@ -1475,6 +1529,19 @@ static void ab8500_fg_periodic_work(struct work_struct *work)
ab8500_fg_calc_cap_discharge_voltage(di, true);
ab8500_fg_check_capacity_limits(di, true);
di->init_capacity = false;
+
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else if (di->flags.user_cap) {
+ if (check_sysfs_capacity(di)) {
+ ab8500_fg_check_capacity_limits(di, true);
+ if (di->flags.charging)
+ ab8500_fg_charge_state_to(di,
+ AB8500_FG_CHARGE_INIT);
+ else
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT_INIT);
+ }
+ di->flags.user_cap = false;
queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
} else
ab8500_fg_algorithm(di);
@@ -1977,9 +2044,38 @@ static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
}
return ret;
}
+
+static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
+{
+ return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
+}
+
+static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
+ size_t count)
+{
+ unsigned long charge_now;
+ ssize_t ret;
+
+ ret = strict_strtoul(buf, 10, &charge_now);
+
+ dev_dbg(di->dev, "Ret %d charge_now %lu was %d",
+ ret, charge_now, di->bat_cap.prev_mah);
+
+ if (!ret) {
+ di->bat_cap.user_mah = (int) charge_now;
+ di->flags.user_cap = true;
+ ret = count;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ }
+ return ret;
+}
+
static struct ab8500_fg_sysfs_entry charge_full_attr =
__ATTR(charge_full, 0644, charge_full_show, charge_full_store);
+static struct ab8500_fg_sysfs_entry charge_now_attr =
+ __ATTR(charge_now, 0644, charge_now_show, charge_now_store);
+
static ssize_t
ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
@@ -2017,6 +2113,7 @@ const struct sysfs_ops ab8500_fg_sysfs_ops = {
static struct attribute *ab8500_fg_attrs[] = {
&charge_full_attr.attr,
+ &charge_now_attr.attr,
NULL,
};
diff --git a/include/linux/mfd/ab8500/bm.h b/include/linux/mfd/ab8500/bm.h
index 783817c893d..90ec670888c 100644
--- a/include/linux/mfd/ab8500/bm.h
+++ b/include/linux/mfd/ab8500/bm.h
@@ -282,6 +282,9 @@ struct ab8500_fg;
* Resolution in 50 mV step.
* @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
* Resolution in 50 mV step.
+ * @user_cap_limit Capacity reported from user must be within this
+ * limit to be considered as sane, in percentage
+ * points.
*/
struct ab8500_fg_parameters {
int recovery_sleep_timer;
@@ -296,6 +299,7 @@ struct ab8500_fg_parameters {
int lowbat_threshold;
int battok_falling_th_sel0;
int battok_raising_th_sel1;
+ int user_cap_limit;
};
/**