aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorMykola Oleksiienko <x0174904@ti.com>2012-04-04 14:10:36 +0300
committerZiyann <jaraidaniel@gmail.com>2014-10-01 13:00:20 +0200
commitc61ed9aad21758a408c1e19b756f1761366768c9 (patch)
tree104ecb31e9ab46094d91eaebc9b7cf3982577b04 /drivers/power
parented7413d27476a41a0da7bd48ea59c679faaf04a3 (diff)
downloadkernel_samsung_tuna-c61ed9aad21758a408c1e19b756f1761366768c9.zip
kernel_samsung_tuna-c61ed9aad21758a408c1e19b756f1761366768c9.tar.gz
kernel_samsung_tuna-c61ed9aad21758a408c1e19b756f1761366768c9.tar.bz2
OMAP4: TWL6032: Stop USB charging if VBUS voltage below limit.
This patch adds ability to set VBUS limit (via sysfs node). If VBUS below this limit, device stops charging from USB (and start charging if device VBUS is above this limit). New sysfs node has been added: /sys/bus/platform/devices/twl6030_bci/vbus_charge_thres This node holds VBUS limit value in mV. Writing to this file cause setting vbus_charge_thres field in twl6030_bci_device_info structure. Main work is done by twl6030_usb_autogate_charger() function, which reads GPADC channel 10 value (VBUS voltage), compare it with limit value and tunrns on/off USB charger. This function is called from twl6030_bci_battery_work() function (also known as twl6030_bci_monitor_work workqueue). There is also additional check in twl6030_start_usb_charger() function, to prevent charging from low voltage USB source. Change-Id: I5b9fb557cf60d69588f8d1234cffb606f185e207 Signed-off-by: Mykola Oleksiienko <x0174904@ti.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/twl6030_bci_battery.c76
1 files changed, 74 insertions, 2 deletions
diff --git a/drivers/power/twl6030_bci_battery.c b/drivers/power/twl6030_bci_battery.c
index c0a17ca..45109d9 100644
--- a/drivers/power/twl6030_bci_battery.c
+++ b/drivers/power/twl6030_bci_battery.c
@@ -274,6 +274,7 @@ struct twl6030_bci_device_info {
u16 current_avg_interval;
u16 monitoring_interval;
unsigned int min_vbus;
+ unsigned int vbus_charge_thres;
struct twl4030_bci_platform_data *platform_data;
@@ -577,6 +578,11 @@ static int is_battery_present(struct twl6030_bci_device_info *di)
return 1;
}
+static inline int twl6030_vbus_above_thres(struct twl6030_bci_device_info *di)
+{
+ return (di->vbus_charge_thres < twl6030_get_gpadc_conversion(di, 10));
+}
+
static void twl6030_stop_usb_charger(struct twl6030_bci_device_info *di)
{
int ret;
@@ -604,6 +610,11 @@ static void twl6030_start_usb_charger(struct twl6030_bci_device_info *di)
if (di->charger_source == POWER_SUPPLY_TYPE_MAINS)
return;
+ if (!twl6030_vbus_above_thres(di)) {
+ twl6030_stop_usb_charger(di);
+ return;
+ }
+
if ((di->features & TWL6032_SUBCLASS) &&
di->platform_data->use_eeprom_config)
goto enable;
@@ -1458,6 +1469,30 @@ err:
pr_err("%s: Error access to TWL6030 (%d)\n", __func__, ret);
}
+static int twl6030_usb_autogate_charger(struct twl6030_bci_device_info *di)
+{
+ int ret = 0;
+
+ if ((di->charger_source == POWER_SUPPLY_TYPE_USB) &&
+ !twl6030_vbus_above_thres(di)) {
+
+ twl6030_stop_usb_charger(di);
+
+ if (di->ac_online == POWER_SUPPLY_TYPE_MAINS)
+ twl6030_start_ac_charger(di);
+
+ ret = 1;
+ } else if ((di->charger_source != POWER_SUPPLY_TYPE_MAINS) &&
+ di->usb_online) {
+
+ twl6030_start_usb_charger(di);
+
+ ret = 1;
+ }
+
+ return ret;
+}
+
static int capacity_changed(struct twl6030_bci_device_info *di)
{
int curr_capacity = di->capacity;
@@ -1566,7 +1601,7 @@ static void twl6030_bci_battery_work(struct work_struct *work)
struct twl6030_gpadc_request req;
int adc_code;
int temp;
- int ret;
+ int ret, ret1;
/* Kick the charger watchdog */
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING)
@@ -1612,7 +1647,10 @@ static void twl6030_bci_battery_work(struct work_struct *work)
di->temp_C = (temp - 2) * 10; /* in tenths of degree Celsius */
}
- if (capacity_changed(di))
+ ret = capacity_changed(di);
+ ret1 = twl6030_usb_autogate_charger(di);
+
+ if (ret || ret1)
power_supply_changed(&di->bat);
}
@@ -2295,6 +2333,37 @@ static ssize_t show_status_int2(struct device *dev,
return sprintf(buf, "%u\n", val);
}
+static ssize_t show_vbus_charge_thres(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int val;
+ struct twl6030_bci_device_info *di = dev_get_drvdata(dev);
+
+ val = di->vbus_charge_thres;
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t set_vbus_charge_thres(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long val;
+ int status = count;
+ struct twl6030_bci_device_info *di = dev_get_drvdata(dev);
+
+ /*
+ * Revisit: add limit range checking
+ */
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ di->vbus_charge_thres = val & 0xffffffff;
+
+ cancel_delayed_work(&di->twl6030_bci_monitor_work);
+ schedule_delayed_work(&di->twl6030_bci_monitor_work, 0);
+
+ return status;
+}
+
static DEVICE_ATTR(fg_mode, S_IWUSR | S_IRUGO, show_fg_mode, set_fg_mode);
static DEVICE_ATTR(charge_src, S_IWUSR | S_IRUGO, show_charge_src,
set_charge_src);
@@ -2326,6 +2395,8 @@ static DEVICE_ATTR(bsi, S_IRUGO, show_bsi, NULL);
static DEVICE_ATTR(stat1, S_IRUGO, show_stat1, NULL);
static DEVICE_ATTR(status_int1, S_IRUGO, show_status_int1, NULL);
static DEVICE_ATTR(status_int2, S_IRUGO, show_status_int2, NULL);
+static DEVICE_ATTR(vbus_charge_thres, S_IWUSR | S_IRUGO,
+ show_vbus_charge_thres, set_vbus_charge_thres);
static struct attribute *twl6030_bci_attributes[] = {
&dev_attr_fg_mode.attr,
@@ -2351,6 +2422,7 @@ static struct attribute *twl6030_bci_attributes[] = {
&dev_attr_status_int1.attr,
&dev_attr_status_int2.attr,
&dev_attr_wakelock_enable.attr,
+ &dev_attr_vbus_charge_thres.attr,
NULL,
};