summaryrefslogtreecommitdiff
path: root/drivers/power/ab8500_charger.c
diff options
context:
space:
mode:
authorKalle Komierowski <karl.komierowski@stericsson.com>2011-09-12 09:03:46 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 16:07:35 +0200
commitec1272eb00c6cc4666c9d24c336f6d87d8978fca (patch)
tree70d86dd22d07dc2351e5f6f5c97f4b99c3eb4a63 /drivers/power/ab8500_charger.c
parent0e323de5ae8774669daa88ee386408cdbaf51070 (diff)
power: ab8500_charger: Lower VBUS input current when below threshold
Due to a bug in AB8500 the input current to the VBUS charger needs to be lowered when the battery voltage is below 3,7 V. Otherwise there is a risk that a higher than allowed current is drawn from the USB host. ST-Ericsson ID: 338533 ST-Ericsson Linux next: Not tested ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I7f32b94411459a1665cc1c2890051f4d5290ed3d Signed-off-by: Kalle Komierowski <karl.komierowski@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30630 Reviewed-by: Johan PALSSON <johan.palsson@stericsson.com> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30883 Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com> Tested-by: Arun MURTHY <arun.murthy@stericsson.com>
Diffstat (limited to 'drivers/power/ab8500_charger.c')
-rw-r--r--drivers/power/ab8500_charger.c127
1 files changed, 126 insertions, 1 deletions
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 7463b5db04e..1b827a980ce 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -123,6 +123,8 @@ enum ab8500_usb_state {
#define USB_CH_IP_CUR_LVL_1P4 1400
#define USB_CH_IP_CUR_LVL_1P5 1500
+#define VBAT_3700 3700
+
#define to_ab8500_charger_usb_device_info(x) container_of((x), \
struct ab8500_charger, usb_chg)
#define to_ab8500_charger_ac_device_info(x) container_of((x), \
@@ -173,6 +175,8 @@ struct ab8500_charger_usb_state {
* VBUS detected during startup
* @ac_conn: This will be true when the AC charger has been plugged
* @vddadc_en: Indicate if VDD ADC supply is enabled from this driver
+ * @vbat Battery voltage
+ * @old_vbat Previously measured battery voltage
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @pdata: Pointer to the ab8500_charger platform data
@@ -185,6 +189,7 @@ struct ab8500_charger_usb_state {
* @usb: Structure that holds the USB charger properties
* @regu: Pointer to the struct regulator
* @charger_wq: Work queue for the IRQs and checking HW state
+ * @check_vbat_work Work for checking vbat threshold to adjust vbus current
* @check_hw_failure_work: Work for checking HW state
* @check_usbchgnotok_work: Work for checking USB charger not ok status
* @kick_wd_work: Work for kicking the charger watchdog in case
@@ -206,6 +211,8 @@ struct ab8500_charger {
bool vbus_detected_start;
bool ac_conn;
bool vddadc_en;
+ int vbat;
+ int old_vbat;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct ab8500_charger_platform_data *pdata;
@@ -218,6 +225,7 @@ struct ab8500_charger {
struct ab8500_charger_info usb;
struct regulator *regu;
struct workqueue_struct *charger_wq;
+ struct delayed_work check_vbat_work;
struct delayed_work check_hw_failure_work;
struct delayed_work check_usbchgnotok_work;
struct delayed_work kick_wd_work;
@@ -881,6 +889,19 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
/* We should always use to lowest current limit */
min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
+ switch (min_value) {
+ case 100:
+ if (di->vbat < VBAT_3700)
+ min_value = USB_CH_IP_CUR_LVL_0P05;
+ break;
+ case 500:
+ if (di->vbat < VBAT_3700)
+ min_value = USB_CH_IP_CUR_LVL_0P45;
+ break;
+ default:
+ break;
+ }
+
input_curr_index = ab8500_vbus_in_curr_to_regval(min_value);
if (input_curr_index < 0) {
dev_err(di->dev, "VBUS input current limit too high\n");
@@ -1174,6 +1195,8 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
if (ret < 0)
dev_err(di->dev, "failed to enable LED\n");
+ queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
+
di->usb.charger_online = 1;
} else {
/* Disable USB charging */
@@ -1193,6 +1216,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
di->usb.charger_online = 0;
di->usb.wd_expired = false;
dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+
+ /* Cancel any pending Vbat check work */
+ if (delayed_work_pending(&di->check_vbat_work))
+ cancel_delayed_work(&di->check_vbat_work);
+
}
power_supply_changed(&di->usb_chg.psy);
@@ -1274,6 +1302,101 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
return ret;
}
+static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_charger *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+ struct ux500_charger *usb_chg;
+
+ usb_chg = (struct ux500_charger *)data;
+ psy = &usb_chg->psy;
+
+ di = to_ab8500_charger_usb_device_info(usb_chg);
+
+ ext = dev_get_drvdata(dev);
+
+ /* For all psy where the driver name appears in any supplied_to */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->vbat = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_check_vbat_work() - keep vbus current within spec
+ * @work pointer to the work_struct structure
+ *
+ * Due to a asic bug it is necessary to lower the input current to the vbus
+ * charger when charging with at some specific levels. This issue is only valid
+ * for below a certain battery voltage. This function makes sure that the
+ * the allowed current limit isn't exceeded.
+ */
+static void ab8500_charger_check_vbat_work(struct work_struct *work)
+{
+ int t = 10;
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_vbat_work.work);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
+
+ /* First run old_vbat is 0. */
+ if (di->old_vbat == 0)
+ di->old_vbat = di->vbat;
+
+ if (!((di->old_vbat <= VBAT_3700 && di->vbat <= VBAT_3700) ||
+ (di->old_vbat > VBAT_3700 && di->vbat > VBAT_3700))) {
+ dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
+ " old: %d\n", di->max_usb_in_curr, di->vbat,
+ di->old_vbat);
+ ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ power_supply_changed(&di->usb_chg.psy);
+ }
+
+ di->old_vbat = di->vbat;
+
+ /*
+ * No need to check the battery voltage every second when not close to
+ * the threshold.
+ */
+ if (di->vbat < (VBAT_3700 + 100) &&
+ (di->vbat > (VBAT_3700 - 100)))
+ t = 1;
+
+ queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
+}
+
/**
* ab8500_charger_check_hw_failure_work() - check main charger failure
* @work: pointer to the work_struct structure
@@ -2398,7 +2521,6 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
ab8500_charger_check_usbchargernotok_work);
-
/*
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog
* logic. That means we have to continously kick the charger
@@ -2411,6 +2533,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
INIT_DELAYED_WORK_DEFERRABLE(&di->kick_wd_work,
ab8500_charger_kick_watchdog_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work,
+ ab8500_charger_check_vbat_work);
+
/* Init work for charger detection */
INIT_WORK(&di->usb_link_status_work,
ab8500_charger_usb_link_status_work);