summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Gonzalez <alex.gonzalez@digi.com>2012-01-31 15:50:47 +0100
committerAlex Gonzalez <alex.gonzalez@digi.com>2012-01-31 17:29:01 +0100
commit63fecb5d5440c096674b19e5a04112fb6756d5ee (patch)
tree96e409acb33be32355289941d54ff4937a0dcc72
parent4bd285c57296e1165013c4ff43436693721e40b3 (diff)
ccxmx51: mc13892, add capacity calculation to battery driver.
Add a basic voltage based capacity calculation to the driver. It uses static charge and discharge lookup tables for 25 celsius, characterized for a hopefully generic Li-Ion battery. This simple implementation disregards temperature and age, and it also does not include hysteresis algorithms so the capacity may vary upon charger connection and disconnection. User space can still implement better algorithms using the information provided in the sysfs. Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com> (cherry picked from commit 35fea3d126c91a4f25d9269ad58dc8a1c52fcd1f)
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_battery.c217
-rw-r--r--include/linux/pmic_battery.h53
2 files changed, 236 insertions, 34 deletions
diff --git a/drivers/mxc/pmic/mc13892/pmic_battery.c b/drivers/mxc/pmic/mc13892/pmic_battery.c
index 743548cb1b30..1b6c41a08cc2 100644
--- a/drivers/mxc/pmic/mc13892/pmic_battery.c
+++ b/drivers/mxc/pmic/mc13892/pmic_battery.c
@@ -96,6 +96,9 @@
#define COULOMB_TO_UAH(c) (10000 * c / 36)
+static int chg_work_interval = 100;
+static int monitor_work_interval = HZ * 10;
+
enum chg_setting {
TRICKLE_CHG_EN,
LOW_POWER_BOOT_ACK,
@@ -350,6 +353,7 @@ struct mc13892_dev_info {
int charger_online;
int charger_voltage_uV;
int accum_current_uAh;
+ int cal_capacity;
struct power_supply bat;
struct power_supply charger;
@@ -367,12 +371,133 @@ static enum power_supply_property mc13892_battery_props[] = {
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_PRESENT,
};
static enum power_supply_property mc13892_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
+s32 mc13892_get_bat_level(struct mc13892_dev_info *di)
+{
+ u16 vbat = 0;
+ static int bat_capacity_old;
+ int retval;
+
+ retval = pmic_get_batt_voltage(&(di->voltage_raw));
+ if (retval == 0)
+ di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV;
+ else
+ pr_debug("Unable to update battery voltage measurement.\n");
+
+ vbat = di->voltage_uV/1000;
+
+ switch(di->battery_status){
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if( vbat > VDISCHARGE_100 )
+ di->cal_capacity = 100;
+ else if(vbat > VDISCHARGE_95)
+ di->cal_capacity = 95;
+ else if(vbat > VDISCHARGE_90)
+ di->cal_capacity = 90;
+ else if(vbat > VDISCHARGE_85)
+ di->cal_capacity = 85;
+ else if(vbat > VDISCHARGE_80)
+ di->cal_capacity = 80;
+ else if(vbat > VDISCHARGE_75)
+ di->cal_capacity = 75;
+ else if(vbat > VDISCHARGE_65)
+ di->cal_capacity = 65;
+ else if(vbat > VDISCHARGE_55)
+ di->cal_capacity = 55;
+ else if(vbat > VDISCHARGE_50)
+ di->cal_capacity = 50;
+ else if(vbat > VDISCHARGE_45)
+ di->cal_capacity = 45;
+ else if(vbat > VDISCHARGE_40)
+ di->cal_capacity = 40;
+ else if(vbat > VDISCHARGE_35)
+ di->cal_capacity = 35;
+ else if(vbat > VDISCHARGE_25)
+ di->cal_capacity = 25;
+ else if(vbat > VDISCHARGE_20)
+ di->cal_capacity = 20;
+ else if(vbat > VDISCHARGE_15)
+ di->cal_capacity = 15;
+ else if(vbat > VDISCHARGE_10)
+ di->cal_capacity = 10;
+ else if(vbat > VDISCHARGE_5)
+ di->cal_capacity = 5;
+ else
+ di->cal_capacity = 2;
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if(vbat<VCHARGE_5)
+ di->cal_capacity = 5;
+ else if(vbat<VCHARGE_10)
+ di->cal_capacity = 10;
+ else if(vbat<VCHARGE_15)
+ di->cal_capacity = 15;
+ else if(vbat<VCHARGE_20)
+ di->cal_capacity = 20;
+ else if(vbat<VCHARGE_25)
+ di->cal_capacity = 25;
+ else if(vbat<VCHARGE_30)
+ di->cal_capacity = 30;
+ else if(vbat<VCHARGE_35)
+ di->cal_capacity = 35;
+ else if(vbat<VCHARGE_40)
+ di->cal_capacity = 40;
+ else if(vbat<VCHARGE_45)
+ di->cal_capacity = 45;
+ else if(vbat<VCHARGE_50)
+ di->cal_capacity = 50;
+ else if(vbat<VCHARGE_55)
+ di->cal_capacity = 55;
+ else if(vbat<VCHARGE_60)
+ di->cal_capacity = 60;
+ else if(vbat<VCHARGE_65)
+ di->cal_capacity = 65;
+ else if(vbat<VCHARGE_70)
+ di->cal_capacity = 70;
+ else if(vbat<VCHARGE_75)
+ di->cal_capacity = 75;
+ else if(vbat<VCHARGE_80)
+ di->cal_capacity = 80;
+ else if(vbat<VCHARGE_85)
+ di->cal_capacity = 85;
+ else if(vbat<VCHARGE_90)
+ di->cal_capacity = 90;
+ else if(vbat<VCHARGE_95)
+ di->cal_capacity = 95;
+ else
+ di->cal_capacity = 100;
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ di->cal_capacity = 100;
+ break;
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ di->cal_capacity = 0;
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("cal_capacity %d\n",di->cal_capacity);
+ if( bat_capacity_old != di->cal_capacity ){
+ if( di->charger_online )
+ power_supply_changed(&di->charger);
+ else
+ power_supply_changed(&di->bat);
+ }
+ bat_capacity_old = di->cal_capacity;
+ return 0;
+}
+
static int pmic_get_chg_value(unsigned int *value)
{
t_channel channel;
@@ -429,7 +554,7 @@ static void chg_thread(struct work_struct *work)
if (disable_chg_timer) {
disable_chg_timer = 0;
pmic_set_chg_current(main_charger_current);
- queue_delayed_work(chg_wq, &chg_work, 100);
+ queue_delayed_work(chg_wq, &chg_work, chg_work_interval);
chg_wa_timer = 1;
return;
}
@@ -449,7 +574,7 @@ static void chg_thread(struct work_struct *work)
disable_chg_timer = 1;
}
- queue_delayed_work(chg_wq, &chg_work, 100);
+ queue_delayed_work(chg_wq, &chg_work, chg_work_interval);
chg_wa_timer = 1;
}
}
@@ -473,11 +598,11 @@ static int mc13892_charger_update_status(struct mc13892_dev_info *di)
cancel_delayed_work(&di->monitor_work);
queue_delayed_work(di->monitor_wqueue,
- &di->monitor_work, HZ / 10);
+ &di->monitor_work, monitor_work_interval);
if (online) {
pmic_start_coulomb_counter();
pmic_restart_charging();
- queue_delayed_work(chg_wq, &chg_work, 100);
+ queue_delayed_work(chg_wq, &chg_work, chg_work_interval);
chg_wa_timer = 1;
} else {
cancel_delayed_work(&chg_work);
@@ -577,12 +702,14 @@ static void mc13892_battery_work(struct work_struct *work)
struct mc13892_dev_info *di = container_of(work,
struct mc13892_dev_info,
monitor_work.work);
- const int interval = HZ * 60;
dev_dbg(di->dev, "%s\n", __func__);
mc13892_battery_update_status(di);
- queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+
+ mc13892_get_bat_level(di);
+
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, monitor_work_interval);
}
static void charger_online_event_callback(void *para)
@@ -599,37 +726,58 @@ static int mc13892_battery_get_property(struct power_supply *psy,
{
struct mc13892_dev_info *di = to_mc13892_dev_info(psy);
switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
- mc13892_charger_update_status(di);
- mc13892_battery_update_status(di);
- }
- val->intval = di->battery_status;
- return 0;
- default:
- break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ mc13892_charger_update_status(di);
+ mc13892_battery_update_status(di);
+ }
+ val->intval = di->battery_status;
+ return 0;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = !di->charger_online;
+ return 0;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = di->cal_capacity;
+ return 0;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (di->cal_capacity < CAPACITY_LIMIT_LOW)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (di->cal_capacity < CAPACITY_LIMIT_HIGH)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ else if (di->cal_capacity == CAPACITY_LIMIT_FULL)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+ else if (di->cal_capacity > CAPACITY_LIMIT_HIGH)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = BATTERY_TYPE;
+ return 0;
+ default:
+ break;
}
mc13892_battery_read_status(di);
switch (psp) {
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = di->voltage_uV;
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = di->current_uA;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW:
- val->intval = di->accum_current_uAh;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = max_voltage_design;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
- val->intval = min_voltage_design;
- break;
- default:
- return -EINVAL;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = di->accum_current_uAh;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = max_voltage_design;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = min_voltage_design;
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -653,7 +801,7 @@ static ssize_t chg_wa_enable_store(struct device *dev,
if (chg_wa_timer)
printk(KERN_INFO "Charger timer is already on\n");
else {
- queue_delayed_work(chg_wq, &chg_work, 100);
+ queue_delayed_work(chg_wq, &chg_work, chg_work_interval);
chg_wa_timer = 1;
printk(KERN_INFO "Turned on the timer\n");
}
@@ -751,7 +899,8 @@ static int pmic_battery_probe(struct platform_device *pdev)
retval = -ESRCH;
goto workqueue_failed;
}
- queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work,
+ monitor_work_interval);
di->dev = &pdev->dev;
di->bat.name = "mc13892_bat";
diff --git a/include/linux/pmic_battery.h b/include/linux/pmic_battery.h
index 4f719e44d074..215cfa45d0c2 100644
--- a/include/linux/pmic_battery.h
+++ b/include/linux/pmic_battery.h
@@ -414,6 +414,59 @@ PMIC_STATUS pmic_batt_event_subscribe(t_batt_event event, void *callback);
*/
PMIC_STATUS pmic_batt_event_unsubscribe(t_batt_event event, void *callback);
+/* Battery characterization look up table @ 25 celsius for Li-Ion battery. */
+
+/* Battery discharge voltage level (mV) */
+#define VDISCHARGE_5 3320
+#define VDISCHARGE_10 3430
+#define VDISCHARGE_15 3510
+#define VDISCHARGE_20 3580
+#define VDISCHARGE_25 3610
+#define VDISCHARGE_30 3640
+#define VDISCHARGE_35 3670
+#define VDISCHARGE_40 3690
+#define VDISCHARGE_45 3720
+#define VDISCHARGE_50 3740
+#define VDISCHARGE_55 3760
+#define VDISCHARGE_60 3780
+#define VDISCHARGE_65 3800
+#define VDISCHARGE_70 3830
+#define VDISCHARGE_75 3860
+#define VDISCHARGE_80 3890
+#define VDISCHARGE_85 3920
+#define VDISCHARGE_90 3960
+#define VDISCHARGE_95 4010
+#define VDISCHARGE_100 4070
+
+
+/* Battery charge voltage level (mV) */
+#define VCHARGE_5 3320
+#define VCHARGE_10 3430
+#define VCHARGE_15 3510
+#define VCHARGE_20 3580
+#define VCHARGE_25 3610
+#define VCHARGE_30 3640
+#define VCHARGE_35 3670
+#define VCHARGE_40 3690
+#define VCHARGE_45 3720
+#define VCHARGE_50 3740
+#define VCHARGE_55 3760
+#define VCHARGE_60 3780
+#define VCHARGE_65 3800
+#define VCHARGE_70 3830
+#define VCHARGE_75 3860
+#define VCHARGE_80 3890
+#define VCHARGE_85 3920
+#define VCHARGE_90 3960
+#define VCHARGE_95 4010
+#define VCHARGE_100 4070
+
+#define CAPACITY_LIMIT_HIGH 85
+#define CAPACITY_LIMIT_LOW 25
+#define CAPACITY_LIMIT_FULL 100
+
+#define BATTERY_TYPE POWER_SUPPLY_TECHNOLOGY_LION
+
#endif /* __KERNEL__ */
#endif /* __ASM_ARCH_MXC_PMIC_BATTERY_H__ */