-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
power: Add twl4030_madc battery driver.
This driver is used and tested on gta04 phone. It's using twl4030_madc (similar to rx51 existing driver). Driver also implement charging and discharging calibration data so user can define ranges and level. Signed-off-by: Marek Belisko <marek@goldelico.com> Signed-off-by: Lukas Märdian <lukas@goldelico.com> Signed-off-by: Anton Vorontsov <anton@enomsg.org>
- Loading branch information
Marek Belisko
authored and
Anton Vorontsov
committed
Aug 31, 2013
1 parent
d24fed3
commit da0a00e
Showing
4 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
/* | ||
* Dumb driver for LiIon batteries using TWL4030 madc. | ||
* | ||
* Copyright 2013 Golden Delicious Computers | ||
* Lukas Märdian <lukas@goldelico.com> | ||
* | ||
* Based on dumb driver for gta01 battery | ||
* Copyright 2009 Openmoko, Inc | ||
* Balaji Rao <balajirrao@openmoko.org> | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/param.h> | ||
#include <linux/delay.h> | ||
#include <linux/workqueue.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/power_supply.h> | ||
#include <linux/slab.h> | ||
#include <linux/sort.h> | ||
#include <linux/i2c/twl4030-madc.h> | ||
#include <linux/power/twl4030_madc_battery.h> | ||
|
||
struct twl4030_madc_battery { | ||
struct power_supply psy; | ||
struct twl4030_madc_bat_platform_data *pdata; | ||
}; | ||
|
||
static enum power_supply_property twl4030_madc_bat_props[] = { | ||
POWER_SUPPLY_PROP_PRESENT, | ||
POWER_SUPPLY_PROP_STATUS, | ||
POWER_SUPPLY_PROP_TECHNOLOGY, | ||
POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
POWER_SUPPLY_PROP_CURRENT_NOW, | ||
POWER_SUPPLY_PROP_CAPACITY, | ||
POWER_SUPPLY_PROP_CHARGE_FULL, | ||
POWER_SUPPLY_PROP_CHARGE_NOW, | ||
POWER_SUPPLY_PROP_TEMP, | ||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, | ||
}; | ||
|
||
static int madc_read(int index) | ||
{ | ||
struct twl4030_madc_request req; | ||
int val; | ||
|
||
req.channels = index; | ||
req.method = TWL4030_MADC_SW2; | ||
req.type = TWL4030_MADC_WAIT; | ||
req.do_avg = 0; | ||
req.raw = false; | ||
req.func_cb = NULL; | ||
|
||
val = twl4030_madc_conversion(&req); | ||
if (val < 0) | ||
return val; | ||
|
||
return req.rbuf[ffs(index) - 1]; | ||
} | ||
|
||
static int twl4030_madc_bat_get_charging_status(void) | ||
{ | ||
return (madc_read(TWL4030_MADC_ICHG) > 0) ? 1 : 0; | ||
} | ||
|
||
static int twl4030_madc_bat_get_voltage(void) | ||
{ | ||
return madc_read(TWL4030_MADC_VBAT); | ||
} | ||
|
||
static int twl4030_madc_bat_get_current(void) | ||
{ | ||
return madc_read(TWL4030_MADC_ICHG) * 1000; | ||
} | ||
|
||
static int twl4030_madc_bat_get_temp(void) | ||
{ | ||
return madc_read(TWL4030_MADC_BTEMP) * 10; | ||
} | ||
|
||
static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, | ||
int volt) | ||
{ | ||
struct twl4030_madc_bat_calibration *calibration; | ||
int i, res = 0; | ||
|
||
/* choose charging curve */ | ||
if (twl4030_madc_bat_get_charging_status()) | ||
calibration = bat->pdata->charging; | ||
else | ||
calibration = bat->pdata->discharging; | ||
|
||
if (volt > calibration[0].voltage) { | ||
res = calibration[0].level; | ||
} else { | ||
for (i = 0; calibration[i+1].voltage >= 0; i++) { | ||
if (volt <= calibration[i].voltage && | ||
volt >= calibration[i+1].voltage) { | ||
/* interval found - interpolate within range */ | ||
res = calibration[i].level - | ||
((calibration[i].voltage - volt) * | ||
(calibration[i].level - | ||
calibration[i+1].level)) / | ||
(calibration[i].voltage - | ||
calibration[i+1].voltage); | ||
break; | ||
} | ||
} | ||
} | ||
return res; | ||
} | ||
|
||
static int twl4030_madc_bat_get_property(struct power_supply *psy, | ||
enum power_supply_property psp, | ||
union power_supply_propval *val) | ||
{ | ||
struct twl4030_madc_battery *bat = container_of(psy, | ||
struct twl4030_madc_battery, psy); | ||
|
||
switch (psp) { | ||
case POWER_SUPPLY_PROP_STATUS: | ||
if (twl4030_madc_bat_voltscale(bat, | ||
twl4030_madc_bat_get_voltage()) > 95) | ||
val->intval = POWER_SUPPLY_STATUS_FULL; | ||
else { | ||
if (twl4030_madc_bat_get_charging_status()) | ||
val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
else | ||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
} | ||
break; | ||
case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
val->intval = twl4030_madc_bat_get_voltage() * 1000; | ||
break; | ||
case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
break; | ||
case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
val->intval = twl4030_madc_bat_get_current(); | ||
break; | ||
case POWER_SUPPLY_PROP_PRESENT: | ||
/* assume battery is always present */ | ||
val->intval = 1; | ||
break; | ||
case POWER_SUPPLY_PROP_CHARGE_NOW: { | ||
int percent = twl4030_madc_bat_voltscale(bat, | ||
twl4030_madc_bat_get_voltage()); | ||
val->intval = (percent * bat->pdata->capacity) / 100; | ||
break; | ||
} | ||
case POWER_SUPPLY_PROP_CAPACITY: | ||
val->intval = twl4030_madc_bat_voltscale(bat, | ||
twl4030_madc_bat_get_voltage()); | ||
break; | ||
case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
val->intval = bat->pdata->capacity; | ||
break; | ||
case POWER_SUPPLY_PROP_TEMP: | ||
val->intval = twl4030_madc_bat_get_temp(); | ||
break; | ||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: { | ||
int percent = twl4030_madc_bat_voltscale(bat, | ||
twl4030_madc_bat_get_voltage()); | ||
/* in mAh */ | ||
int chg = (percent * (bat->pdata->capacity/1000))/100; | ||
|
||
/* assume discharge with 400 mA (ca. 1.5W) */ | ||
val->intval = (3600l * chg) / 400; | ||
break; | ||
} | ||
default: | ||
return -EINVAL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static void twl4030_madc_bat_ext_changed(struct power_supply *psy) | ||
{ | ||
struct twl4030_madc_battery *bat = container_of(psy, | ||
struct twl4030_madc_battery, psy); | ||
|
||
power_supply_changed(&bat->psy); | ||
} | ||
|
||
static int twl4030_cmp(const void *a, const void *b) | ||
{ | ||
return ((struct twl4030_madc_bat_calibration *)b)->voltage - | ||
((struct twl4030_madc_bat_calibration *)a)->voltage; | ||
} | ||
|
||
static int twl4030_madc_battery_probe(struct platform_device *pdev) | ||
{ | ||
struct twl4030_madc_battery *twl4030_madc_bat; | ||
struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data; | ||
|
||
twl4030_madc_bat = kzalloc(sizeof(*twl4030_madc_bat), GFP_KERNEL); | ||
if (!twl4030_madc_bat) | ||
return -ENOMEM; | ||
|
||
twl4030_madc_bat->psy.name = "twl4030_battery"; | ||
twl4030_madc_bat->psy.type = POWER_SUPPLY_TYPE_BATTERY; | ||
twl4030_madc_bat->psy.properties = twl4030_madc_bat_props; | ||
twl4030_madc_bat->psy.num_properties = | ||
ARRAY_SIZE(twl4030_madc_bat_props); | ||
twl4030_madc_bat->psy.get_property = twl4030_madc_bat_get_property; | ||
twl4030_madc_bat->psy.external_power_changed = | ||
twl4030_madc_bat_ext_changed; | ||
|
||
/* sort charging and discharging calibration data */ | ||
sort(pdata->charging, pdata->charging_size, | ||
sizeof(struct twl4030_madc_bat_calibration), | ||
twl4030_cmp, NULL); | ||
sort(pdata->discharging, pdata->discharging_size, | ||
sizeof(struct twl4030_madc_bat_calibration), | ||
twl4030_cmp, NULL); | ||
|
||
twl4030_madc_bat->pdata = pdata; | ||
platform_set_drvdata(pdev, twl4030_madc_bat); | ||
power_supply_register(&pdev->dev, &twl4030_madc_bat->psy); | ||
|
||
return 0; | ||
} | ||
|
||
static int twl4030_madc_battery_remove(struct platform_device *pdev) | ||
{ | ||
struct twl4030_madc_battery *bat = platform_get_drvdata(pdev); | ||
|
||
power_supply_unregister(&bat->psy); | ||
kfree(bat); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver twl4030_madc_battery_driver = { | ||
.driver = { | ||
.name = "twl4030_madc_battery", | ||
}, | ||
.probe = twl4030_madc_battery_probe, | ||
.remove = twl4030_madc_battery_remove, | ||
}; | ||
module_platform_driver(twl4030_madc_battery_driver); | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); | ||
MODULE_DESCRIPTION("twl4030_madc battery driver"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Dumb driver for LiIon batteries using TWL4030 madc. | ||
* | ||
* Copyright 2013 Golden Delicious Computers | ||
* Nikolaus Schaller <hns@goldelico.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License as published by the | ||
* Free Software Foundation; either version 2 of the License, or (at your | ||
* option) any later version. | ||
* | ||
* You should have received a copy of the GNU General Public License along | ||
* with this program; if not, write to the Free Software Foundation, Inc., | ||
* 675 Mass Ave, Cambridge, MA 02139, USA. | ||
* | ||
*/ | ||
|
||
#ifndef __TWL4030_MADC_BATTERY_H | ||
#define __TWL4030_MADC_BATTERY_H | ||
|
||
/* | ||
* Usually we can assume 100% @ 4.15V and 0% @ 3.3V but curves differ for | ||
* charging and discharging! | ||
*/ | ||
|
||
struct twl4030_madc_bat_calibration { | ||
short voltage; /* in mV - specify -1 for end of list */ | ||
short level; /* in percent (0 .. 100%) */ | ||
}; | ||
|
||
struct twl4030_madc_bat_platform_data { | ||
unsigned int capacity; /* total capacity in uAh */ | ||
struct twl4030_madc_bat_calibration *charging; | ||
int charging_size; | ||
struct twl4030_madc_bat_calibration *discharging; | ||
int discharging_size; | ||
}; | ||
|
||
#endif |