summaryrefslogtreecommitdiff
path: root/drivers/power/ab8500_charger.c
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-05 14:24:13 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:15:00 +0200
commite22bc14269f69aeb1952f95268d9017c693d9345 (patch)
tree2bb5e80481bc4e6dd1348b8f2da10104eb0218c4 /drivers/power/ab8500_charger.c
parent0bd6061aec5473a0a8f03c6813d7a8460d8b71af (diff)
power: ab8500_charger: Keep VDD ADC supply enabled if charger is connected
Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts will be triggered everytime we enable the VDD ADC supply. This will turn off charging for a short while. It will be avoided by having the regulator on when there is a charger connected. ST-Ericsson Linux next: - ST-Ericsson ID: ER329557 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I2a084945430f1e0a996526a194d898db13c54a29 Signed-off-by: Johan Palsson <johan.palsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/18610 Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com> Conflicts: arch/arm/mach-ux500/board-mop500-regulators.c
Diffstat (limited to 'drivers/power/ab8500_charger.c')
-rw-r--r--drivers/power/ab8500_charger.c58
1 files changed, 54 insertions, 4 deletions
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 47be3e089ff..96859456ef7 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -18,6 +18,8 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/mfd/ab8500.h>
@@ -169,6 +171,7 @@ struct ab8500_charger_usb_state {
* @vbus_detected_start:
* 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
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @pdata: Pointer to the ab8500_charger platform data
@@ -179,6 +182,7 @@ struct ab8500_charger_usb_state {
* @usb_chg: USB charger power supply
* @ac: Structure that holds the AC charger properties
* @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_hw_failure_work: Work for checking HW state
* @check_usbchgnotok_work: Work for checking USB charger not ok status
@@ -200,6 +204,7 @@ struct ab8500_charger {
bool vbus_detected;
bool vbus_detected_start;
bool ac_conn;
+ bool vddadc_en;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct ab8500_charger_platform_data *pdata;
@@ -210,6 +215,7 @@ struct ab8500_charger {
struct ux500_charger usb_chg;
struct ab8500_charger_info ac;
struct ab8500_charger_info usb;
+ struct regulator *regu;
struct workqueue_struct *charger_wq;
struct delayed_work check_hw_failure_work;
struct delayed_work check_usbchgnotok_work;
@@ -419,7 +425,7 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
AB8500_CH_STATUS1_REG, &val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
- return ret;
+ goto out;
}
if (val & MAIN_CH_DET)
@@ -430,13 +436,39 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
AB8500_CH_USBCH_STAT1_REG, &val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
- return ret;
+ goto out;
}
if (val & (VBUS_DET_DBNC100 | VBUS_DET_DBNC1))
result |= USB_PW_CONN;
+ /*
+ * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+ * will be triggered everytime we enable the VDD ADC supply.
+ * This will turn off charging for a short while.
+ * It can be avoided by having the supply on when
+ * there is a charger connected. Normally the VDD ADC supply
+ * is enabled everytime a GPADC conversion is triggered. We will
+ * force it to be enabled from this driver to have
+ * the GPADC module independant of the AB8500 chargers
+ */
+ if (result == NO_PW_CONN && di->vddadc_en) {
+ regulator_disable(di->regu);
+ di->vddadc_en = false;
+ } else if ((result & AC_PW_CONN || result & USB_PW_CONN) &&
+ !di->vddadc_en) {
+ regulator_enable(di->regu);
+ di->vddadc_en = true;
+ }
+
return result;
+
+out:
+ if (di->vddadc_en) {
+ regulator_disable(di->regu);
+ di->vddadc_en = false;
+ }
+ return ret;
}
/**
@@ -2247,6 +2279,9 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
free_irq(irq, di);
}
+ /* disable the regulator */
+ regulator_put(di->regu);
+
/* Backup battery voltage and current disable */
ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
@@ -2390,18 +2425,31 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
di->chip_id = ret;
dev_dbg(di->dev, "AB8500 CID is: 0x%02x\n", di->chip_id);
+ /*
+ * VDD ADC supply needs to be enabled from this driver when there
+ * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+ * interrupts during charging
+ */
+ di->regu = regulator_get(di->dev, "vddadc");
+ if (IS_ERR(di->regu)) {
+ ret = PTR_ERR(di->regu);
+ dev_err(di->dev, "failed to get vddadc regulator\n");
+ goto free_charger_wq;
+ }
+
+
/* Initialize OVV, and other registers */
ret = ab8500_charger_init_hw_registers(di);
if (ret) {
dev_err(di->dev, "failed to initialize ABB registers\n");
- goto free_charger_wq;
+ goto free_regulator;
}
/* Register AC charger class */
ret = power_supply_register(di->dev, &di->ac_chg.psy);
if (ret) {
dev_err(di->dev, "failed to register AC charger\n");
- goto free_charger_wq;
+ goto free_regulator;
}
/* Register USB charger class */
@@ -2474,6 +2522,8 @@ free_usb:
power_supply_unregister(&di->usb_chg.psy);
free_ac:
power_supply_unregister(&di->ac_chg.psy);
+free_regulator:
+ regulator_put(di->regu);
free_charger_wq:
destroy_workqueue(di->charger_wq);
free_device_info: