From patchwork Thu Sep 27 20:08:54 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Poirier X-Patchwork-Id: 11810 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id BD3E524140 for ; Thu, 27 Sep 2012 20:10:10 +0000 (UTC) Received: from mail-ie0-f180.google.com (mail-ie0-f180.google.com [209.85.223.180]) by fiordland.canonical.com (Postfix) with ESMTP id 6C0B8A18351 for ; Thu, 27 Sep 2012 20:10:10 +0000 (UTC) Received: by mail-ie0-f180.google.com with SMTP id e10so5234855iej.11 for ; Thu, 27 Sep 2012 13:10:10 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to :subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=qHrsYT50+EhO83YAQYYNrgc813UPpFeh8GuHCK65ljI=; b=ahWZWWgUMkol/WoTDTm4xXImjqXc9+NO6RxaAW3MG9/lw1IgprQ2v0soF22byce4xU SO5Dl51m3CqFfVx73HK7PbQ7TygrafKHU9j2vQG6nNhmYsVqQUoUdewfWNBRephrHCla me2v5/IODg/AqsRu+K3uqtteNxtlc7x60TTDi0HTObYgFnwOVmRu9mIKXyLSxQgCI3Q1 jaHbmChjLVLD3utE7s9Bpd3FBp4THHneNew5Q4N9ZdC0HWI2k9RVXO7bvmVeuksHQmSG AOyrZy5gbXPgowsCmanX1UXMTzsYApzUL6RBHAg/S6rmzBC1w2T+Ka7OJPuyD05E4wL/ xKzQ== Received: by 10.50.217.229 with SMTP id pb5mr4699521igc.28.1348776610165; Thu, 27 Sep 2012 13:10:10 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.184.232 with SMTP id ex8csp436356igc; Thu, 27 Sep 2012 13:10:09 -0700 (PDT) Received: by 10.68.135.234 with SMTP id pv10mr14203854pbb.156.1348776609173; Thu, 27 Sep 2012 13:10:09 -0700 (PDT) Received: from mail-pa0-f50.google.com (mail-pa0-f50.google.com [209.85.220.50]) by mx.google.com with ESMTPS id vn9si8390651pbc.17.2012.09.27.13.10.09 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:10:09 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) client-ip=209.85.220.50; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) smtp.mail=mathieu.poirier@linaro.org Received: by mail-pa0-f50.google.com with SMTP id hz11so1847989pad.37 for ; Thu, 27 Sep 2012 13:10:09 -0700 (PDT) Received: by 10.68.204.66 with SMTP id kw2mr14402367pbc.110.1348776608926; Thu, 27 Sep 2012 13:10:08 -0700 (PDT) Received: from localhost.localdomain (S0106002369de4dac.cg.shawcable.net. [70.73.24.112]) by mx.google.com with ESMTPS id sa2sm1587890pbc.4.2012.09.27.13.10.07 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:10:07 -0700 (PDT) From: mathieu.poirier@linaro.org To: patches@linaro.org Subject: [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour Date: Thu, 27 Sep 2012 14:08:54 -0600 Message-Id: <1348776554-10019-38-git-send-email-mathieu.poirier@linaro.org> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> References: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> X-Gm-Message-State: ALoCoQmsgpd86FK0B+5sWFMULCrdrTR0xWpuKQYipDVjfzVx466llCwi9ByQACmUmZJi5cuhn+9h From: Kalle Komierowski Due to a bug in some AB8500 ASICs charger removal cannot always be detected if the removal and reinsertion is done to close in time. This patch detects above described case and handles the situation so that charging will be kept turned on. Signed-off-by: Kalle Komierowski Signed-off-by: Mathieu Poirier Reviewed-by: Marcus COOPER Reviewed-by: Jonas ABERG Reviewed-by: Philippe LANGLAIS --- drivers/power/ab8500_charger.c | 105 ++++++++++++++++++++++++++++- drivers/power/abx500_chargalg.c | 31 ++++++++- include/linux/mfd/abx500/ux500_chargalg.h | 1 + 3 files changed, 134 insertions(+), 3 deletions(-) diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 8137ea5..70e7c5e 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -49,6 +49,7 @@ #define VBUS_DET_DBNC100 0x02 #define VBUS_DET_DBNC1 0x01 #define OTP_ENABLE_WD 0x01 +#define DROP_COUNT_RESET 0x01 #define MAIN_CH_INPUT_CURR_SHIFT 4 #define VBUS_IN_CURR_LIM_SHIFT 4 @@ -1672,6 +1673,105 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, } /** + * ab8500_charger_usb_check_enable() - enable usb charging + * @charger: pointer to the ux500_charger structure + * @vset: charging voltage + * @iset: charger output current + * + * Check if the VBUS charger has been disconnected and reconnected without + * AB8500 rising an interrupt. Returns 0 on success. + */ +static int ab8500_charger_usb_check_enable(struct ux500_charger *charger, + int vset, int iset) +{ + u8 usbch_ctrl1 = 0; + int ret = 0; + + struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger); + + if (!di->usb.charger_connected) + return ret; + + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_USBCH_CTRL1_REG, &usbch_ctrl1); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", __LINE__); + return ret; + } + dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1); + + if (!(usbch_ctrl1 & USB_CH_ENA)) { + dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n"); + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CHARGER_CTRL, + DROP_COUNT_RESET, DROP_COUNT_RESET); + if (ret < 0) { + dev_err(di->dev, "ab8500 write failed %d\n", __LINE__); + return ret; + } + + ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset); + if (ret < 0) { + dev_err(di->dev, "Failed to enable VBUS charger %d\n", + __LINE__); + return ret; + } + } + return ret; +} + +/** + * ab8500_charger_ac_check_enable() - enable usb charging + * @charger: pointer to the ux500_charger structure + * @vset: charging voltage + * @iset: charger output current + * + * Check if the AC charger has been disconnected and reconnected without + * AB8500 rising an interrupt. Returns 0 on success. + */ +static int ab8500_charger_ac_check_enable(struct ux500_charger *charger, + int vset, int iset) +{ + u8 mainch_ctrl1 = 0; + int ret = 0; + + struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger); + + if (!di->ac.charger_connected) + return ret; + + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_MCH_CTRL1, &mainch_ctrl1); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", __LINE__); + return ret; + } + dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1); + + if (!(mainch_ctrl1 & MAIN_CH_ENA)) { + dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n"); + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CHARGER_CTRL, + DROP_COUNT_RESET, DROP_COUNT_RESET); + + if (ret < 0) { + dev_err(di->dev, "ab8500 write failed %d\n", __LINE__); + return ret; + } + + ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset); + if (ret < 0) { + dev_err(di->dev, "failed to enable AC charger %d\n", + __LINE__); + return ret; + } + } + return ret; +} + +/** * ab8500_charger_watchdog_kick() - kick charger watchdog * @di: pointer to the ab8500_charger structure * @@ -1728,8 +1828,7 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, /* Reset the main and usb drop input current measurement counter */ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, - AB8500_CHARGER_CTRL, - 0x1); + AB8500_CHARGER_CTRL, DROP_COUNT_RESET); if (ret) { dev_err(di->dev, "%s write failed\n", __func__); return ret; @@ -3197,6 +3296,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants; /* ux500_charger sub-class */ di->ac_chg.ops.enable = &ab8500_charger_ac_en; + di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current; di->ac_chg.max_out_volt = ab8500_charger_voltage_map[ @@ -3218,6 +3318,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants; /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; + di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current; di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 4e3d20f..9568f63 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -311,6 +311,29 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di, di->charge_state = state; } +static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) +{ + switch (di->charge_state) { + case STATE_NORMAL: + case STATE_MAINTENANCE_A: + case STATE_MAINTENANCE_B: + break; + default: + return 0; + } + + if (di->chg_info.charger_type & USB_CHG) { + return di->usb_chg->ops.check_enable(di->usb_chg, + di->bat->bat_type[di->bat->batt_id].normal_vol_lvl, + di->bat->bat_type[di->bat->batt_id].normal_cur_lvl); + } else if (di->chg_info.charger_type & AC_CHG) { + return di->ac_chg->ops.check_enable(di->ac_chg, + di->bat->bat_type[di->bat->batt_id].normal_vol_lvl, + di->bat->bat_type[di->bat->batt_id].normal_cur_lvl); + } + return -ENXIO; +} + /** * abx500_chargalg_check_charger_connection() - Check charger connection change * @di: pointer to the abx500_chargalg structure @@ -1220,6 +1243,7 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy) static void abx500_chargalg_algorithm(struct abx500_chargalg *di) { int charger_status; + int ret; /* Collect data from all power_supply class devices */ class_for_each_device(power_supply_class, NULL, @@ -1228,8 +1252,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) abx500_chargalg_end_of_charge(di); abx500_chargalg_check_temp(di); abx500_chargalg_check_charger_voltage(di); - charger_status = abx500_chargalg_check_charger_connection(di); + + ret = abx500_chargalg_check_charger_enable(di); + if (ret < 0) + dev_err(di->dev, "Checking charger if enabled error: %d line: %d\n", + ret, __LINE__); + /* * First check if we have a charger connected. * Also we don't allow charging of unknown batteries if configured diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h index d43ac0f..110d12f 100644 --- a/include/linux/mfd/abx500/ux500_chargalg.h +++ b/include/linux/mfd/abx500/ux500_chargalg.h @@ -17,6 +17,7 @@ struct ux500_charger; struct ux500_charger_ops { int (*enable) (struct ux500_charger *, int, int, int); + int (*check_enable) (struct ux500_charger *, int, int); int (*kick_wd) (struct ux500_charger *); int (*update_curr) (struct ux500_charger *, int); };