linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/57] power: Upgrade to ux500 battery management driver
@ 2012-09-25 16:11 mathieu.poirier
  2012-09-25 16:11 ` [PATCH 01/57] power: ab8500_bm: Charger current step-up/down mathieu.poirier
                   ` (57 more replies)
  0 siblings, 58 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:11 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

This patch set upgrades the current ux500 battery management driver
to the latest HW and functionality.

Pull request for convenience:

The following changes since commit 56d27adcb536b7430d5f8a6240df8ad261eb00bd:

  Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile (2012-09-24 16:17:17 -0700)

are available in the git repository at:

  git://git.linaro.org/people/mpoirier/linux.git ux500-battery-management

Daniel WILLERUD (1):
  power: Add sysfs interfaces for capacity

Hakan Berg (10):
  power: ab8500_bm: Ignore false btemp low interrupt
  power: Adds support for Car/Travel Adapters
  power: bm remove superfluous BTEMP thermal comp.
  power: ab8500_bm: Added support for BATT_OVV
  power: ab8500_fg: Adjust for RF bursts voltage drops.
  power: ab8500_btemp: Filter btemp readings
  power: charging: Allow capacity to raise from 1%
  power: charging: Add AB8505_USB_LINK_STATUS
  power: ab8500-chargalg: update battery health on safety timer exp
  power: abx500_chargalg: Use hrtimer

Henrik Sölver (1):
  power: AB workaround for invalid charger

Johan Bjornstedt (2):
  power: ab8500_bm: Charger current step-up/down
  power: ab8500_bm: Skip first CCEOC irq for instant current

Jonas Aaberg (7):
  power: ab8500_btemp: Detect battery type in workqueue
  power: ab8500_bm: Detect removed charger
  power: ab8500_fg: flush sync on suspend
  power: ab8500_fg: usleep_range instead of short msleep
  power: ab8500_charger: Handle gpadc errors
  power: ab8500: Flush & sync all works
  power: ab8500_charger: Do not touch VBUSOVV bits

Kalle Komierowski (2):
  power: ab8500_bm: Don't clear the CCMuxOffset bit
  power: ab8500_bm: Quick re-attach charging behaviour

Loic Pallardy (3):
  power: Add plaform data charger configurables
  power: charge: update watchdog for pm2xxx support
  power: chargealg: Realign with upstream version

Marcus Cooper (8):
  power: Recharge condition not optimal for battery
  power: remove unused defines.
  power: Adds support for legacy USB chargers
  power: ab8500: ADC for battery thermistor
  power: ab8500: remove unecesary define flag
  power: ab8500_charger: Use USBLink1Status Register
  power: ab8500_charger: Add UsbLineCtrl2 reference
  power: abx500_chargalg: Fix quick re-attach charger issue.

Martin Bergstrom (2):
  power: ab8500_fg: Report unscaled capacity
  power: ab8500_charger: Limit USB charger current

Martin Bergström (1):
  power: ab8500_fg: Goto INIT_RECOVERY when charger removed

Martin Sjoblom (1):
  power: ab8500_charger: Prevent auto drop of VBUS

Mathieu J. Poirier (4):
  power: Harmonising platform data declaration/handling
  power: Cancelling status charging notification.
  power: ab8500: Re-alignment with internal developement.
  power: ab8500_fg: Moving structure definitions to header file

Michel JAOUEN (2):
  power: ab8500: adaptation to ab version
  power: sysfs interface update

Nicolas Guion (1):
  power: ab8500 - Accessing Autopower register fails

Paer-Olof Haakansson (3):
  power: ab8500_bm: Rename the power_loss function
  power: Overflow in current calculation
  power: u8500_charger: Delay for USB enumeration

Philippe Langlais (1):
  power: ab8500: bm: movimg back to ab8500 platform data managment

Rajkumar Kasirajan (1):
  power: ab8500_fg: fix to use correct battery charge full design

Rickard Andersson (1):
  power: ab8500_fg: balance IRQ enable

Rikard Olsson (1):
  power: ab8500_fg: add power cut feature for ab8505

Rupesh Kumar (3):
  power: l9540: Charge only mode fixes
  power: ab8500: defer btemp filtering while init
  power: ab8500 : quick re-attach for ext charger

Yang QU (1):
  power: add backup battery charge voltages.

pender01 (1):
  power: ab8500_fg: Round capacity output

 drivers/mfd/ab8500-core.c                 |    6 +
 drivers/power/Kconfig                     |    7 -
 drivers/power/ab8500_btemp.c              |  165 +++--
 drivers/power/ab8500_charger.c            | 1410 +++++++++++++++++++++++------
 drivers/power/ab8500_fg.c                 | 1085 +++++++++++++++++------
 drivers/power/ab8500_fg.h                 |  201 ++++
 drivers/power/abx500_chargalg.c           |  333 ++++++--
 include/linux/mfd/abx500.h                |   30 +-
 include/linux/mfd/abx500/ab8500-bm.h      |   47 +-
 include/linux/mfd/abx500/ab8500.h         |   22 +-
 include/linux/mfd/abx500/ux500_chargalg.h |    8 +
 11 files changed, 2607 insertions(+), 707 deletions(-)
 create mode 100644 drivers/power/ab8500_fg.h
-- 
1.7.5.4


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH 01/57] power: ab8500_bm: Charger current step-up/down
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
@ 2012-09-25 16:11 ` mathieu.poirier
  2012-09-28  3:41   ` Anton Vorontsov
  2012-09-25 16:11 ` [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit mathieu.poirier
                   ` (56 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:11 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Johan Bjornstedt <johan.bjornstedt@stericsson.com>

There is no state machine in the AB to step up/down
the charger current to avoid dips and spikes on VBUS
and VBAT when charging is started.
Instead this is implemented in SW

Signed-off-by: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
Signed-off-by: Mattias Wallin <mattias.wallin@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_charger.c |  172 +++++++++++++++++++++++++++++++---------
 1 files changed, 133 insertions(+), 39 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index d4f0c98..3ceb788 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -77,6 +77,9 @@
 /* Lowest charger voltage is 3.39V -> 0x4E */
 #define LOW_VOLT_REG			0x4E
 
+/* Step up/down delay in us */
+#define STEP_UDELAY			1000
+
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
 	USB_STAT_NOT_CONFIGURED,
@@ -934,6 +937,88 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
 }
 
 /**
+ * ab8500_charger_set_current() - set charger current
+ * @di:		pointer to the ab8500_charger structure
+ * @ich:	charger current, in mA
+ * @reg:	select what charger register to set
+ *
+ * Set charger current.
+ * There is no state machine in the AB to step up/down the charger
+ * current to avoid dips and spikes on MAIN, VBUS and VBAT when
+ * charging is started. Instead we need to implement
+ * this charger current step-up/down here.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_current(struct ab8500_charger *di,
+	int ich, int reg)
+{
+	int ret, i;
+	int curr_index, prev_curr_index, shift_value;
+	u8 reg_value;
+
+	switch (reg) {
+	case AB8500_MCH_IPT_CURLVL_REG:
+		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+		curr_index = ab8500_current_to_regval(ich);
+		break;
+	case AB8500_USBCH_IPT_CRNTLVL_REG:
+		shift_value = VBUS_IN_CURR_LIM_SHIFT;
+		curr_index = ab8500_vbus_in_curr_to_regval(ich);
+		break;
+	case AB8500_CH_OPT_CRNTLVL_REG:
+		shift_value = 0;
+		curr_index = ab8500_current_to_regval(ich);
+		break;
+	default:
+		dev_err(di->dev, "%s current register not valid\n", __func__);
+		return -ENXIO;
+	}
+
+	if (curr_index < 0) {
+		dev_err(di->dev, "requested current limit out-of-range\n");
+		return -ENXIO;
+	}
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+		reg, &reg_value);
+	if (ret < 0) {
+		dev_err(di->dev, "%s read failed\n", __func__);
+		return ret;
+	}
+	prev_curr_index = (reg_value >> shift_value);
+
+	/* only update current if it's been changed */
+	if (prev_curr_index == curr_index)
+		return 0;
+
+	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
+		__func__, ich, reg);
+
+	if (prev_curr_index > curr_index) {
+		for (i = prev_curr_index - 1; i >= curr_index; i--) {
+			ret = abx500_set_register_interruptible(di->dev,
+				AB8500_CHARGER, reg, (u8) i << shift_value);
+			if (ret) {
+				dev_err(di->dev, "%s write failed\n", __func__);
+				return ret;
+			}
+			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+		}
+	} else {
+		for (i = prev_curr_index + 1; i <= curr_index; i++) {
+			ret = abx500_set_register_interruptible(di->dev,
+				AB8500_CHARGER, reg, (u8) i << shift_value);
+			if (ret) {
+				dev_err(di->dev, "%s write failed\n", __func__);
+				return ret;
+			}
+			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+		}
+	}
+	return ret;
+}
+
+/**
  * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
  * @di:		pointer to the ab8500_charger structure
  * @ich_in:	charger input current limit
@@ -944,8 +1029,6 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
 static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 		int ich_in)
 {
-	int ret;
-	int input_curr_index;
 	int min_value;
 
 	/* We should always use to lowest current limit */
@@ -964,19 +1047,38 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 		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");
-		return -ENXIO;
-	}
+	return ab8500_charger_set_current(di, min_value,
+		AB8500_USBCH_IPT_CRNTLVL_REG);
+}
 
-	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-		AB8500_USBCH_IPT_CRNTLVL_REG,
-		input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
-	if (ret)
-		dev_err(di->dev, "%s write failed\n", __func__);
+/**
+ * ab8500_charger_set_main_in_curr() - set main charger input current
+ * @di:		pointer to the ab8500_charger structure
+ * @ich_in:	input charger current, in mA
+ *
+ * Set main charger input current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
+	int ich_in)
+{
+	return ab8500_charger_set_current(di, ich_in,
+		AB8500_MCH_IPT_CURLVL_REG);
+}
 
-	return ret;
+/**
+ * ab8500_charger_set_output_curr() - set charger output current
+ * @di:		pointer to the ab8500_charger structure
+ * @ich_out:	output charger current, in mA
+ *
+ * Set charger output current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_output_curr(struct ab8500_charger *di,
+	int ich_out)
+{
+	return ab8500_charger_set_current(di, ich_out,
+		AB8500_CH_OPT_CRNTLVL_REG);
 }
 
 /**
@@ -1088,18 +1190,18 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
 			return ret;
 		}
 		/* MainChInputCurr: current that can be drawn from the charger*/
-		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-			AB8500_MCH_IPT_CURLVL_REG,
-			input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
+		ret = ab8500_charger_set_main_in_curr(di,
+			di->bat->chg_params->ac_curr_max);
 		if (ret) {
-			dev_err(di->dev, "%s write failed\n", __func__);
+			dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
+				__func__);
 			return ret;
 		}
 		/* ChOutputCurentLevel: protected output current */
-		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-			AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+		ret = ab8500_charger_set_output_curr(di, iset);
 		if (ret) {
-			dev_err(di->dev, "%s write failed\n", __func__);
+			dev_err(di->dev,
+			 "%s Failed to set ChOutputCurentLevel\n", __func__);
 			return ret;
 		}
 
@@ -1156,12 +1258,11 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
 				return ret;
 			}
 
-			ret = abx500_set_register_interruptible(di->dev,
-				AB8500_CHARGER,
-				AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
+			ret = ab8500_charger_set_output_curr(di, 0);
 			if (ret) {
 				dev_err(di->dev,
-					"%s write failed\n", __func__);
+				 "%s Failed to set ChOutputCurentLevel\n",
+				 __func__);
 				return ret;
 			}
 		} else {
@@ -1264,10 +1365,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 			return ret;
 		}
 		/* ChOutputCurentLevel: protected output current */
-		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-			AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+		ret = ab8500_charger_set_output_curr(di, ich_out);
 		if (ret) {
-			dev_err(di->dev, "%s write failed\n", __func__);
+			dev_err(di->dev,
+			 "%s Failed to set ChOutputCurentLevel\n",
+			 __func__);
 			return ret;
 		}
 		/* Check if VBAT overshoot control should be enabled */
@@ -1364,7 +1466,6 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
 		int ich_out)
 {
 	int ret;
-	int curr_index;
 	struct ab8500_charger *di;
 
 	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
@@ -1374,18 +1475,11 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
 	else
 		return -ENXIO;
 
-	curr_index = ab8500_current_to_regval(ich_out);
-	if (curr_index < 0) {
-		dev_err(di->dev,
-			"Charger current too high, "
-			"charging not started\n");
-		return -ENXIO;
-	}
-
-	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-		AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+	ret = ab8500_charger_set_output_curr(di, ich_out);
 	if (ret) {
-		dev_err(di->dev, "%s write failed\n", __func__);
+		dev_err(di->dev,
+		 "%s Failed to set ChOutputCurentLevel\n",
+		 __func__);
 		return ret;
 	}
 
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
  2012-09-25 16:11 ` [PATCH 01/57] power: ab8500_bm: Charger current step-up/down mathieu.poirier
@ 2012-09-25 16:11 ` mathieu.poirier
  2012-09-28  3:42   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 03/57] power: ab8500_btemp: Detect battery type in workqueue mathieu.poirier
                   ` (55 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:11 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Kalle Komierowski <karl.komierowski@stericsson.com>

The CCMuxOffset bit is not kept set, this will force the columb counter
of the AB8500 to use the measure offset calibration.
This should increase the accuracy of the fuel gauge.

Signed-off-by: Kalle Komierowski <karl.komierowski@stericsson.com>
Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index bf02225..af792a8 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -485,8 +485,9 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
 		di->flags.fg_enabled = true;
 	} else {
 		/* Clear any pending read requests */
-		ret = abx500_set_register_interruptible(di->dev,
-			AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
+			AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+			(RESET_ACCU | READ_REQ), 0);
 		if (ret)
 			goto cc_err;
 
@@ -1404,8 +1405,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
 		sleep_time = di->bat->fg_params->init_timer;
 
 		/* Discard the first [x] seconds */
-		if (di->init_cnt >
-			di->bat->fg_params->init_discard_time) {
+		if (di->init_cnt > di->bat->fg_params->init_discard_time) {
 			ab8500_fg_calc_cap_discharge_voltage(di, true);
 
 			ab8500_fg_check_capacity_limits(di, true);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 03/57] power: ab8500_btemp: Detect battery type in workqueue
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
  2012-09-25 16:11 ` [PATCH 01/57] power: ab8500_bm: Charger current step-up/down mathieu.poirier
  2012-09-25 16:11 ` [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment mathieu.poirier
                   ` (54 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Detect battery type in work queue instead of probe.
This reduces the system boot time with 1.5s

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_btemp.c |   15 +++++++++++----
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index bba3cca..94a3ee8 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -83,6 +83,7 @@ struct ab8500_btemp_ranges {
  * @btemp_ranges:	Battery temperature range structure
  * @btemp_wq:		Work queue for measuring the temperature periodically
  * @btemp_periodic_work:	Work for measuring the temperature periodically
+ * @initialized:	True if battery id read.
  */
 struct ab8500_btemp {
 	struct device *dev;
@@ -100,6 +101,7 @@ struct ab8500_btemp {
 	struct ab8500_btemp_ranges btemp_ranges;
 	struct workqueue_struct *btemp_wq;
 	struct delayed_work btemp_periodic_work;
+	bool initialized;
 };
 
 /* BTEMP power supply properties */
@@ -569,6 +571,13 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 	struct ab8500_btemp *di = container_of(work,
 		struct ab8500_btemp, btemp_periodic_work.work);
 
+	if (!di->initialized) {
+		di->initialized = true;
+		/* Identify the battery */
+		if (ab8500_btemp_id(di) < 0)
+			dev_warn(di->dev, "failed to identify the battery\n");
+	}
+
 	di->bat_temp = ab8500_btemp_measure_temp(di);
 
 	if (di->bat_temp != di->prev_bat_temp) {
@@ -981,6 +990,8 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 	di->parent = dev_get_drvdata(pdev->dev.parent);
 	di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
+	di->initialized = false;
+
 	/* get btemp specific platform data */
 	di->pdata = plat_data->btemp;
 	if (!di->pdata) {
@@ -1021,10 +1032,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 	INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
 		ab8500_btemp_periodic_work);
 
-	/* Identify the battery */
-	if (ab8500_btemp_id(di) < 0)
-		dev_warn(di->dev, "failed to identify the battery\n");
-
 	/* Set BTEMP thermal limits. Low and Med are fixed */
 	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
 	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (2 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 03/57] power: ab8500_btemp: Detect battery type in workqueue mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  2:42   ` Anton Vorontsov
  2012-09-27  2:53   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 05/57] power: ab8500_bm: Rename the power_loss function mathieu.poirier
                   ` (53 subsequent siblings)
  57 siblings, 2 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Philippe Langlais <philippe.langlais@linaro.org>

Signed-off-by: Philippe Langlais <philippe.langlais@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/ab8500_btemp.c      |    8 ++------
 drivers/power/ab8500_charger.c    |    9 +++------
 drivers/power/ab8500_fg.c         |    8 ++------
 drivers/power/abx500_chargalg.c   |    7 ++++---
 include/linux/mfd/abx500/ab8500.h |    7 ++++++-
 5 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 94a3ee8..41a8ce4 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -973,14 +973,9 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 {
 	int irq, i, ret = 0;
 	u8 val;
-	struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+	struct ab8500_platform_data *plat_data;
 	struct ab8500_btemp *di;
 
-	if (!plat_data) {
-		dev_err(&pdev->dev, "No platform data\n");
-		return -EINVAL;
-	}
-
 	di = kzalloc(sizeof(*di), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
@@ -993,6 +988,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 	di->initialized = false;
 
 	/* get btemp specific platform data */
+	plat_data = dev_get_platdata(di->parent->dev);
 	di->pdata = plat_data->btemp;
 	if (!di->pdata) {
 		dev_err(di->dev, "no btemp platform data supplied\n");
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 3ceb788..22076f5 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2628,14 +2628,9 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 {
 	int irq, i, charger_status, ret = 0;
-	struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+	struct ab8500_platform_data *plat_data;
 	struct ab8500_charger *di;
 
-	if (!plat_data) {
-		dev_err(&pdev->dev, "No platform data\n");
-		return -EINVAL;
-	}
-
 	di = kzalloc(sizeof(*di), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
@@ -2649,6 +2644,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	spin_lock_init(&di->usb_state.usb_lock);
 
 	/* get charger specific platform data */
+	plat_data = dev_get_platdata(di->parent->dev);
+
 	di->pdata = plat_data->charger;
 	if (!di->pdata) {
 		dev_err(di->dev, "no charger platform data supplied\n");
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index af792a8..c098ddd 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2446,14 +2446,9 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 {
 	int i, irq;
 	int ret = 0;
-	struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+	struct ab8500_platform_data *plat_data;
 	struct ab8500_fg *di;
 
-	if (!plat_data) {
-		dev_err(&pdev->dev, "No platform data\n");
-		return -EINVAL;
-	}
-
 	di = kzalloc(sizeof(*di), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
@@ -2466,6 +2461,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 	di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
 	/* get fg specific platform data */
+	plat_data = dev_get_platdata(di->parent->dev);
 	di->pdata = plat_data->fg;
 	if (!di->pdata) {
 		dev_err(di->dev, "no fg platform data supplied\n");
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 804b88c..032b27d 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -220,6 +220,7 @@ enum maxim_ret {
  */
 struct abx500_chargalg {
 	struct device *dev;
+	struct ab8500 *parent;
 	int charge_status;
 	int eoc_cnt;
 	int rch_cnt;
@@ -1802,7 +1803,7 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
 
 static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 {
-	struct abx500_bm_plat_data *plat_data;
+	struct ab8500_platform_data *plat_data;
 	int ret = 0;
 
 	struct abx500_chargalg *di =
@@ -1812,8 +1813,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 
 	/* get device struct */
 	di->dev = &pdev->dev;
-
-	plat_data = pdev->dev.platform_data;
+	di->parent = dev_get_drvdata(pdev->dev.parent);
+	plat_data = dev_get_platdata(di->parent->dev);
 	di->pdata = plat_data->chargalg;
 	di->bat = plat_data->battery;
 
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 3764cb6..db8a1e3 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -170,7 +170,7 @@ enum ab8500_version {
 #define AB8500_INT_ID_DET_R2F		99
 #define AB8500_INT_ID_DET_R3F		100
 #define AB8500_INT_ID_DET_R4F		101
-#define AB8500_INT_CHAUTORESTARTAFTSEC  102
+#define AB8500_INT_CHAUTORESTARTAFTSEC	102
 #define AB8500_INT_CHSTOPBYSEC		103
 /* ab8500_irq_regoffset[13] -> IT[Source|Latch|Mask]22 */
 #define AB8500_INT_USB_CH_TH_PROT_F	104
@@ -289,6 +289,11 @@ struct ab8500_platform_data {
 	struct regulator_init_data *regulator;
 	struct ab8500_gpio_platform_data *gpio;
 	struct ab8500_codec_platform_data *codec;
+	struct abx500_bm_data *battery;
+	struct abx500_charger_platform_data *charger;
+	struct abx500_btemp_platform_data *btemp;
+	struct abx500_fg_platform_data *fg;
+	struct abx500_chargalg_platform_data *chargalg;
 };
 
 extern int __devinit ab8500_init(struct ab8500 *ab8500,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 05/57] power: ab8500_bm: Rename the power_loss function
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (3 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 06/57] power: ab8500_bm: Skip first CCEOC irq for instant current mathieu.poirier
                   ` (52 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>

Rename the ab8500_power_loss_handling function
to a more describing name ab8500_enable_disable_sw_fallback

Signed-off-by: Robert Marklund <robert.marklund@stericsson.com>
Signed-off-by: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Par-Olof HAKANSSON <par-olof.hakansson@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_charger.c |   23 +++++++++++------------
 1 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 22076f5..a7d0c3a 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -268,20 +268,19 @@ static enum power_supply_property ab8500_charger_usb_props[] = {
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 };
 
-/**
- * ab8500_power_loss_handling - set how we handle powerloss.
- * @di:		pointer to the ab8500_charger structure
- *
- * Magic nummbers are from STE HW department.
+/*
+ * Function for enabling and disabling sw fallback mode
+ * should always be disabled when no charger is connected.
  */
-static void ab8500_power_loss_handling(struct ab8500_charger *di)
+static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
+		bool fallback)
 {
 	u8 reg;
 	int ret;
 
-	dev_dbg(di->dev, "Autopower : %d\n", di->autopower);
+	dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
 
-	/* read the autopower register */
+	/* read the register containing fallback bit */
 	ret = abx500_get_register_interruptible(di->dev, 0x15, 0x00, &reg);
 	if (ret) {
 		dev_err(di->dev, "%d write failed\n", __LINE__);
@@ -295,12 +294,12 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di)
 		return;
 	}
 
-	if (di->autopower)
+	if (fallback)
 		reg |= 0x8;
 	else
 		reg &= ~0x8;
 
-	/* write back the changed value to autopower reg */
+	/* write back the changed fallback bit value to register */
 	ret = abx500_set_register_interruptible(di->dev, 0x15, 0x00, reg);
 	if (ret) {
 		dev_err(di->dev, "%d write failed\n", __LINE__);
@@ -330,12 +329,12 @@ static void ab8500_power_supply_changed(struct ab8500_charger *di,
 		    !di->ac.charger_connected &&
 		    di->autopower) {
 			di->autopower = false;
-			ab8500_power_loss_handling(di);
+			ab8500_enable_disable_sw_fallback(di, false);
 		} else if (!di->autopower &&
 			   (di->ac.charger_connected ||
 			    di->usb.charger_connected)) {
 			di->autopower = true;
-			ab8500_power_loss_handling(di);
+			ab8500_enable_disable_sw_fallback(di, true);
 		}
 	}
 	power_supply_changed(psy);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 06/57] power: ab8500_bm: Skip first CCEOC irq for instant current
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (4 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 05/57] power: ab8500_bm: Rename the power_loss function mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 07/57] power: ab8500_bm: Detect removed charger mathieu.poirier
                   ` (51 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Johan Bjornstedt <johan.bjornstedt@stericsson.com>

When enabling the CCEOC irq we might get false interrupt
from ab8500-driver due to the latched value will be saved
and interpreted as an IRQ when enabled

Signed-off-by: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
Signed-off-by: Henrik Solver <henrik.solver@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_btemp.c         |   11 ++----
 drivers/power/ab8500_fg.c            |   63 +++++++++++++++++++++++++++++----
 include/linux/mfd/abx500/ab8500-bm.h |    6 +++
 3 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 41a8ce4..45b10ad 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -374,13 +374,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
 		return ret;
 	}
 
-	/*
-	 * Since there is no interrupt when current measurement is done,
-	 * loop for over 250ms (250ms is one sample conversion time
-	 * with 32.768 Khz RTC clock). Note that a stop time must be set
-	 * since the ab8500_btemp_read_batctrl_voltage call can block and
-	 * take an unknown amount of time to complete.
-	 */
+	do {
+		msleep(20);
+	} while (!ab8500_fg_inst_curr_started(di->fg));
+
 	i = 0;
 
 	do {
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index c098ddd..342d118 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -159,6 +159,7 @@ struct inst_curr_result_list {
  * @recovery_cnt:	Counter for recovery mode
  * @high_curr_cnt:	Counter for high current mode
  * @init_cnt:		Counter for init mode
+ * @nbr_cceoc_irq_cnt	Counter for number of CCEOC irqs received since enabled
  * @recovery_needed:	Indicate if recovery is needed
  * @high_curr_mode:	Indicate if we're in high current mode
  * @init_capacity:	Indicate if initial capacity measuring should be done
@@ -166,6 +167,7 @@ struct inst_curr_result_list {
  * @calib_state		State during offset calibration
  * @discharge_state:	Current discharge state
  * @charge_state:	Current charge state
+ * @ab8500_fg_started	Completion struct used for the instant current start
  * @ab8500_fg_complete	Completion struct used for the instant current reading
  * @flags:		Structure for information about events triggered
  * @bat_cap:		Structure for battery capacity specific parameters
@@ -199,6 +201,7 @@ struct ab8500_fg {
 	int recovery_cnt;
 	int high_curr_cnt;
 	int init_cnt;
+	int nbr_cceoc_irq_cnt;
 	bool recovery_needed;
 	bool high_curr_mode;
 	bool init_capacity;
@@ -206,6 +209,7 @@ struct ab8500_fg {
 	enum ab8500_fg_calibration_state calib_state;
 	enum ab8500_fg_discharge_state discharge_state;
 	enum ab8500_fg_charge_state charge_state;
+	struct completion ab8500_fg_started;
 	struct completion ab8500_fg_complete;
 	struct ab8500_fg_flags flags;
 	struct ab8500_fg_battery_capacity bat_cap;
@@ -525,13 +529,14 @@ cc_err:
  * Note: This is part "one" and has to be called before
  * ab8500_fg_inst_curr_finalize()
  */
- int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
 {
 	u8 reg_val;
 	int ret;
 
 	mutex_lock(&di->cc_lock);
 
+	di->nbr_cceoc_irq_cnt = 0;
 	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
 		AB8500_RTC_CC_CONF_REG, &reg_val);
 	if (ret < 0)
@@ -559,6 +564,7 @@ cc_err:
 	}
 
 	/* Return and WFI */
+	INIT_COMPLETION(di->ab8500_fg_started);
 	INIT_COMPLETION(di->ab8500_fg_complete);
 	enable_irq(di->irq);
 
@@ -570,6 +576,17 @@ fail:
 }
 
 /**
+ * ab8500_fg_inst_curr_started() - check if fg conversion has started
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion started, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
+{
+	return completion_done(&di->ab8500_fg_started);
+}
+
+/**
  * ab8500_fg_inst_curr_done() - check if fg conversion is done
  * @di:         pointer to the ab8500_fg structure
  *
@@ -597,13 +614,15 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
 	int timeout;
 
 	if (!completion_done(&di->ab8500_fg_complete)) {
-		timeout = wait_for_completion_timeout(&di->ab8500_fg_complete,
+		timeout = wait_for_completion_timeout(
+			&di->ab8500_fg_complete,
 			INS_CURR_TIMEOUT);
 		dev_dbg(di->dev, "Finalize time: %d ms\n",
 			((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
 		if (!timeout) {
 			ret = -ETIME;
 			disable_irq(di->irq);
+			di->nbr_cceoc_irq_cnt = 0;
 			dev_err(di->dev, "completion timed out [%d]\n",
 				__LINE__);
 			goto fail;
@@ -611,6 +630,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
 	}
 
 	disable_irq(di->irq);
+	di->nbr_cceoc_irq_cnt = 0;
 
 	ret = abx500_mask_and_set_register_interruptible(di->dev,
 			AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
@@ -685,6 +705,7 @@ fail:
 int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
 {
 	int ret;
+	int timeout;
 	int res = 0;
 
 	ret = ab8500_fg_inst_curr_start(di);
@@ -693,13 +714,32 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
 		return 0;
 	}
 
+	/* Wait for CC to actually start */
+	if (!completion_done(&di->ab8500_fg_started)) {
+		timeout = wait_for_completion_timeout(
+			&di->ab8500_fg_started,
+			INS_CURR_TIMEOUT);
+		dev_dbg(di->dev, "Start time: %d ms\n",
+			((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
+		if (!timeout) {
+			ret = -ETIME;
+			dev_err(di->dev, "completion timed out [%d]\n",
+				__LINE__);
+			goto fail;
+		}
+	}
+
 	ret = ab8500_fg_inst_curr_finalize(di, &res);
 	if (ret) {
 		dev_err(di->dev, "Failed to finalize fg_inst\n");
 		return 0;
 	}
 
+	dev_dbg(di->dev, "%s instant current: %d", __func__, res);
 	return res;
+fail:
+	mutex_unlock(&di->cc_lock);
+	return ret;
 }
 
 /**
@@ -1524,8 +1564,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
 
 	case AB8500_FG_DISCHARGE_WAKEUP:
 		ab8500_fg_coulomb_counter(di, true);
-		di->inst_curr = ab8500_fg_inst_curr_blocking(di);
-
 		ab8500_fg_calc_cap_discharge_voltage(di, true);
 
 		di->fg_samples = SEC_TO_SAMPLE(
@@ -1642,8 +1680,6 @@ static void ab8500_fg_periodic_work(struct work_struct *work)
 		fg_periodic_work.work);
 
 	if (di->init_capacity) {
-		/* A dummy read that will return 0 */
-		di->inst_curr = ab8500_fg_inst_curr_blocking(di);
 		/* Get an initial capacity calculation */
 		ab8500_fg_calc_cap_discharge_voltage(di, true);
 		ab8500_fg_check_capacity_limits(di, true);
@@ -1829,7 +1865,13 @@ static void ab8500_fg_instant_work(struct work_struct *work)
 static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
 {
 	struct ab8500_fg *di = _di;
-	complete(&di->ab8500_fg_complete);
+	if (!di->nbr_cceoc_irq_cnt) {
+		di->nbr_cceoc_irq_cnt++;
+		complete(&di->ab8500_fg_started);
+	} else {
+		di->nbr_cceoc_irq_cnt = 0;
+		complete(&di->ab8500_fg_complete);
+	}
 	return IRQ_HANDLED;
 }
 
@@ -2548,7 +2590,11 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 	di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
 	ab8500_fg_coulomb_counter(di, true);
 
-	/* Initialize completion used to notify completion of inst current */
+	/*
+	 * Initialize completion used to notify completion and start
+	 * of inst current
+	 */
+	init_completion(&di->ab8500_fg_started);
 	init_completion(&di->ab8500_fg_complete);
 
 	/* Register interrupts */
@@ -2568,6 +2614,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 	}
 	di->irq = platform_get_irq_byname(pdev, "CCEOC");
 	disable_irq(di->irq);
+	di->nbr_cceoc_irq_cnt = 0;
 
 	platform_set_drvdata(pdev, di);
 
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 44310c9..73479f0 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -431,11 +431,17 @@ struct ab8500_fg *ab8500_fg_get(void);
 int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
 int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
 int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
 
 #else
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
+{
+	return 0;
+}
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
 {
+	return 0;
 }
 static void ab8500_fg_reinit(void)
 {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 07/57] power: ab8500_bm: Detect removed charger
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (5 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 06/57] power: ab8500_bm: Skip first CCEOC irq for instant current mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:09   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 08/57] power: ab8500_fg: flush sync on suspend mathieu.poirier
                   ` (50 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/ab8500_charger.c |  122 +++++++++++++++++++++++++++++++++++++++-
 1 files changed, 121 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index a7d0c3a..f7bba07 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -66,6 +66,11 @@
 #define MAIN_CH_NOK			0x01
 #define VBUS_DET			0x80
 
+#define MAIN_CH_STATUS2_MAINCHGDROP		0x80
+#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC	0x40
+#define USB_CH_VBUSDROP				0x40
+#define USB_CH_VBUSDETDBNC			0x01
+
 /* UsbLineStatus register bit masks */
 #define AB8500_USB_LINK_STATUS		0x78
 #define AB8500_STD_HOST_SUSP		0x18
@@ -80,6 +85,8 @@
 /* Step up/down delay in us */
 #define STEP_UDELAY			1000
 
+#define CHARGER_STATUS_POLL 10 /* in ms */
+
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
 	USB_STAT_NOT_CONFIGURED,
@@ -201,6 +208,10 @@ struct ab8500_charger_usb_state {
  * @check_usbchgnotok_work:	Work for checking USB charger not ok status
  * @kick_wd_work:		Work for kicking the charger watchdog in case
  *				of ABB rev 1.* due to the watchog logic bug
+ * @ac_charger_attached_work:	Work for checking if AC charger is still
+ *				connected
+ * @usb_charger_attached_work:	Work for checking if USB charger is still
+ *				connected
  * @ac_work:			Work for checking AC charger connection
  * @detect_usb_type_work:	Work for detecting the USB type connected
  * @usb_link_status_work:	Work for checking the new USB link status
@@ -237,6 +248,8 @@ struct ab8500_charger {
 	struct delayed_work check_hw_failure_work;
 	struct delayed_work check_usbchgnotok_work;
 	struct delayed_work kick_wd_work;
+	struct delayed_work ac_charger_attached_work;
+	struct delayed_work usb_charger_attached_work;
 	struct work_struct ac_work;
 	struct work_struct detect_usb_type_work;
 	struct work_struct usb_link_status_work;
@@ -347,6 +360,15 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
 		dev_dbg(di->dev, "USB connected:%i\n", connected);
 		di->usb.charger_connected = connected;
 		sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
+
+		if (connected) {
+			queue_delayed_work(di->charger_wq,
+					   &di->usb_charger_attached_work,
+					   HZ);
+		} else {
+			cancel_delayed_work_sync
+					(&di->usb_charger_attached_work);
+		}
 	}
 }
 
@@ -1703,6 +1725,81 @@ static void ab8500_charger_ac_work(struct work_struct *work)
 	sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
 }
 
+static void ab8500_charger_usb_attached_work(struct work_struct *work)
+{
+	int i;
+	int ret;
+	u8 statval;
+	struct ab8500_charger *di = container_of(work,
+					 struct ab8500_charger,
+					 usb_charger_attached_work.work);
+
+	for (i = 0 ; i < 10; i++) {
+		ret = abx500_get_register_interruptible(di->dev,
+						AB8500_CHARGER,
+						AB8500_CH_USBCH_STAT1_REG,
+						&statval);
+		if (ret < 0) {
+			dev_err(di->dev, "ab8500 read failed %d\n",
+				__LINE__);
+			goto reschedule;
+		}
+		if ((statval & (USB_CH_VBUSDROP |
+				USB_CH_VBUSDETDBNC)) !=
+		    (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC))
+			goto reschedule;
+
+		msleep(CHARGER_STATUS_POLL);
+	}
+
+	(void) ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);
+
+	return;
+reschedule:
+	queue_delayed_work(di->charger_wq,
+			   &di->usb_charger_attached_work,
+			   HZ);
+}
+
+static void ab8500_charger_ac_attached_work(struct work_struct *work)
+{
+
+	int i;
+	int ret;
+	u8 statval;
+	struct ab8500_charger *di = container_of(work,
+					 struct ab8500_charger,
+					 ac_charger_attached_work.work);
+
+	for (i = 0 ; i < 10; i++) {
+		ret = abx500_get_register_interruptible(di->dev,
+							AB8500_CHARGER,
+							AB8500_CH_STATUS2_REG,
+							&statval);
+		if (ret < 0) {
+			dev_err(di->dev, "ab8500 read failed %d\n",
+				__LINE__);
+			goto reschedule;
+		}
+		if ((statval & (MAIN_CH_STATUS2_MAINCHGDROP |
+				MAIN_CH_STATUS2_MAINCHARGERDETDBNC)) !=
+		     (MAIN_CH_STATUS2_MAINCHGDROP |
+		      MAIN_CH_STATUS2_MAINCHARGERDETDBNC))
+			goto reschedule;
+
+		msleep(CHARGER_STATUS_POLL);
+	}
+
+	(void) ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);
+	queue_work(di->charger_wq, &di->ac_work);
+
+	return;
+reschedule:
+	queue_delayed_work(di->charger_wq,
+			   &di->ac_charger_attached_work,
+			   HZ);
+}
+
 /**
  * ab8500_charger_detect_usb_type_work() - work to detect USB type
  * @work:	Pointer to the work_struct structure
@@ -1983,6 +2080,8 @@ static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
 	dev_dbg(di->dev, "Main charger unplugged\n");
 	queue_work(di->charger_wq, &di->ac_work);
 
+	cancel_delayed_work_sync(&di->ac_charger_attached_work);
+
 	return IRQ_HANDLED;
 }
 
@@ -2000,6 +2099,9 @@ static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
 	dev_dbg(di->dev, "Main charger plugged\n");
 	queue_work(di->charger_wq, &di->ac_work);
 
+	queue_delayed_work(di->charger_wq,
+			   &di->ac_charger_attached_work,
+			   HZ);
 	return IRQ_HANDLED;
 }
 
@@ -2626,7 +2728,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 
 static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 {
-	int irq, i, charger_status, ret = 0;
+	int irq, i, charger_status, ret = 0, ch_stat;
 	struct ab8500_platform_data *plat_data;
 	struct ab8500_charger *di;
 
@@ -2713,6 +2815,11 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
 		ab8500_charger_check_usbchargernotok_work);
 
+	INIT_DELAYED_WORK(&di->ac_charger_attached_work,
+			  ab8500_charger_ac_attached_work);
+	INIT_DELAYED_WORK(&di->usb_charger_attached_work,
+			  ab8500_charger_usb_attached_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
@@ -2826,6 +2933,19 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, di);
 
+	ch_stat = ab8500_charger_detect_chargers(di);
+
+	if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
+		queue_delayed_work(di->charger_wq,
+					   &di->ac_charger_attached_work,
+					   HZ);
+	}
+	if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
+		queue_delayed_work(di->charger_wq,
+					   &di->usb_charger_attached_work,
+					   HZ);
+	}
+
 	return ret;
 
 free_irq:
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 08/57] power: ab8500_fg: flush sync on suspend
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (6 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 07/57] power: ab8500_bm: Detect removed charger mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:11   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep mathieu.poirier
                   ` (49 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Do flush sync on the fg workqueue at suspend instead of
just flushing it.

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com>
---
 drivers/power/ab8500_fg.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 342d118..1e02b00 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2437,7 +2437,7 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
 {
 	struct ab8500_fg *di = platform_get_drvdata(pdev);
 
-	flush_delayed_work(&di->fg_periodic_work);
+	flush_delayed_work_sync(&di->fg_periodic_work);
 
 	/*
 	 * If the FG is enabled we will disable it before going to suspend
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (7 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 08/57] power: ab8500_fg: flush sync on suspend mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  2:36   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 10/57] power: ab8500_charger: Handle gpadc errors mathieu.poirier
                   ` (48 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Signed-off-by: Jonas ABERG <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com>
---
 drivers/power/ab8500_fg.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 1e02b00..0db17c7 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -957,7 +957,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
 	do {
 		vbat += ab8500_fg_bat_voltage(di);
 		i++;
-		msleep(5);
+		usleep_range(5000, 5001);
 	} while (!ab8500_fg_inst_curr_done(di));
 
 	ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 10/57] power: ab8500_charger: Handle gpadc errors
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (8 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 11/57] power: Recharge condition not optimal for battery mathieu.poirier
                   ` (47 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Gracefully handle gpadc conversion errors.

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com>
---
 drivers/power/ab8500_charger.c |   22 ++++++++++++++++++----
 1 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index f7bba07..06a7c8b 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -157,6 +157,7 @@ struct ab8500_charger_info {
 	int charger_voltage;
 	int cv_active;
 	bool wd_expired;
+	int charger_current;
 };
 
 struct ab8500_charger_event_flags {
@@ -2341,6 +2342,7 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
 	union power_supply_propval *val)
 {
 	struct ab8500_charger *di;
+	int ret;
 
 	di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
 
@@ -2362,7 +2364,10 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
 		val->intval = di->ac.charger_connected;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		di->ac.charger_voltage = ab8500_charger_get_ac_voltage(di);
+		ret = ab8500_charger_get_ac_voltage(di);
+		if (ret >= 0)
+			di->ac.charger_voltage = ret;
+		/* On error, use previous value */
 		val->intval = di->ac.charger_voltage * 1000;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
@@ -2374,7 +2379,10 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
 		val->intval = di->ac.cv_active;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		val->intval = ab8500_charger_get_ac_current(di) * 1000;
+		ret = ab8500_charger_get_ac_current(di);
+		if (ret >= 0)
+			di->ac.charger_current = ret;
+		val->intval = di->ac.charger_current * 1000;
 		break;
 	default:
 		return -EINVAL;
@@ -2401,6 +2409,7 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
 	union power_supply_propval *val)
 {
 	struct ab8500_charger *di;
+	int ret;
 
 	di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
 
@@ -2424,7 +2433,9 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
 		val->intval = di->usb.charger_connected;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		di->usb.charger_voltage = ab8500_charger_get_vbus_voltage(di);
+		ret = ab8500_charger_get_vbus_voltage(di);
+		if (ret >= 0)
+			di->usb.charger_voltage = ret;
 		val->intval = di->usb.charger_voltage * 1000;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
@@ -2436,7 +2447,10 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
 		val->intval = di->usb.cv_active;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		val->intval = ab8500_charger_get_usb_current(di) * 1000;
+		ret = ab8500_charger_get_usb_current(di);
+		if (ret >= 0)
+			di->usb.charger_current = ret;
+		val->intval = di->usb.charger_current * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_AVG:
 		/*
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 11/57] power: Recharge condition not optimal for battery
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (9 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 10/57] power: ab8500_charger: Handle gpadc errors mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:29   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 12/57] power: ab8500_fg: balance IRQ enable mathieu.poirier
                   ` (46 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

Today the battery recharge is determined with a voltage threshold. This
voltage threshold is only valid when the battery is relaxed. In charging
algorithm the voltage read is the loaded battery voltage and no
compensation is done to get the relaxed voltage. When maintenance
charging is not selected, this makes the recharging condition to almost
immediately activate when there is a discharge present on the battery.

Depending on which vendor the battery comes from this behavior can wear
out the battery much faster than normal.

The fuelgauge driver is responsible to monitor the actual battery
capacity and is able to estimate the remaining capacity. It is better to
use the remaining capacity as a limit to determine when battery should
be recharged.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c       |  157 +++++++++++++++++++++++++++++++++++----
 drivers/power/abx500_chargalg.c |   59 +++++++++++----
 include/linux/mfd/abx500.h      |    6 +-
 3 files changed, 191 insertions(+), 31 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 0db17c7..7af616c 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -112,6 +112,13 @@ struct ab8500_fg_avg_cap {
 	int sum;
 };
 
+struct ab8500_fg_cap_scaling {
+	bool enable;
+	int cap_to_scale[2];
+	int disable_cap_level;
+	int scaled_cap;
+};
+
 struct ab8500_fg_battery_capacity {
 	int max_mah_design;
 	int max_mah;
@@ -122,6 +129,7 @@ struct ab8500_fg_battery_capacity {
 	int prev_percent;
 	int prev_level;
 	int user_mah;
+	struct ab8500_fg_cap_scaling cap_scale;
 };
 
 struct ab8500_fg_flags {
@@ -928,10 +936,11 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
 		resist = tbl[tbl_size - 1].resist;
 	}
 
-	dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
-	    " fg resistance %d, total: %d (mOhm)\n",
-		__func__, di->bat_temp, resist, di->bat->fg_res / 10,
-		(di->bat->fg_res / 10) + resist);
+	dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d",
+					__func__, di->bat_temp, resist);
+	dev_dbg(di->dev, " fg resistance %d, total: %d (mOhm)\n",
+					di->bat->fg_res / 10,
+					(di->bat->fg_res / 10) + resist);
 
 	/* fg_res variable is in 0.1mOhm */
 	resist += di->bat->fg_res / 10;
@@ -1168,6 +1177,99 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di)
 }
 
 /**
+ * ab8500_fg_calculate_scaled_capacity() - Capacity scaling
+ * @di:		pointer to the ab8500_fg structure
+ *
+ * Calculates the capacity to be shown to upper layers. Scales the capacity
+ * to have 100% as a reference from the actual capacity upon removal of charger
+ * when charging is in maintenance mode.
+ */
+static int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di)
+{
+	struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
+	int capacity = di->bat_cap.prev_percent;
+
+	if (!cs->enable)
+		return capacity;
+
+	/*
+	 * As long as we are in fully charge mode scale the capacity
+	 * to show 100%.
+	 */
+	if (di->flags.fully_charged) {
+		cs->cap_to_scale[0] = 100;
+		cs->cap_to_scale[1] =
+			max(capacity, di->bat->fg_params->maint_thres);
+		dev_dbg(di->dev, "Scale cap with %d/%d\n",
+			 cs->cap_to_scale[0], cs->cap_to_scale[1]);
+	}
+
+	/* Calculates the scaled capacity. */
+	if ((cs->cap_to_scale[0] != cs->cap_to_scale[1])
+					&& (cs->cap_to_scale[1] > 0))
+		capacity = min(100,
+				 DIV_ROUND_CLOSEST(di->bat_cap.prev_percent *
+						 cs->cap_to_scale[0],
+						 cs->cap_to_scale[1]));
+
+	if (di->flags.charging) {
+		if (capacity < cs->disable_cap_level) {
+			cs->disable_cap_level = capacity;
+			dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n",
+				cs->disable_cap_level);
+		} else if (!di->flags.fully_charged) {
+			if (di->bat_cap.prev_percent >=
+			    cs->disable_cap_level) {
+				dev_dbg(di->dev, "Disabling scaled capacity\n");
+				cs->enable = false;
+				capacity = di->bat_cap.prev_percent;
+			} else {
+				dev_dbg(di->dev,
+					"Waiting in cap to level %d%%\n",
+					cs->disable_cap_level);
+				capacity = cs->disable_cap_level;
+			}
+		}
+	}
+
+	return capacity;
+}
+
+/**
+ * ab8500_fg_update_cap_scalers() - Capacity scaling
+ * @di:		pointer to the ab8500_fg structure
+ *
+ * To be called when state change from charge<->discharge to update
+ * the capacity scalers.
+ */
+static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di)
+{
+	struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
+
+	if (!cs->enable)
+		return;
+	if (di->flags.charging) {
+		di->bat_cap.cap_scale.disable_cap_level =
+			di->bat_cap.cap_scale.scaled_cap;
+		dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n",
+				di->bat_cap.cap_scale.disable_cap_level);
+	} else {
+		if (cs->scaled_cap != 100) {
+			cs->cap_to_scale[0] = cs->scaled_cap;
+			cs->cap_to_scale[1] = di->bat_cap.prev_percent;
+		} else {
+			cs->cap_to_scale[0] = 100;
+			cs->cap_to_scale[1] =
+				max(di->bat_cap.prev_percent,
+				    di->bat->fg_params->maint_thres);
+		}
+
+		dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n",
+				cs->cap_to_scale[0], cs->cap_to_scale[1]);
+	}
+}
+
+/**
  * ab8500_fg_check_capacity_limits() - Check if capacity has changed
  * @di:		pointer to the ab8500_fg structure
  * @init:	capacity is allowed to go up in init mode
@@ -1215,16 +1317,24 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 	} else if (di->flags.fully_charged) {
 		/*
 		 * We report 100% if algorithm reported fully charged
-		 * unless capacity drops too much
+		 * and show 100% during maintenance charging (scaling).
 		 */
 		if (di->flags.force_full) {
 			di->bat_cap.prev_percent = di->bat_cap.permille / 10;
 			di->bat_cap.prev_mah = di->bat_cap.mah;
-		} else if (!di->flags.force_full &&
-			di->bat_cap.prev_percent !=
-			(di->bat_cap.permille) / 10 &&
-			(di->bat_cap.permille / 10) <
-			di->bat->fg_params->maint_thres) {
+
+			changed = true;
+
+			if (!di->bat_cap.cap_scale.enable &&
+						di->bat->capacity_scaling) {
+				di->bat_cap.cap_scale.enable = true;
+				di->bat_cap.cap_scale.cap_to_scale[0] = 100;
+				di->bat_cap.cap_scale.cap_to_scale[1] =
+						di->bat_cap.prev_percent;
+				di->bat_cap.cap_scale.disable_cap_level = 100;
+			}
+		} else if (di->bat_cap.prev_percent !=
+			(di->bat_cap.permille) / 10) {
 			dev_dbg(di->dev,
 				"battery reported full "
 				"but capacity dropping: %d\n",
@@ -1273,6 +1383,14 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 	}
 
 	if (changed) {
+		if (di->bat->capacity_scaling) {
+			di->bat_cap.cap_scale.scaled_cap =
+				ab8500_fg_calculate_scaled_capacity(di);
+
+			dev_info(di->dev, "capacity=%d (%d)\n",
+				di->bat_cap.prev_percent,
+				di->bat_cap.cap_scale.scaled_cap);
+		}
 		power_supply_changed(&di->fg_psy);
 		if (di->flags.fully_charged && di->flags.force_full) {
 			dev_dbg(di->dev, "Battery full, notifying.\n");
@@ -1338,7 +1456,7 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
 		 * Read the FG and calculate the new capacity
 		 */
 		mutex_lock(&di->cc_lock);
-		if (!di->flags.conv_done) {
+		if (!di->flags.conv_done && !di->flags.force_full) {
 			/* Wasn't the CC IRQ that got us here */
 			mutex_unlock(&di->cc_lock);
 			dev_dbg(di->dev, "%s CC conv not done\n",
@@ -2028,7 +2146,9 @@ static int ab8500_fg_get_property(struct power_supply *psy,
 			val->intval = di->bat_cap.prev_mah;
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
-		if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+		if (di->bat->capacity_scaling)
+			val->intval = di->bat_cap.cap_scale.scaled_cap;
+		else if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
 				di->flags.batt_id_received)
 			val->intval = 100;
 		else
@@ -2092,6 +2212,9 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
 						break;
 					di->flags.charging = false;
 					di->flags.fully_charged = false;
+					if (di->bat->capacity_scaling)
+						ab8500_fg_update_cap_scalers
+									(di);
 					queue_work(di->fg_wq, &di->fg_work);
 					break;
 				case POWER_SUPPLY_STATUS_FULL:
@@ -2104,10 +2227,14 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
 					queue_work(di->fg_wq, &di->fg_work);
 					break;
 				case POWER_SUPPLY_STATUS_CHARGING:
-					if (di->flags.charging)
+					if (di->flags.charging &&
+						!di->flags.fully_charged)
 						break;
 					di->flags.charging = true;
 					di->flags.fully_charged = false;
+					if (di->bat->capacity_scaling)
+						ab8500_fg_update_cap_scalers
+									(di);
 					queue_work(di->fg_wq, &di->fg_work);
 					break;
 				};
@@ -2147,8 +2274,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
 		case POWER_SUPPLY_PROP_TEMP:
 			switch (ext->type) {
 			case POWER_SUPPLY_TYPE_BATTERY:
-			    if (di->flags.batt_id_received)
-				di->bat_temp = ret.intval;
+				if (di->flags.batt_id_received)
+					di->bat_temp = ret.intval;
 				break;
 			default:
 				break;
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 032b27d..ca87cb2 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -31,9 +31,6 @@
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT			10
 
-/* Recharge criteria counter */
-#define RCH_COND_CNT			3
-
 #define to_abx500_chargalg_device_info(x) container_of((x), \
 	struct abx500_chargalg, chargalg_psy);
 
@@ -189,13 +186,18 @@ enum maxim_ret {
 	MAXIM_RET_IBAT_TOO_HIGH,
 };
 
+enum maintenance_state {
+	MAINT_A,
+	MAINT_B,
+};
+
 /**
  * struct abx500_chargalg - abx500 Charging algorithm device information
  * @dev:		pointer to the structure device
  * @charge_status:	battery operating status
  * @eoc_cnt:		counter used to determine end-of_charge
- * @rch_cnt:		counter used to determine start of recharge
  * @maintenance_chg:	indicate if maintenance charge is active
+ * @maint_state:	indicate what maintenance state we should go to next
  * @t_hyst_norm		temperature hysteresis when the temperature has been
  *			over or under normal limits
  * @t_hyst_lowhigh	temperature hysteresis when the temperature has been
@@ -223,8 +225,8 @@ struct abx500_chargalg {
 	struct ab8500 *parent;
 	int charge_status;
 	int eoc_cnt;
-	int rch_cnt;
 	bool maintenance_chg;
+	enum maintenance_state maint_state;
 	int t_hyst_norm;
 	int t_hyst_lowhigh;
 	enum abx500_chargalg_states charge_state;
@@ -859,6 +861,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 	union power_supply_propval ret;
 	int i, j;
 	bool psy_found = false;
+	bool capacity_updated = false;
 
 	psy = (struct power_supply *)data;
 	ext = dev_get_drvdata(dev);
@@ -871,6 +874,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 	if (!psy_found)
 		return 0;
 
+	/*
+	 * If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
+	 * property because of handling that sysfs entry on its own, this is
+	 * the place to get the battery capacity.
+	 */
+	if (!ext->get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
+		di->batt_data.percent = ret.intval;
+		capacity_updated = true;
+	}
+
 	/* Go through all properties for the psy */
 	for (j = 0; j < ext->num_properties; j++) {
 		enum power_supply_property prop;
@@ -1156,6 +1169,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 			break;
 		case POWER_SUPPLY_PROP_CAPACITY:
 			di->batt_data.percent = ret.intval;
+			if (!capacity_updated)
+				di->batt_data.percent = ret.intval;
 			break;
 		default:
 			break;
@@ -1425,16 +1440,32 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 	case STATE_WAIT_FOR_RECHARGE_INIT:
 		abx500_chargalg_hold_charging(di);
 		abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
-		di->rch_cnt = RCH_COND_CNT;
 		/* Intentional fallthrough */
 
 	case STATE_WAIT_FOR_RECHARGE:
-		if (di->batt_data.volt <=
-			di->bat->bat_type[di->bat->batt_id].recharge_vol) {
-			if (di->rch_cnt-- == 0)
+		if (di->bat->no_maintenance) {
+			if (di->batt_data.percent <=
+				di->bat->bat_type[di->bat->batt_id].
+				recharge_cap)
 				abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-		} else
-			di->rch_cnt = RCH_COND_CNT;
+		} else {
+			/* Maintenance A */
+			if (di->maint_state == MAINT_A &&
+					di->batt_data.volt <
+					di->bat->bat_type[di->bat->batt_id].
+					maint_a_vol_lvl) {
+				abx500_chargalg_state_to(di,
+						STATE_MAINTENANCE_A_INIT);
+			}
+			/* Maintenance B */
+			else if (di->maint_state == MAINT_B &&
+					di->batt_data.volt <
+					di->bat->bat_type[di->bat->batt_id].
+					maint_b_vol_lvl)  {
+				abx500_chargalg_state_to(di,
+						STATE_MAINTENANCE_B_INIT);
+			}
+		}
 		break;
 
 	case STATE_MAINTENANCE_A_INIT:
@@ -1718,7 +1749,7 @@ static struct kobj_type abx500_chargalg_ktype = {
 
 /**
  * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
- * @di:                pointer to the struct abx500_chargalg
+ * @di:		       pointer to the struct abx500_chargalg
  *
  * This function removes the entry in sysfs.
  */
@@ -1729,7 +1760,7 @@ static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
 
 /**
  * abx500_chargalg_sysfs_init() - init of sysfs entry
- * @di:                pointer to the struct abx500_chargalg
+ * @di:		       pointer to the struct abx500_chargalg
  *
  * This function adds an entry in sysfs.
  * Returns error code in case of failure else 0(on success)
@@ -1780,7 +1811,7 @@ static int abx500_chargalg_suspend(struct platform_device *pdev,
 }
 #else
 #define abx500_chargalg_suspend      NULL
-#define abx500_chargalg_resume       NULL
+#define abx500_chargalg_resume	     NULL
 #endif
 
 static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 1318ca6..4d44a10 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -246,7 +246,7 @@ struct abx500_maxim_parameters {
  * @nominal_voltage:		Nominal voltage of the battery in mV
  * @termination_vol:		max voltage upto which battery can be charged
  * @termination_curr		battery charging termination current in mA
- * @recharge_vol		battery voltage limit that will trigger a new
+ * @recharge_cap		battery capacity limit that will trigger a new
  *				full charging cycle in the case where maintenan-
  *				-ce charging has been disabled
  * @normal_cur_lvl:		charger current in normal state in mA
@@ -275,7 +275,7 @@ struct abx500_battery_type {
 	int nominal_voltage;
 	int termination_vol;
 	int termination_curr;
-	int recharge_vol;
+	int recharge_cap;
 	int normal_cur_lvl;
 	int normal_vol_lvl;
 	int maint_a_cur_lvl;
@@ -339,6 +339,7 @@ struct abx500_bm_charger_parameters {
  * @bkup_bat_v		voltage which we charge the backup battery with
  * @bkup_bat_i		current which we charge the backup battery with
  * @no_maintenance	indicates that maintenance charging is disabled
+ * @capacity_scaling	indicates whether capacity scaling is to be used
  * @abx500_adc_therm	placement of thermistor, batctrl or battemp adc
  * @chg_unknown_bat	flag to enable charging of unknown batteries
  * @enable_overshoot	flag to enable VBAT overshoot control
@@ -369,6 +370,7 @@ struct abx500_bm_data {
 	int bkup_bat_v;
 	int bkup_bat_i;
 	bool no_maintenance;
+	bool capacity_scaling;
 	bool chg_unknown_bat;
 	bool enable_overshoot;
 	bool auto_trig;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 12/57] power: ab8500_fg: balance IRQ enable
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (10 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 11/57] power: Recharge condition not optimal for battery mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt mathieu.poirier
                   ` (45 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rickard Andersson <rickard.andersson@stericsson.com>

In case of time out error IRQ needs to be disabled
otherwise we will get unbalanced enable/disable pairs.

Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 7af616c..492f6bf 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -746,6 +746,7 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
 	dev_dbg(di->dev, "%s instant current: %d", __func__, res);
 	return res;
 fail:
+	disable_irq(di->irq);
 	mutex_unlock(&di->cc_lock);
 	return ret;
 }
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (11 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 12/57] power: ab8500_fg: balance IRQ enable mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:32   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 14/57] power: Adds support for Car/Travel Adapters mathieu.poirier
                   ` (44 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

Ignore the low btemp interrupts for ab8500 3.0 and 3.3

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_btemp.c      |   22 +++++++++++-----------
 include/linux/mfd/abx500/ab8500.h |    5 +++++
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 45b10ad..04f9dec 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -622,9 +622,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
 {
 	struct ab8500_btemp *di = _di;
 
-	if (is_ab8500_2p0_or_earlier(di->parent)) {
-		dev_dbg(di->dev, "Ignore false btemp low irq"
-			" for ABB cut 1.0, 1.1 and 2.0\n");
+	if (is_ab8500_3p3_or_earlier(di->parent)) {
+		dev_dbg(di->dev, "Ignore false btemp low irq");
+		dev_dbg(di->dev, " for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
 	} else {
 		dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
 
@@ -738,30 +738,30 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
 	int temp = 0;
 
 	/*
-	 * The BTEMP events are not reliabe on AB8500 cut2.0
+	 * The BTEMP events are not reliabe on AB8500 cut3.3
 	 * and prior versions
 	 */
-	if (is_ab8500_2p0_or_earlier(di->parent)) {
+	if (is_ab8500_3p3_or_earlier(di->parent)) {
 		temp = di->bat_temp * 10;
 	} else {
 		if (di->events.btemp_low) {
 			if (temp > di->btemp_ranges.btemp_low_limit)
-				temp = di->btemp_ranges.btemp_low_limit;
+				temp = di->btemp_ranges.btemp_low_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_high) {
 			if (temp < di->btemp_ranges.btemp_high_limit)
-				temp = di->btemp_ranges.btemp_high_limit;
+				temp = di->btemp_ranges.btemp_high_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_lowmed) {
 			if (temp > di->btemp_ranges.btemp_med_limit)
-				temp = di->btemp_ranges.btemp_med_limit;
+				temp = di->btemp_ranges.btemp_med_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_medhigh) {
 			if (temp < di->btemp_ranges.btemp_med_limit)
-				temp = di->btemp_ranges.btemp_med_limit;
+				temp = di->btemp_ranges.btemp_med_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else
@@ -1026,8 +1026,8 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 		ab8500_btemp_periodic_work);
 
 	/* Set BTEMP thermal limits. Low and Med are fixed */
-	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
-	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
+	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT * 10;
+	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT * 10;
 
 	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
 		AB8500_BTEMP_HIGH_TH, &val);
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index db8a1e3..087b445 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -340,6 +340,11 @@ static inline int is_ab8500_2p0_or_earlier(struct ab8500 *ab)
 	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT2P0));
 }
 
+static inline int is_ab8500_3p3_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT3P3));
+}
+
 /* exclude also ab8505, ab9540... */
 static inline int is_ab8500_2p0(struct ab8500 *ab)
 {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 14/57] power: Adds support for Car/Travel Adapters
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (12 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 15/57] power: ab8500_fg: Round capacity output mathieu.poirier
                   ` (43 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

The Travel and Carkit adapter should be handled directly by
the charger driver.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c       |   94 +++++++++++++++++++++++++---------
 include/linux/mfd/abx500/ab8500-bm.h |    1 +
 2 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 06a7c8b..09edd9c 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -105,6 +105,18 @@ enum ab8500_charger_link_status {
 	USB_STAT_HM_IDGND,
 	USB_STAT_RESERVED,
 	USB_STAT_NOT_VALID_LINK,
+	USB_STAT_PHY_EN,
+	USB_STAT_SUP_NO_IDGND_VBUS,
+	USB_STAT_SUP_IDGND_VBUS,
+	USB_STAT_CHARGER_LINE_1,
+	USB_STAT_CARKIT_1,
+	USB_STAT_CARKIT_2,
+	USB_STAT_ACA_DOCK_CHARGER,
+	USB_STAT_SAMSUNG_USB_PHY_DIS,
+	USB_STAT_SAMSUNG_USB_PHY_ENA,
+	USB_STAT_SAMSUNG_UART_PHY_DIS,
+	USB_STAT_SAMSUNG_UART_PHY_ENA,
+	USB_STAT_MOTOROLA_USB_PHY_ENA,
 };
 
 enum ab8500_usb_state {
@@ -577,7 +589,7 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
  * Returns error code in case of failure else 0 on success
  */
 static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
-	enum ab8500_charger_link_status link_status)
+		enum ab8500_charger_link_status link_status)
 {
 	int ret = 0;
 
@@ -585,16 +597,20 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 	case USB_STAT_STD_HOST_NC:
 	case USB_STAT_STD_HOST_C_NS:
 	case USB_STAT_STD_HOST_C_S:
-		dev_dbg(di->dev, "USB Type - Standard host is "
-			"detected through USB driver\n");
+		dev_dbg(di->dev, "USB Type - Standard host is ");
+		dev_dbg(di->dev, "detected through USB driver\n");
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
 		break;
 	case USB_STAT_HOST_CHG_HS_CHIRP:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
 		break;
 	case USB_STAT_HOST_CHG_HS:
 	case USB_STAT_ACA_RID_C_HS:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
 		break;
 	case USB_STAT_ACA_RID_A:
 		/*
@@ -602,6 +618,8 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		 * can consume (300mA). Closest level is 1100mA
 		 */
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
 		break;
 	case USB_STAT_ACA_RID_B:
 		/*
@@ -609,34 +627,50 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		 * 100mA for potential accessory). Closest level is 1300mA
 		 */
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
 		break;
-	case USB_STAT_DEDICATED_CHG:
 	case USB_STAT_HOST_CHG_NM:
-	case USB_STAT_ACA_RID_C_HS_CHIRP:
+	case USB_STAT_DEDICATED_CHG:
 	case USB_STAT_ACA_RID_C_NM:
+	case USB_STAT_ACA_RID_C_HS_CHIRP:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
-		break;
-	case USB_STAT_RESERVED:
-		/*
-		 * This state is used to indicate that VBUS has dropped below
-		 * the detection level 4 times in a row. This is due to the
-		 * charger output current is set to high making the charger
-		 * voltage collapse. This have to be propagated through to
-		 * chargalg. This is done using the property
-		 * POWER_SUPPLY_PROP_CURRENT_AVG = 1
-		 */
-		di->flags.vbus_collapse = true;
-		dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
-			"VBUS has collapsed\n");
-		ret = -1;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
 		break;
 	case USB_STAT_HM_IDGND:
-	case USB_STAT_NOT_CONFIGURED:
 	case USB_STAT_NOT_VALID_LINK:
+	case USB_STAT_NOT_CONFIGURED:
 		dev_err(di->dev, "USB Type - Charging not allowed\n");
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
 		ret = -ENXIO;
 		break;
+	case USB_STAT_RESERVED:
+		if (is_ab8500(di->parent)) {
+			di->flags.vbus_collapse = true;
+			dev_err(di->dev, "USB Type - USB_STAT_RESERVED ");
+			dev_err(di->dev, "VBUS has collapsed\n");
+			ret = -ENXIO;
+			break;
+		}
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			dev_dbg(di->dev, "USB Type - Charging not allowed\n");
+			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+			dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+					link_status, di->max_usb_in_curr);
+			ret = -ENXIO;
+			break;
+		}
+		break;
+	case USB_STAT_CARKIT_1:
+	case USB_STAT_CARKIT_2:
+	case USB_STAT_ACA_DOCK_CHARGER:
+	case USB_STAT_CHARGER_LINE_1:
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+				di->max_usb_in_curr);
+		break;
+
 	default:
 		dev_err(di->dev, "USB Type - Unknown\n");
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
@@ -668,8 +702,14 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 		return ret;
 	}
-	ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-		AB8500_USB_LINE_STAT_REG, &val);
+	if (is_ab8500(di->parent)) {
+		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+				AB8500_USB_LINE_STAT_REG, &val);
+	} else {
+		if (is_ab9540(di->parent) || is_ab8505(di->parent))
+			ret = abx500_get_register_interruptible(di->dev,
+				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
+	}
 	if (ret < 0) {
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 		return ret;
@@ -709,8 +749,13 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
 		}
-		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-			AB8500_USB_LINE_STAT_REG, &val);
+
+		if (is_ab8500(di->parent))
+			ret = abx500_get_register_interruptible(di->dev,
+				AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
+		else
+			ret = abx500_get_register_interruptible(di->dev,
+				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
 		if (ret < 0) {
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
@@ -2922,7 +2967,6 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	}
 
 	if (charger_status & USB_PW_CONN) {
-		dev_dbg(di->dev, "VBUS Detect during startup\n");
 		di->vbus_detected = true;
 		di->vbus_detected_start = true;
 		queue_work(di->charger_wq,
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 73479f0..25af2c1 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -23,6 +23,7 @@
  * Bank : 0x5
  */
 #define AB8500_USB_LINE_STAT_REG	0x80
+#define AB8500_USB_LINK1_STAT_REG	0x94
 
 /*
  * Charger / status register offfsets
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 15/57] power: ab8500_fg: Round capacity output
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (13 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 14/57] power: Adds support for Car/Travel Adapters mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 16/57] power: bm remove superfluous BTEMP thermal comp mathieu.poirier
                   ` (42 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: pender01 <peter.enderborg@stericsson.com>

Round the capacity values for better enduser experience.

Signed-off-by: pender01 <peter.enderborg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |   28 +++++++++++++++-------------
 1 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 492f6bf..c4d9307 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -31,6 +31,7 @@
 #include <linux/mfd/abx500.h>
 #include <linux/time.h>
 #include <linux/completion.h>
+#include <linux/kernel.h>
 
 #define MILLI_TO_MICRO			1000
 #define FG_LSB_IN_MA			1627
@@ -1160,7 +1161,7 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di)
 {
 	int ret, percent;
 
-	percent = di->bat_cap.permille / 10;
+	percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
 
 	if (percent <= di->bat->cap_levels->critical ||
 		di->flags.low_bat)
@@ -1281,6 +1282,7 @@ static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di)
 static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 {
 	bool changed = false;
+	int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
 
 	di->bat_cap.level = ab8500_fg_capacity_level(di);
 
@@ -1312,6 +1314,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 		dev_dbg(di->dev, "Battery low, set capacity to 0\n");
 		di->bat_cap.prev_percent = 0;
 		di->bat_cap.permille = 0;
+		percent = 0;
 		di->bat_cap.prev_mah = 0;
 		di->bat_cap.mah = 0;
 		changed = true;
@@ -1321,7 +1324,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 		 * and show 100% during maintenance charging (scaling).
 		 */
 		if (di->flags.force_full) {
-			di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+			di->bat_cap.prev_percent = percent;
 			di->bat_cap.prev_mah = di->bat_cap.mah;
 
 			changed = true;
@@ -1334,19 +1337,18 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 						di->bat_cap.prev_percent;
 				di->bat_cap.cap_scale.disable_cap_level = 100;
 			}
-		} else if (di->bat_cap.prev_percent !=
-			(di->bat_cap.permille) / 10) {
+		} else if (di->bat_cap.prev_percent != percent) {
 			dev_dbg(di->dev,
 				"battery reported full "
 				"but capacity dropping: %d\n",
-				di->bat_cap.permille / 10);
-			di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+				percent);
+			di->bat_cap.prev_percent = percent;
 			di->bat_cap.prev_mah = di->bat_cap.mah;
 
 			changed = true;
 		}
-	} else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
-		if (di->bat_cap.permille / 10 == 0) {
+	} else if (di->bat_cap.prev_percent != percent) {
+		if (percent == 0) {
 			/*
 			 * We will not report 0% unless we've got
 			 * the LOW_BAT IRQ, no matter what the FG
@@ -1356,11 +1358,11 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 			di->bat_cap.permille = 1;
 			di->bat_cap.prev_mah = 1;
 			di->bat_cap.mah = 1;
+			percent = 1;
 
 			changed = true;
 		} else if (!(!di->flags.charging &&
-			(di->bat_cap.permille / 10) >
-			di->bat_cap.prev_percent) || init) {
+			percent > di->bat_cap.prev_percent) || init) {
 			/*
 			 * We do not allow reported capacity to go up
 			 * unless we're charging or if we're in init
@@ -1368,9 +1370,9 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 			dev_dbg(di->dev,
 				"capacity changed from %d to %d (%d)\n",
 				di->bat_cap.prev_percent,
-				di->bat_cap.permille / 10,
+				percent,
 				di->bat_cap.permille);
-			di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+			di->bat_cap.prev_percent = percent;
 			di->bat_cap.prev_mah = di->bat_cap.mah;
 
 			changed = true;
@@ -1378,7 +1380,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 			dev_dbg(di->dev, "capacity not allowed to go up since "
 				"no charger is connected: %d to %d (%d)\n",
 				di->bat_cap.prev_percent,
-				di->bat_cap.permille / 10,
+				percent,
 				di->bat_cap.permille);
 		}
 	}
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 16/57] power: bm remove superfluous BTEMP thermal comp.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (14 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 15/57] power: ab8500_fg: Round capacity output mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV mathieu.poirier
                   ` (41 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

BTEMP thermal compensation factor times 10 is applied in two
places, probe and get_property. Removed from probe.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_btemp.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 04f9dec..74cddba 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -1026,8 +1026,8 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 		ab8500_btemp_periodic_work);
 
 	/* Set BTEMP thermal limits. Low and Med are fixed */
-	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT * 10;
-	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT * 10;
+	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
+	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
 
 	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
 		AB8500_BTEMP_HIGH_TH, &val);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (15 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 16/57] power: bm remove superfluous BTEMP thermal comp mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:36   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 18/57] power: Add sysfs interfaces for capacity mathieu.poirier
                   ` (40 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

Add support for the battery over-voltage situation

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_fg.c |   32 ++++++++++++++++----------------
 1 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index c4d9307..8507254 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -1842,24 +1842,26 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
 	 * If we have had a battery over-voltage situation,
 	 * check ovv-bit to see if it should be reset.
 	 */
-	if (di->flags.bat_ovv) {
-		ret = abx500_get_register_interruptible(di->dev,
-			AB8500_CHARGER, AB8500_CH_STAT_REG,
-			&reg_value);
-		if (ret < 0) {
-			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-			return;
-		}
-		if ((reg_value & BATT_OVV) != BATT_OVV) {
-			dev_dbg(di->dev, "Battery recovered from OVV\n");
-			di->flags.bat_ovv = false;
+	ret = abx500_get_register_interruptible(di->dev,
+		AB8500_CHARGER, AB8500_CH_STAT_REG,
+		&reg_value);
+	if (ret < 0) {
+		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+		return;
+	}
+	if ((reg_value & BATT_OVV) == BATT_OVV) {
+		if (!di->flags.bat_ovv) {
+			dev_dbg(di->dev, "Battery OVV\n");
+			di->flags.bat_ovv = true;
 			power_supply_changed(&di->fg_psy);
-			return;
 		}
-
 		/* Not yet recovered from ovv, reschedule this test */
 		queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
-				   round_jiffies(HZ));
+			HZ);
+		} else {
+			dev_dbg(di->dev, "Battery recovered from OVV\n");
+			di->flags.bat_ovv = false;
+			power_supply_changed(&di->fg_psy);
 	}
 }
 
@@ -2039,8 +2041,6 @@ static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
 	struct ab8500_fg *di = _di;
 
 	dev_dbg(di->dev, "Battery OVV\n");
-	di->flags.bat_ovv = true;
-	power_supply_changed(&di->fg_psy);
 
 	/* Schedule a new HW failure check */
 	queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 18/57] power: Add sysfs interfaces for capacity
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (16 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:08   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 19/57] power: remove unused defines mathieu.poirier
                   ` (39 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Daniel WILLERUD <daniel.willerud@stericsson.com>

Switchable depending on whether capacity scaling is enabled

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Daniel WILLERUD <daniel.willerud@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |   57 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 56 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 8507254..46010ec 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -266,7 +266,6 @@ static enum power_supply_property ab8500_fg_props[] = {
 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 	POWER_SUPPLY_PROP_CHARGE_FULL,
 	POWER_SUPPLY_PROP_CHARGE_NOW,
-	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 };
 
@@ -2543,6 +2542,54 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
 
 	return ret;
 }
+
+static ssize_t ab8500_show_capacity(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+	int capacity;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (di->bat->capacity_scaling)
+		capacity = di->bat_cap.cap_scale.scaled_cap;
+	else
+		capacity = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", capacity);
+}
+
+static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
+	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
+};
+
+static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ab8500_fg_sysfs_psy_attrs); i++)
+		if (device_create_file(dev, &ab8500_fg_sysfs_psy_attrs[i]))
+			goto sysfs_psy_create_attrs_failed;
+
+	return 0;
+
+sysfs_psy_create_attrs_failed:
+	dev_err(dev, "Failed creating sysfs psy attrs.\n");
+	while (i--)
+		device_remove_file(dev, &ab8500_fg_sysfs_psy_attrs[i]);
+
+	return -EIO;
+}
+
+static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ab8500_fg_sysfs_psy_attrs); i++)
+		(void)device_remove_file(dev, &ab8500_fg_sysfs_psy_attrs[i]);
+}
 /* Exposure to the sysfs interface <<END>> */
 
 #if defined(CONFIG_PM)
@@ -2599,6 +2646,7 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev)
 	ab8500_fg_sysfs_exit(di);
 
 	flush_scheduled_work();
+	ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev);
 	power_supply_unregister(&di->fg_psy);
 	platform_set_drvdata(pdev, NULL);
 	kfree(di);
@@ -2754,6 +2802,13 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 		goto free_irq;
 	}
 
+	ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
+	if (ret) {
+		dev_err(di->dev, "failed to create FG psy\n");
+		ab8500_fg_sysfs_exit(di);
+		goto free_irq;
+	}
+
 	/* Calibrate the fg first time */
 	di->flags.calibrate = true;
 	di->calib_state = AB8500_FG_CALIB_INIT;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 19/57] power: remove unused defines.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (17 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 18/57] power: Add sysfs interfaces for capacity mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 20/57] power: Adds support for legacy USB chargers mathieu.poirier
                   ` (38 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

Cleanup of the ab8500_charger driver.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/ab8500_charger.c |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 09edd9c..4155feb 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -112,11 +112,6 @@ enum ab8500_charger_link_status {
 	USB_STAT_CARKIT_1,
 	USB_STAT_CARKIT_2,
 	USB_STAT_ACA_DOCK_CHARGER,
-	USB_STAT_SAMSUNG_USB_PHY_DIS,
-	USB_STAT_SAMSUNG_USB_PHY_ENA,
-	USB_STAT_SAMSUNG_UART_PHY_DIS,
-	USB_STAT_SAMSUNG_UART_PHY_ENA,
-	USB_STAT_MOTOROLA_USB_PHY_ENA,
 };
 
 enum ab8500_usb_state {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 20/57] power: Adds support for legacy USB chargers
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (18 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 19/57] power: remove unused defines mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:15   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 21/57] power: Overflow in current calculation mathieu.poirier
                   ` (37 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

A Legacy USB charger should be handled directly by the charger
driver.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c |   66 ++++++++++++++++++++++++++++++++++-----
 1 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 4155feb..7cd4165 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -85,6 +85,9 @@
 /* Step up/down delay in us */
 #define STEP_UDELAY			1000
 
+/* Wait for enumeration before charging in ms */
+#define WAIT_FOR_USB_ENUMERATION	(5 * 1000)
+
 #define CHARGER_STATUS_POLL 10 /* in ms */
 
 /* UsbLineStatus register - usb types */
@@ -198,6 +201,7 @@ struct ab8500_charger_usb_state {
  *			charger is enabled
  * @vbat		Battery voltage
  * @old_vbat		Previously measured battery voltage
+ * @usb_device_is_unrecognised	USB device is unrecognised by the hardware
  * @autopower		Indicate if we should have automatic pwron after pwrloss
  * @parent:		Pointer to the struct ab8500
  * @gpadc:		Pointer to the struct gpadc
@@ -216,6 +220,7 @@ struct ab8500_charger_usb_state {
  * @check_usbchgnotok_work:	Work for checking USB charger not ok status
  * @kick_wd_work:		Work for kicking the charger watchdog in case
  *				of ABB rev 1.* due to the watchog logic bug
+ * @attach_work:		Work for checking the usb enumeration
  * @ac_charger_attached_work:	Work for checking if AC charger is still
  *				connected
  * @usb_charger_attached_work:	Work for checking if USB charger is still
@@ -239,6 +244,7 @@ struct ab8500_charger {
 	bool vddadc_en_usb;
 	int vbat;
 	int old_vbat;
+	bool usb_device_is_unrecognised;
 	bool autopower;
 	struct ab8500 *parent;
 	struct ab8500_gpadc *gpadc;
@@ -256,6 +262,7 @@ struct ab8500_charger {
 	struct delayed_work check_hw_failure_work;
 	struct delayed_work check_usbchgnotok_work;
 	struct delayed_work kick_wd_work;
+	struct delayed_work attach_work;
 	struct delayed_work ac_charger_attached_work;
 	struct delayed_work usb_charger_attached_work;
 	struct work_struct ac_work;
@@ -588,6 +595,8 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 {
 	int ret = 0;
 
+	di->usb_device_is_unrecognised = false;
+
 	switch (link_status) {
 	case USB_STAT_STD_HOST_NC:
 	case USB_STAT_STD_HOST_C_NS:
@@ -633,9 +642,15 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
 				di->max_usb_in_curr);
 		break;
+	case USB_STAT_NOT_CONFIGURED:
+		if (di->vbus_detected) {
+			di->usb_device_is_unrecognised = true;
+			dev_dbg(di->dev, "USB Type - Legacy charger.\n");
+			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+			break;
+		}
 	case USB_STAT_HM_IDGND:
 	case USB_STAT_NOT_VALID_LINK:
-	case USB_STAT_NOT_CONFIGURED:
 		dev_err(di->dev, "USB Type - Charging not allowed\n");
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
 		ret = -ENXIO;
@@ -1899,6 +1914,30 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
 }
 
 /**
+ * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
+ * @work:	pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
+{
+	int ret;
+
+	struct ab8500_charger *di = container_of(work,
+		struct ab8500_charger, attach_work.work);
+
+	/* Update maximum input current if USB enumeration is not detected */
+	if (!di->usb.charger_online) {
+		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+		if (ret)
+			return;
+	}
+
+	ab8500_charger_set_usb_connected(di, true);
+	ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
  * ab8500_charger_usb_link_status_work() - work to detect USB type
  * @work:	pointer to the work_struct structure
  *
@@ -1928,14 +1967,20 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 		di->vbus_detected = 1;
 		ret = ab8500_charger_read_usb_type(di);
 		if (!ret) {
-			/* Update maximum input current */
-			ret = ab8500_charger_set_vbus_in_curr(di,
-					di->max_usb_in_curr);
-			if (ret)
-				return;
-
-			ab8500_charger_set_usb_connected(di, true);
-			ab8500_power_supply_changed(di, &di->usb_chg.psy);
+			if (di->usb_device_is_unrecognised) {
+				dev_dbg(di->dev,
+					"Potential Legacy Charger device. "
+					"Delay work for %d msec for USB enum "
+					"to finish",
+					WAIT_FOR_USB_ENUMERATION);
+				queue_delayed_work(di->charger_wq,
+					&di->attach_work,
+					msecs_to_jiffies
+						(WAIT_FOR_USB_ENUMERATION));
+			} else {
+				queue_delayed_work(di->charger_wq,
+							&di->attach_work, 0);
+			}
 		} else if (ret == -ENXIO) {
 			/* No valid charger type detected */
 			ab8500_charger_set_usb_connected(di, false);
@@ -2886,6 +2931,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(&di->attach_work,
+		ab8500_charger_usb_link_attach_work);
+
 	INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work,
 		ab8500_charger_check_vbat_work);
 
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 21/57] power: Overflow in current calculation
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (19 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 20/57] power: Adds support for legacy USB chargers mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 22/57] power: AB workaround for invalid charger mathieu.poirier
                   ` (36 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>

When calculating the average current the nominator will
overflow when the charging current is high.

Signed-off-by: Henrik Sölver <henrik.solver@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Par-Olof HAKANSSON <par-olof.hakansson@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 46010ec..7c42150 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -805,15 +805,13 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
 
 	/*
 	 * Convert to unit value in mA
-	 * Full scale input voltage is
-	 * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
-	 * Given a 250ms conversion cycle time the LSB corresponds
-	 * to 112.9 nAh. Convert to current by dividing by the conversion
+	 * by dividing by the conversion
 	 * time in hours (= samples / (3600 * 4)h)
-	 * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+	 * and multiply with 1000
 	 */
-	di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
-		(1000 * di->bat->fg_res * (di->fg_samples / 4));
+
+	di->avg_curr = (di->accu_charge * 36) /
+			((di->fg_samples / 4) * 10);
 
 	di->flags.conv_done = true;
 
@@ -821,6 +819,9 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
 
 	queue_work(di->fg_wq, &di->fg_work);
 
+	dev_dbg(di->dev,
+		"fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d\n",
+		di->bat->fg_res, di->fg_samples, val, di->accu_charge);
 	return;
 exit:
 	dev_err(di->dev,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 22/57] power: AB workaround for invalid charger
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (20 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 21/57] power: Overflow in current calculation mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 23/57] power: Add plaform data charger configurables mathieu.poirier
                   ` (35 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Henrik Sölver <henrik.solver@stericsson.com>

AB8500 refuses to start charging when some types of non standard
chargers are connected. This change force the AB to start charging.

Signed-off-by: Henrik Sölver <henrik.solver@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Yvan FILLION <yvan.fillion@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c |   70 +++++++++++++++++++++++++++++++++++++--
 1 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 7cd4165..cbc9fd7 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -203,6 +203,7 @@ struct ab8500_charger_usb_state {
  * @old_vbat		Previously measured battery voltage
  * @usb_device_is_unrecognised	USB device is unrecognised by the hardware
  * @autopower		Indicate if we should have automatic pwron after pwrloss
+ * @invalid_charger_detect_state	State when forcing AB to use invalid charger
  * @parent:		Pointer to the struct ab8500
  * @gpadc:		Pointer to the struct gpadc
  * @pdata:		Pointer to the abx500_charger platform data
@@ -246,6 +247,7 @@ struct ab8500_charger {
 	int old_vbat;
 	bool usb_device_is_unrecognised;
 	bool autopower;
+	int invalid_charger_detect_state;
 	struct ab8500 *parent;
 	struct ab8500_gpadc *gpadc;
 	struct abx500_charger_platform_data *pdata;
@@ -650,7 +652,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 			break;
 		}
 	case USB_STAT_HM_IDGND:
-	case USB_STAT_NOT_VALID_LINK:
 		dev_err(di->dev, "USB Type - Charging not allowed\n");
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
 		ret = -ENXIO;
@@ -679,6 +680,9 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
 		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
 				di->max_usb_in_curr);
+	case USB_STAT_NOT_VALID_LINK:
+		dev_err(di->dev, "USB Type invalid - try charging anyway\n");
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
 		break;
 
 	default:
@@ -1945,7 +1949,9 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
  */
 static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 {
+	int detected_chargers;
 	int ret;
+	u8 val;
 
 	struct ab8500_charger *di = container_of(work,
 		struct ab8500_charger, usb_link_status_work);
@@ -1955,11 +1961,66 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	 * synchronously, we have the check if  is
 	 * connected by reading the status register
 	 */
-	ret = ab8500_charger_detect_chargers(di);
-	if (ret < 0)
+	detected_chargers = ab8500_charger_detect_chargers(di);
+	if (detected_chargers < 0)
 		return;
 
-	if (!(ret & USB_PW_CONN)) {
+	/*
+	 * Some chargers that breaks the USB spec is
+	 * identified as invalid by AB8500 and it refuse
+	 * to start the charging process. but by jumping
+	 * thru a few hoops it can be forced to start.
+	 */
+	ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+			AB8500_USB_LINE_STAT_REG, &val);
+	if (ret >= 0)
+		dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
+	else
+		dev_dbg(di->dev, "Error reading USB link status\n");
+
+	if (detected_chargers & USB_PW_CONN) {
+		if (((val & AB8500_USB_LINK_STATUS) >> 3) ==
+						USB_STAT_NOT_VALID_LINK &&
+				di->invalid_charger_detect_state == 0) {
+			dev_dbg(di->dev,
+				 "Invalid charger detected, state= 0\n");
+			/*Enable charger*/
+			abx500_mask_and_set_register_interruptible(di->dev,
+						AB8500_CHARGER,
+						AB8500_USBCH_CTRL1_REG,
+						0x01, 0x01)
+			/*Enable charger detection*/
+			abx500_mask_and_set_register_interruptible(di->dev,
+						AB8500_USB,
+						AB8500_MCH_IPT_CURLVL_REG,
+						0x01, 0x01);
+			di->invalid_charger_detect_state = 1;
+			/*exit and wait for new link status interrupt.*/
+			return;
+
+		}
+		if (di->invalid_charger_detect_state == 1) {
+			dev_dbg(di->dev,
+				"Invalid charger detected, state= 1\n");
+			/*Stop charger detection*/
+			abx500_mask_and_set_register_interruptible(di->dev,
+						AB8500_USB,
+						AB8500_MCH_IPT_CURLVL_REG,
+						0x01, 0x00);
+			/*Check link status*/
+			ret = abx500_get_register_interruptible(di->dev,
+						AB8500_USB,
+						AB8500_USB_LINE_STAT_REG,
+						&val);
+			dev_dbg(di->dev, "USB link status= 0x%02x\n",
+					(val & AB8500_USB_LINK_STATUS) >> 3);
+			di->invalid_charger_detect_state = 2;
+		}
+	} else {
+		di->invalid_charger_detect_state = 0;
+	}
+
+	if (!(detected_chargers & USB_PW_CONN)) {
 		di->vbus_detected = 0;
 		ab8500_charger_set_usb_connected(di, false);
 		ab8500_power_supply_changed(di, &di->usb_chg.psy);
@@ -2862,6 +2923,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	}
 
 	di->autopower = false;
+	di->invalid_charger_detect_state = 0;
 
 	/* AC supply */
 	/* power_supply base class */
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 23/57] power: Add plaform data charger configurables
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (21 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 22/57] power: AB workaround for invalid charger mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 24/57] power: ab8500_fg: Adjust for RF bursts voltage drops mathieu.poirier
                   ` (34 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Loic Pallardy <loic.pallardy@stericsson.com>

Allow to indicate wheter AC and USB charge capabilities are
supported from platform data.

Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/58093
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
---
 drivers/power/ab8500_charger.c            |   36 ++++++++++++++++++----------
 include/linux/mfd/abx500.h                |    2 +
 include/linux/mfd/abx500/ux500_chargalg.h |    2 +
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index cbc9fd7..3100f11 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2878,8 +2878,11 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 	destroy_workqueue(di->charger_wq);
 
 	flush_scheduled_work();
-	power_supply_unregister(&di->usb_chg.psy);
-	power_supply_unregister(&di->ac_chg.psy);
+	if (di->usb_chg.enabled)
+		power_supply_unregister(&di->usb_chg.psy);
+	if (di->ac_chg.enabled)
+		power_supply_unregister(&di->ac_chg.psy);
+
 	platform_set_drvdata(pdev, NULL);
 	kfree(di);
 
@@ -2942,6 +2945,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
 	di->ac_chg.max_out_curr = ab8500_charger_current_map[
 		ARRAY_SIZE(ab8500_charger_current_map) - 1];
+	di->ac_chg.enabled = di->pdata->ac_enabled;
 
 	/* USB supply */
 	/* power_supply base class */
@@ -2960,7 +2964,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
 	di->usb_chg.max_out_curr = ab8500_charger_current_map[
 		ARRAY_SIZE(ab8500_charger_current_map) - 1];
-
+	di->usb_chg.enabled = di->pdata->usb_enabled;
 
 	/* Create a work queue for the charger */
 	di->charger_wq =
@@ -3036,17 +3040,21 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	}
 
 	/* 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_regulator;
+	if (di->ac_chg.enabled) {
+		ret = power_supply_register(di->dev, &di->ac_chg.psy);
+		if (ret) {
+			dev_err(di->dev, "failed to register AC charger\n");
+			goto free_regulator;
+		}
 	}
 
 	/* Register USB charger class */
-	ret = power_supply_register(di->dev, &di->usb_chg.psy);
-	if (ret) {
-		dev_err(di->dev, "failed to register USB charger\n");
-		goto free_ac;
+	if (di->usb_chg.enabled) {
+		ret = power_supply_register(di->dev, &di->usb_chg.psy);
+		if (ret) {
+			dev_err(di->dev, "failed to register USB charger\n");
+			goto free_ac;
+		}
 	}
 
 	di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
@@ -3122,9 +3130,11 @@ free_irq:
 put_usb_phy:
 	usb_put_phy(di->usb_phy);
 free_usb:
-	power_supply_unregister(&di->usb_chg.psy);
+	if (di->usb_chg.enabled)
+		power_supply_unregister(&di->usb_chg.psy);
 free_ac:
-	power_supply_unregister(&di->ac_chg.psy);
+	if (di->ac_chg.enabled)
+		power_supply_unregister(&di->ac_chg.psy);
 free_regulator:
 	regulator_put(di->regu);
 free_charger_wq:
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 4d44a10..eef1ddc 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -398,6 +398,8 @@ struct abx500_charger_platform_data {
 	char **supplied_to;
 	size_t num_supplicants;
 	bool autopower_cfg;
+	bool ac_enabled;
+	bool usb_enabled;
 };
 
 struct abx500_btemp_platform_data {
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
index 9b07725..5b77a61 100644
--- a/include/linux/mfd/abx500/ux500_chargalg.h
+++ b/include/linux/mfd/abx500/ux500_chargalg.h
@@ -27,12 +27,14 @@ struct ux500_charger_ops {
  * @ops			ux500 charger operations
  * @max_out_volt	maximum output charger voltage in mV
  * @max_out_curr	maximum output charger current in mA
+ * @enabled		indicates if this charger is used or not
  */
 struct ux500_charger {
 	struct power_supply psy;
 	struct ux500_charger_ops ops;
 	int max_out_volt;
 	int max_out_curr;
+	bool enabled;
 };
 
 #endif
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 24/57] power: ab8500_fg: Adjust for RF bursts voltage drops.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (22 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 23/57] power: Add plaform data charger configurables mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 25/57] power: ab8500: adaptation to ab version mathieu.poirier
                   ` (33 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

Changed conditions for restarting low battery measurements counter
and adjusted the interval between measurements to avoid RF burst
induced voltage drops, and to shorten time to decide to shut down.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
---
 drivers/power/ab8500_fg.c |   44 ++++++++++++++++++++++++++++++--------------
 1 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 7c42150..861927d 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -42,7 +42,7 @@
 
 #define NBR_AVG_SAMPLES			20
 
-#define LOW_BAT_CHECK_INTERVAL		(2 * HZ)
+#define LOW_BAT_CHECK_INTERVAL		(HZ / 16) /* 62.5 ms */
 
 #define VALID_CAPACITY_SEC		(45 * 60) /* 45 minutes */
 #define BATT_OK_MIN			2360 /* mV */
@@ -168,6 +168,7 @@ struct inst_curr_result_list {
  * @recovery_cnt:	Counter for recovery mode
  * @high_curr_cnt:	Counter for high current mode
  * @init_cnt:		Counter for init mode
+ * @low_bat_cnt		Counter for number of consecutive low battery measures
  * @nbr_cceoc_irq_cnt	Counter for number of CCEOC irqs received since enabled
  * @recovery_needed:	Indicate if recovery is needed
  * @high_curr_mode:	Indicate if we're in high current mode
@@ -210,6 +211,7 @@ struct ab8500_fg {
 	int recovery_cnt;
 	int high_curr_cnt;
 	int init_cnt;
+	int low_bat_cnt;
 	int nbr_cceoc_irq_cnt;
 	bool recovery_needed;
 	bool high_curr_mode;
@@ -1882,25 +1884,29 @@ static void ab8500_fg_low_bat_work(struct work_struct *work)
 
 	/* Check if LOW_BAT still fulfilled */
 	if (vbat < di->bat->fg_params->lowbat_threshold) {
-		di->flags.low_bat = true;
-		dev_warn(di->dev, "Battery voltage still LOW\n");
-
-		/*
-		 * We need to re-schedule this check to be able to detect
-		 * if the voltage increases again during charging
-		 */
-		queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
-			round_jiffies(LOW_BAT_CHECK_INTERVAL));
+		/* Is it time to shut down? */
+		if (di->low_bat_cnt < 1) {
+			di->flags.low_bat = true;
+			dev_warn(di->dev, "Shut down pending...\n");
+		} else {
+			/*
+			* Else we need to re-schedule this check to be able
+			* to detect if the voltage increases again during
+			* charging or due to decreasing load.
+			*/
+			di->low_bat_cnt--;
+			dev_warn(di->dev, "Battery voltage still LOW\n");
+			queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+				round_jiffies(LOW_BAT_CHECK_INTERVAL));
+		}
 	} else {
-		di->flags.low_bat = false;
+		di->flags.low_bat_delay = false;
+		di->low_bat_cnt = 10;
 		dev_warn(di->dev, "Battery voltage OK again\n");
 	}
 
 	/* This is needed to dispatch LOW_BAT */
 	ab8500_fg_check_capacity_limits(di, false);
-
-	/* Set this flag to check if LOW_BAT IRQ still occurs */
-	di->flags.low_bat_delay = false;
 }
 
 /**
@@ -2059,6 +2065,10 @@ static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
 {
 	struct ab8500_fg *di = _di;
 
+	/*
+	 * Initiate handling in ab8500_fg_low_bat_work() if not already
+	 * initiated.
+	 */
 	if (!di->flags.low_bat_delay) {
 		dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
 		di->flags.low_bat_delay = true;
@@ -2748,6 +2758,12 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 	INIT_DELAYED_WORK_DEFERRABLE(&di->fg_check_hw_failure_work,
 		ab8500_fg_check_hw_failure_work);
 
+	/* Reset battery low voltage flag */
+	di->flags.low_bat = false;
+
+	/* Initialize low battery counter */
+	di->low_bat_cnt = 10;
+
 	/* Initialize OVV, and other registers */
 	ret = ab8500_fg_init_hw_registers(di);
 	if (ret) {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 25/57] power: ab8500: adaptation to ab version
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (23 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 24/57] power: ab8500_fg: Adjust for RF bursts voltage drops mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 26/57] power: charge: update watchdog for pm2xxx support mathieu.poirier
                   ` (32 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Michel JAOUEN <michel.jaouen@stericsson.com>

Add AB9540 and AB8505 support to ab8500_temp
driver.

Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_btemp.c         |   71 ++++++++++++++++++++++++++-------
 include/linux/mfd/abx500/ab8500-bm.h |    2 +
 2 files changed, 58 insertions(+), 15 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 74cddba..506f124 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -37,6 +37,9 @@
 #define BTEMP_BATCTRL_CURR_SRC_7UA	7
 #define BTEMP_BATCTRL_CURR_SRC_20UA	20
 
+#define BTEMP_BATCTRL_CURR_SRC_16UA	16
+#define BTEMP_BATCTRL_CURR_SRC_18UA	18
+
 #define to_ab8500_btemp_device_info(x) container_of((x), \
 	struct ab8500_btemp, btemp_psy);
 
@@ -212,10 +215,18 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 
 	/* Only do this for batteries with internal NTC */
 	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
-		if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
-			curr = BAT_CTRL_7U_ENA;
-		else
-			curr = BAT_CTRL_20U_ENA;
+
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
+				curr = BAT_CTRL_16U_ENA;
+			else
+				curr = BAT_CTRL_18U_ENA;
+		} else {
+			if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+				curr = BAT_CTRL_7U_ENA;
+			else
+				curr = BAT_CTRL_20U_ENA;
+		}
 
 		dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
 
@@ -246,11 +257,22 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 	} else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
 		dev_dbg(di->dev, "Disable BATCTRL curr source\n");
 
-		/* Write 0 to the curr bits */
-		ret = abx500_mask_and_set_register_interruptible(di->dev,
-			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-			BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
-			~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			/* Write 0 to the curr bits */
+			ret = abx500_mask_and_set_register_interruptible(
+				di->dev,
+				AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+				BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+				~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+		} else {
+			/* Write 0 to the curr bits */
+			ret = abx500_mask_and_set_register_interruptible(
+				di->dev,
+				AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+				BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+				~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+		}
+
 		if (ret) {
 			dev_err(di->dev, "%s failed disabling current source\n",
 				__func__);
@@ -292,11 +314,20 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 	 * if we got an error above
 	 */
 disable_curr_source:
-	/* Write 0 to the curr bits */
-	ret = abx500_mask_and_set_register_interruptible(di->dev,
+	if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+		/* Write 0 to the curr bits */
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
+			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+			BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+			~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+	} else {
+		/* Write 0 to the curr bits */
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
 			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
 			BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
 			~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+	}
+
 	if (ret) {
 		dev_err(di->dev, "%s failed disabling current source\n",
 			__func__);
@@ -510,8 +541,11 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 {
 	int res;
 	u8 i;
+	if (is_ab9540(di->parent) || is_ab8505(di->parent))
+		di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+	else
+		di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
 
-	di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
 	di->bat->batt_id = BATTERY_UNKNOWN;
 
 	res =  ab8500_btemp_get_batctrl_res(di);
@@ -548,9 +582,16 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 	 * detected type is Type 1, else we use the 7uA source
 	 */
 	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
-			di->bat->batt_id == 1) {
-		dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
-		di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+						 di->bat->batt_id == 1) {
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			dev_dbg(di->dev,
+					"Set BATCTRL current source to 16uA\n");
+			di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+		} else {
+			dev_dbg(di->dev,
+					"Set BATCTRL current source to 20uA\n");
+			di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+		}
 	}
 
 	return di->bat->batt_id;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 25af2c1..175197c 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -226,6 +226,8 @@
 /* BatCtrl Current Source Constants */
 #define BAT_CTRL_7U_ENA			0x01
 #define BAT_CTRL_20U_ENA		0x02
+#define BAT_CTRL_18U_ENA		0x01
+#define BAT_CTRL_16U_ENA		0x02
 #define BAT_CTRL_CMP_ENA		0x04
 #define FORCE_BAT_CTRL_CMP_HIGH		0x08
 #define BAT_CTRL_PULL_UP_ENA		0x10
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 26/57] power: charge: update watchdog for pm2xxx support
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (24 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 25/57] power: ab8500: adaptation to ab version mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 27/57] power: sysfs interface update mathieu.poirier
                   ` (31 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Loic Pallardy <loic.pallardy@stericsson.com>

AB and PMxxx doesn't have same watchdog refresh period
Add watchdog to refresh period parameters in x500 charger structure,
this should kick watchdog every 30sec.
The AC charging should also kick both pm2xxx
and the AB charger watchdog.

Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c            |    6 ++++++
 drivers/power/abx500_chargalg.c           |   23 +++++++++++++++++++++--
 include/linux/mfd/abx500/ux500_chargalg.h |    3 +++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 3100f11..4129599 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -90,6 +90,8 @@
 
 #define CHARGER_STATUS_POLL 10 /* in ms */
 
+#define CHG_WD_INTERVAL			(60 * HZ)
+
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
 	USB_STAT_NOT_CONFIGURED,
@@ -2945,7 +2947,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
 	di->ac_chg.max_out_curr = ab8500_charger_current_map[
 		ARRAY_SIZE(ab8500_charger_current_map) - 1];
+	di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
 	di->ac_chg.enabled = di->pdata->ac_enabled;
+	di->ac_chg.external = false;
 
 	/* USB supply */
 	/* power_supply base class */
@@ -2964,7 +2968,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
 	di->usb_chg.max_out_curr = ab8500_charger_current_map[
 		ARRAY_SIZE(ab8500_charger_current_map) - 1];
+	di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
 	di->usb_chg.enabled = di->pdata->usb_enabled;
+	di->usb_chg.external = false;
 
 	/* Create a work queue for the charger */
 	di->charger_wq =
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index ca87cb2..d3efc2a 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -453,8 +453,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 {
 	/* Check if charger exists and kick watchdog if charging */
 	if (di->ac_chg && di->ac_chg->ops.kick_wd &&
-			di->chg_info.online_chg & AC_CHG)
+			di->chg_info.online_chg & AC_CHG) {
+		/*
+		 * If AB charger watchdog expired, pm2xxx charging
+		 * gets disabled. To be safe, kick both AB charger watchdog
+		 * and pm2xxx watchdog.
+		 */
+		if (di->ac_chg->external &&
+				di->usb_chg && di->usb_chg->ops.kick_wd)
+			di->usb_chg->ops.kick_wd(di->usb_chg);
+
 		return di->ac_chg->ops.kick_wd(di->ac_chg);
+	}
 	else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
 			di->chg_info.online_chg & USB_CHG)
 		return di->usb_chg->ops.kick_wd(di->usb_chg);
@@ -1591,6 +1601,8 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
 static void abx500_chargalg_wd_work(struct work_struct *work)
 {
 	int ret;
+	int refresh_time;
+
 	struct abx500_chargalg *di = container_of(work,
 		struct abx500_chargalg, chargalg_wd_work.work);
 
@@ -1600,8 +1612,15 @@ static void abx500_chargalg_wd_work(struct work_struct *work)
 	if (ret < 0)
 		dev_err(di->dev, "failed to kick watchdog\n");
 
+	if (di->chg_info.online_chg & AC_CHG)
+		refresh_time = di->ac_chg->wdt_refresh;
+	else if (di->chg_info.online_chg & USB_CHG)
+		refresh_time = di->usb_chg->wdt_refresh;
+	else
+		refresh_time = CHG_WD_INTERVAL;
+
 	queue_delayed_work(di->chargalg_wq,
-		&di->chargalg_wd_work, CHG_WD_INTERVAL);
+		&di->chargalg_wd_work, refresh_time);
 }
 
 /**
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
index 5b77a61..d43ac0f 100644
--- a/include/linux/mfd/abx500/ux500_chargalg.h
+++ b/include/linux/mfd/abx500/ux500_chargalg.h
@@ -28,13 +28,16 @@ struct ux500_charger_ops {
  * @max_out_volt	maximum output charger voltage in mV
  * @max_out_curr	maximum output charger current in mA
  * @enabled		indicates if this charger is used or not
+ * @external		external charger unit (pm2xxx)
  */
 struct ux500_charger {
 	struct power_supply psy;
 	struct ux500_charger_ops ops;
 	int max_out_volt;
 	int max_out_curr;
+	int wdt_refresh;
 	bool enabled;
+	bool external;
 };
 
 #endif
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 27/57] power: sysfs interface update
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (25 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 26/57] power: charge: update watchdog for pm2xxx support mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:20   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 28/57] power: ab8500 - Accessing Autopower register fails mathieu.poirier
                   ` (30 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Michel JAOUEN <michel.jaouen@stericsson.com>

Add new sysfs interface to get current charge status

Signed-off-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c  |    3 +++
 drivers/power/abx500_chargalg.c |   24 +++++++++++++++++++++++-
 2 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 4129599..0a781a0 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2759,6 +2759,9 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
 	enum ab8500_usb_state bm_usb_state;
 	unsigned mA = *((unsigned *)power);
 
+	if (di == NULL)
+		return NOTIFY_DONE;
+
 	if (event != USB_EVENT_VBUS) {
 		dev_dbg(di->dev, "not a standard host, returning\n");
 		return NOTIFY_DONE;
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index d3efc2a..4e3d20f 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1679,6 +1679,27 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
 	return 0;
 }
 
+/**
+ * abx500_chargalg_sysfs_show() - sysfs show operations
+ * @kobj:      pointer to the struct kobject
+ * @attr:      pointer to the struct attribute
+ * @buf:       buffer that holds the parameter to send to userspace
+ *
+ * Returns a buffer to be displayed in user space
+ */
+static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
+	struct attribute *attr, char *buf)
+{
+	struct abx500_chargalg *di = container_of(kobj,
+		struct abx500_chargalg, chargalg_kobject);
+
+	if ((di->susp_status.ac_suspended == true) &&
+		(di->susp_status.usb_suspended == true))
+		return sprintf(buf, "0\n");
+	else
+		return sprintf(buf, "1\n");
+}
+
 /* Exposure to the sysfs interface */
 
 /**
@@ -1749,7 +1770,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
 static struct attribute abx500_chargalg_en_charger = \
 {
 	.name = "chargalg",
-	.mode = S_IWUGO,
+	.mode = S_IRUGO | S_IWUSR,
 };
 
 static struct attribute *abx500_chargalg_chg[] = {
@@ -1758,6 +1779,7 @@ static struct attribute *abx500_chargalg_chg[] = {
 };
 
 static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+	.show = abx500_chargalg_sysfs_show,
 	.store = abx500_chargalg_sysfs_charger,
 };
 
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 28/57] power: ab8500 - Accessing Autopower register fails
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (26 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 27/57] power: sysfs interface update mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 29/57] power: ab8500_fg: Goto INIT_RECOVERY when charger removed mathieu.poirier
                   ` (29 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Nicolas Guion <nicolas.guion@stericsson.com>

The fallback software control register has moved in the ab8505
and ab9540.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
Reviewed-by: Nicolas GUION <nicolas.guion@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c |   53 +++++++++++++++++++++++++++-------------
 1 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 0a781a0..ee5ad7b 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -92,6 +92,8 @@
 
 #define CHG_WD_INTERVAL			(60 * HZ)
 
+#define AB8500_SW_CONTROL_FALLBACK	0x03
+
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
 	USB_STAT_NOT_CONFIGURED,
@@ -307,42 +309,59 @@ static enum power_supply_property ab8500_charger_usb_props[] = {
 static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
 		bool fallback)
 {
+	u8 val;
 	u8 reg;
+	u8 bank;
+	u8 bit;
 	int ret;
 
 	dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
 
+	if (is_ab8500(di->parent)) {
+		bank = 0x15;
+		reg = 0x0;
+		bit = 3;
+	} else {
+		bank = AB8500_SYS_CTRL1_BLOCK;
+		reg = AB8500_SW_CONTROL_FALLBACK;
+		bit = 0;
+	}
+
 	/* read the register containing fallback bit */
-	ret = abx500_get_register_interruptible(di->dev, 0x15, 0x00, &reg);
-	if (ret) {
-		dev_err(di->dev, "%d write failed\n", __LINE__);
+	ret = abx500_get_register_interruptible(di->dev, bank, reg, &val);
+	if (ret < 0) {
+		dev_err(di->dev, "%d read failed\n", __LINE__);
 		return;
 	}
 
-	/* enable the OPT emulation registers */
-	ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
-	if (ret) {
-		dev_err(di->dev, "%d write failed\n", __LINE__);
-		return;
+	if (is_ab8500(di->parent)) {
+		/* enable the OPT emulation registers */
+		ret = abx500_set_register_interruptible(di->dev,
+							 0x11, 0x00, 0x2);
+		if (ret) {
+			dev_err(di->dev, "%d write failed\n", __LINE__);
+			goto disable_otp;
+		}
 	}
 
 	if (fallback)
-		reg |= 0x8;
+		val |= (1 << bit);
 	else
-		reg &= ~0x8;
+		val &= ~(1 << bit);
 
 	/* write back the changed fallback bit value to register */
-	ret = abx500_set_register_interruptible(di->dev, 0x15, 0x00, reg);
+	ret = abx500_set_register_interruptible(di->dev, bank, reg, val);
 	if (ret) {
 		dev_err(di->dev, "%d write failed\n", __LINE__);
-		return;
 	}
 
-	/* disable the set OTP registers again */
-	ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
-	if (ret) {
-		dev_err(di->dev, "%d write failed\n", __LINE__);
-		return;
+disable_otp:
+	if (is_ab8500(di->parent)) {
+		/* disable the set OTP registers again */
+		ret = abx500_set_register_interruptible(di->dev,
+							 0x11, 0x00, 0x0);
+		if (ret)
+			dev_err(di->dev, "%d write failed\n", __LINE__);
 	}
 }
 
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 29/57] power: ab8500_fg: Goto INIT_RECOVERY when charger removed
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (27 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 28/57] power: ab8500 - Accessing Autopower register fails mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 30/57] power: ab8500: Flush & sync all works mathieu.poirier
                   ` (28 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Martin Bergström <martin.bergstrom@stericsson.com>

When the charger is removed we need to go to INIT_RECOVERY
state instead of directly to RECOVERY state.

Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 861927d..e7a0e1f 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -1644,7 +1644,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
 
 			if (di->recovery_needed) {
 				ab8500_fg_discharge_state_to(di,
-					AB8500_FG_DISCHARGE_RECOVERY);
+					AB8500_FG_DISCHARGE_INIT_RECOVERY);
 
 				queue_delayed_work(di->fg_wq,
 					&di->fg_periodic_work, 0);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 30/57] power: ab8500: Flush & sync all works
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (28 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 29/57] power: ab8500_fg: Goto INIT_RECOVERY when charger removed mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:23   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design mathieu.poirier
                   ` (27 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Flush and sync all workqueues at suspend to avoid
that we suspend in the middle of a work.

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
---
 drivers/power/ab8500_charger.c |   11 +++++++++++
 drivers/power/ab8500_fg.c      |    5 +++++
 2 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index ee5ad7b..071c7c2 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2862,6 +2862,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 	if (delayed_work_pending(&di->check_hw_failure_work))
 		cancel_delayed_work(&di->check_hw_failure_work);
 
+	flush_delayed_work_sync(&di->attach_work);
+	flush_delayed_work_sync(&di->usb_charger_attached_work);
+	flush_delayed_work_sync(&di->ac_charger_attached_work);
+	flush_delayed_work_sync(&di->check_usbchgnotok_work);
+	flush_delayed_work_sync(&di->check_vbat_work);
+	flush_delayed_work_sync(&di->kick_wd_work);
+
+	flush_work_sync(&di->usb_link_status_work);
+	flush_work_sync(&di->ac_work);
+	flush_work_sync(&di->detect_usb_type_work);
+
 	return 0;
 }
 #else
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index e7a0e1f..0e71e7e 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2626,6 +2626,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
 	struct ab8500_fg *di = platform_get_drvdata(pdev);
 
 	flush_delayed_work_sync(&di->fg_periodic_work);
+	flush_work_sync(&di->fg_work);
+	flush_work_sync(&di->fg_acc_cur_work);
+	flush_delayed_work_sync(&di->fg_reinit_work);
+	flush_delayed_work_sync(&di->fg_low_bat_work);
+	flush_delayed_work_sync(&di->fg_check_hw_failure_work);
 
 	/*
 	 * If the FG is enabled we will disable it before going to suspend
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (29 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 30/57] power: ab8500: Flush & sync all works mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:27   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 32/57] power: ab8500_charger: Do not touch VBUSOVV bits mathieu.poirier
                   ` (26 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>

If battery is not identified while fg probe, mah_max_design gets
initialized with unknown battery's charge full design. Reinitialize
mah_max_design if battery is identified after fg probe.

Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
Reviewed-by: Rupesh KUMAR <rupesh.kumar@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/power/ab8500_fg.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 0e71e7e..5e4a46b 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2258,9 +2258,9 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
 		case POWER_SUPPLY_PROP_TECHNOLOGY:
 			switch (ext->type) {
 			case POWER_SUPPLY_TYPE_BATTERY:
-				if (!di->flags.batt_id_received) {
+				if (!di->flags.batt_id_received &&
+					di->bat->batt_id != BATTERY_UNKNOWN) {
 					const struct abx500_battery_type *b;
-
 					b = &(di->bat->bat_type[di->bat->batt_id]);
 
 					di->flags.batt_id_received = true;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 32/57] power: ab8500_charger: Do not touch VBUSOVV bits
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (30 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 33/57] power: u8500_charger: Delay for USB enumeration mathieu.poirier
                   ` (25 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Do not touch the VBUSOVV in USBCHTRL2 when running on AB8505.

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
---
 drivers/power/ab8500_charger.c    |   22 ++++++++++++++++------
 include/linux/mfd/abx500/ab8500.h |   10 ++++++++++
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 071c7c2..2a323cf 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2667,13 +2667,23 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
 		}
 	}
 
-	/* VBUS OVV set to 6.3V and enable automatic current limitiation */
-	ret = abx500_set_register_interruptible(di->dev,
-		AB8500_CHARGER,
-		AB8500_USBCH_CTRL2_REG,
-		VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+	if (is_ab9540_2p0(di->parent) || is_ab8505_2p0(di->parent))
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
+			AB8500_CHARGER,
+			AB8500_USBCH_CTRL2_REG,
+			VBUS_AUTO_IN_CURR_LIM_ENA,
+			VBUS_AUTO_IN_CURR_LIM_ENA);
+	else
+		/*
+		 * VBUS OVV set to 6.3V and enable automatic current limitation
+		 */
+		ret = abx500_set_register_interruptible(di->dev,
+			AB8500_CHARGER,
+			AB8500_USBCH_CTRL2_REG,
+			VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
 	if (ret) {
-		dev_err(di->dev, "failed to set VBUS OVV\n");
+		dev_err(di->dev,
+			"failed to set automatic current limitation\n");
 		goto out;
 	}
 
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 087b445..5a4b186 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -351,6 +351,16 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
 	return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
 }
 
+static inline int is_ab8505_2p0(struct ab8500 *ab)
+{
+	return (is_ab8505(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab9540_2p0(struct ab8500 *ab)
+{
+	return (is_ab9540(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
 int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq);
 
 #endif /* MFD_AB8500_H */
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 33/57] power: u8500_charger: Delay for USB enumeration
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (31 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 32/57] power: ab8500_charger: Do not touch VBUSOVV bits mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  7:42   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505 mathieu.poirier
                   ` (24 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>

If charging is started before USB enumeration of an
Accessory Charger Adapter has finished, the AB8500 will
generate a VBUS_ERROR. This in turn results in timeouts
and delays the enumeration with around 15 seconds.
This patch delays the charging and then ramps currents
slowly to avoid VBUS errors. The delay allows the enumeration
to have finished before charging is turned on.

Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/mfd/ab8500-core.c      |    6 +
 drivers/power/ab8500_charger.c |  435 +++++++++++++++++++++++++++++----------
 2 files changed, 329 insertions(+), 112 deletions(-)

diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 47adf80..ef13fe1 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -786,6 +786,12 @@ static struct resource __devinitdata ab8500_charger_resources[] = {
 		.end = AB8500_INT_CH_WD_EXP,
 		.flags = IORESOURCE_IRQ,
 	},
+	{
+		.name = "VBUS_CH_DROP_END",
+		.start = AB8500_INT_VBUS_CH_DROP_END,
+		.end = AB8500_INT_VBUS_CH_DROP_END,
+		.flags = IORESOURCE_IRQ,
+	},
 };
 
 static struct resource __devinitdata ab8500_btemp_resources[] = {
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 2a323cf..b3b8f77 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -52,6 +52,7 @@
 
 #define MAIN_CH_INPUT_CURR_SHIFT	4
 #define VBUS_IN_CURR_LIM_SHIFT		4
+#define AUTO_VBUS_IN_CURR_LIM_SHIFT	4
 
 #define LED_INDICATOR_PWM_ENA		0x01
 #define LED_INDICATOR_PWM_DIS		0x00
@@ -85,14 +86,13 @@
 /* Step up/down delay in us */
 #define STEP_UDELAY			1000
 
-/* Wait for enumeration before charging in ms */
-#define WAIT_FOR_USB_ENUMERATION	(5 * 1000)
-
 #define CHARGER_STATUS_POLL 10 /* in ms */
 
 #define CHG_WD_INTERVAL			(60 * HZ)
 
 #define AB8500_SW_CONTROL_FALLBACK	0x03
+/* Wait for enumeration before charging in us */
+#define WAIT_ACA_RID_ENUMERATION	(5 * 1000)
 
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
@@ -182,12 +182,14 @@ struct ab8500_charger_event_flags {
 	bool usbchargernotok;
 	bool chgwdexp;
 	bool vbus_collapse;
+	bool vbus_drop_end;
 };
 
 struct ab8500_charger_usb_state {
-	bool usb_changed;
 	int usb_current;
+	int usb_current_tmp;
 	enum ab8500_usb_state state;
+	enum ab8500_usb_state state_tmp;
 	spinlock_t usb_lock;
 };
 
@@ -207,6 +209,11 @@ struct ab8500_charger_usb_state {
  * @old_vbat		Previously measured battery voltage
  * @usb_device_is_unrecognised	USB device is unrecognised by the hardware
  * @autopower		Indicate if we should have automatic pwron after pwrloss
+ * @invalid_charger_detect_state:
+			State when forcing AB to use invalid charger
+ * @is_aca_rid:		Incicate if accessory is ACA type
+ * @current_stepping_sessions:
+ *			Counter for current stepping sessions
  * @invalid_charger_detect_state	State when forcing AB to use invalid charger
  * @parent:		Pointer to the struct ab8500
  * @gpadc:		Pointer to the struct gpadc
@@ -225,7 +232,6 @@ struct ab8500_charger_usb_state {
  * @check_usbchgnotok_work:	Work for checking USB charger not ok status
  * @kick_wd_work:		Work for kicking the charger watchdog in case
  *				of ABB rev 1.* due to the watchog logic bug
- * @attach_work:		Work for checking the usb enumeration
  * @ac_charger_attached_work:	Work for checking if AC charger is still
  *				connected
  * @usb_charger_attached_work:	Work for checking if USB charger is still
@@ -234,6 +240,8 @@ struct ab8500_charger_usb_state {
  * @detect_usb_type_work:	Work for detecting the USB type connected
  * @usb_link_status_work:	Work for checking the new USB link status
  * @usb_state_changed_work:	Work for checking USB state
+ * @attach_work:		Work for detecting USB type
+ * @vbus_drop_end_work:		Work for detecting VBUS drop end
  * @check_main_thermal_prot_work:
  *				Work for checking Main thermal status
  * @check_usb_thermal_prot_work:
@@ -252,6 +260,9 @@ struct ab8500_charger {
 	bool usb_device_is_unrecognised;
 	bool autopower;
 	int invalid_charger_detect_state;
+	bool is_usb_host;
+	int is_aca_rid;
+	atomic_t current_stepping_sessions;
 	struct ab8500 *parent;
 	struct ab8500_gpadc *gpadc;
 	struct abx500_charger_platform_data *pdata;
@@ -264,17 +275,19 @@ struct ab8500_charger {
 	struct ab8500_charger_info usb;
 	struct regulator *regu;
 	struct workqueue_struct *charger_wq;
+	struct mutex usb_ipt_crnt_lock;
 	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;
+	struct delayed_work usb_state_changed_work;
 	struct delayed_work attach_work;
 	struct delayed_work ac_charger_attached_work;
 	struct delayed_work usb_charger_attached_work;
+	struct delayed_work vbus_drop_end_work;
 	struct work_struct ac_work;
 	struct work_struct detect_usb_type_work;
 	struct work_struct usb_link_status_work;
-	struct work_struct usb_state_changed_work;
 	struct work_struct check_main_thermal_prot_work;
 	struct work_struct check_usb_thermal_prot_work;
 	struct usb_phy *usb_phy;
@@ -560,6 +573,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
 /**
  * ab8500_charger_detect_chargers() - Detect the connected chargers
  * @di:		pointer to the ab8500_charger structure
+ * @probe:     if probe, don't delay and wait for HW
  *
  * Returns the type of charger connected.
  * For USB it will not mean we can actually charge from it
@@ -570,10 +584,10 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
  * Returns an integer value, that means,
  * NO_PW_CONN  no power supply is connected
  * AC_PW_CONN  if the AC power supply is connected
- * USB_PW_CONN  if the USB power supply is connected
+ * USB_PW_CONN	if the USB power supply is connected
  * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
  */
-static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
 {
 	int result = NO_PW_CONN;
 	int ret;
@@ -591,6 +605,15 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
 		result = AC_PW_CONN;
 
 	/* Check for USB charger */
+	if (!probe) {
+		/*
+		 * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
+		 * when disconnecting ACA even though no
+		 * charger was connected. Try waiting a little
+		 * longer than the 100 ms of VBUS_DET_DBNC100...
+		 */
+		msleep(110);
+	}
 	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
 		AB8500_CH_USBCH_STAT1_REG, &val);
 	if (ret < 0) {
@@ -598,6 +621,9 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
 		return ret;
 	}
 
+	dev_dbg(di->dev,
+		"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__, val);
+
 	if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
 		result |= USB_PW_CONN;
 
@@ -620,33 +646,47 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 
 	di->usb_device_is_unrecognised = false;
 
+	/*
+	 * Platform only supports USB 2.0.
+	 * This means that charging current from USB source
+	 * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
+	 * should set USB_CH_IP_CUR_LVL_0P5.
+	 */
+
 	switch (link_status) {
 	case USB_STAT_STD_HOST_NC:
 	case USB_STAT_STD_HOST_C_NS:
 	case USB_STAT_STD_HOST_C_S:
 		dev_dbg(di->dev, "USB Type - Standard host is ");
 		dev_dbg(di->dev, "detected through USB driver\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->is_usb_host = true;
+		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_HOST_CHG_HS_CHIRP:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
+		di->is_usb_host = true;
+		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_HOST_CHG_HS:
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->is_usb_host = true;
+		di->is_aca_rid = 0;
+		break;
 	case USB_STAT_ACA_RID_C_HS:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
-		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
+		di->is_usb_host = false;
+		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_ACA_RID_A:
 		/*
 		 * Dedicated charger level minus maximum current accessory
-		 * can consume (300mA). Closest level is 1100mA
+		 * can consume (900mA). Closest level is 500mA
 		 */
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
-		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
+		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->is_usb_host = false;
+		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_ACA_RID_B:
 		/*
@@ -656,14 +696,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
 		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
 				di->max_usb_in_curr);
+		di->is_usb_host = false;
+		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_HOST_CHG_NM:
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->is_usb_host = true;
+		di->is_aca_rid = 0;
+		break;
 	case USB_STAT_DEDICATED_CHG:
-	case USB_STAT_ACA_RID_C_NM:
+		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+		di->is_usb_host = false;
+		di->is_aca_rid = 0;
+		break;
 	case USB_STAT_ACA_RID_C_HS_CHIRP:
+		case USB_STAT_ACA_RID_C_NM:
 		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
-		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
+		di->is_usb_host = false;
+		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_NOT_CONFIGURED:
 		if (di->vbus_detected) {
@@ -780,6 +830,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 		ret = abx500_get_register_interruptible(di->dev,
 			AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
 			&val);
+		dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
+							 __func__, val);
 		if (ret < 0) {
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
@@ -795,6 +847,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
 		}
+		dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n",
+							 __func__, val);
 		/*
 		 * Until the IT source register is read the UsbLineStatus
 		 * register is not updated, hence doing the same
@@ -1054,69 +1108,128 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
 static int ab8500_charger_set_current(struct ab8500_charger *di,
 	int ich, int reg)
 {
-	int ret, i;
-	int curr_index, prev_curr_index, shift_value;
+	int ret = 0;
+	int auto_curr_index, curr_index, prev_curr_index, shift_value, i;
 	u8 reg_value;
+	u32 step_udelay;
+	bool no_stepping = false;
+
+	atomic_inc(&di->current_stepping_sessions);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+		reg, &reg_value);
+	if (ret < 0) {
+		dev_err(di->dev, "%s read failed\n", __func__);
+		goto exit_set_current;
+	}
 
 	switch (reg) {
 	case AB8500_MCH_IPT_CURLVL_REG:
 		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+		prev_curr_index = (reg_value >> shift_value);
 		curr_index = ab8500_current_to_regval(ich);
+		step_udelay = STEP_UDELAY;
+		if (!di->ac.charger_connected)
+			no_stepping = true;
 		break;
 	case AB8500_USBCH_IPT_CRNTLVL_REG:
 		shift_value = VBUS_IN_CURR_LIM_SHIFT;
+		prev_curr_index = (reg_value >> shift_value);
 		curr_index = ab8500_vbus_in_curr_to_regval(ich);
+		step_udelay = STEP_UDELAY * 100;
+
+		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+					AB8500_CH_USBCH_STAT2_REG, &reg_value);
+
+		if (ret < 0) {
+			dev_err(di->dev, "%s read failed\n", __func__);
+			goto exit_set_current;
+		}
+		auto_curr_index =
+			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
+
+		dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
+			__func__,
+			ab8500_charger_vbus_in_curr_map[auto_curr_index]);
+
+		prev_curr_index = min(prev_curr_index, auto_curr_index);
+
+		if (!di->usb.charger_connected)
+			no_stepping = true;
 		break;
 	case AB8500_CH_OPT_CRNTLVL_REG:
 		shift_value = 0;
+		prev_curr_index = (reg_value >> shift_value);
 		curr_index = ab8500_current_to_regval(ich);
+		if (curr_index == 0)
+			step_udelay = STEP_UDELAY;
+		else if ((curr_index - prev_curr_index) > 1)
+			step_udelay = STEP_UDELAY * 100;
+		else
+			step_udelay = STEP_UDELAY;
+
+		if (!di->usb.charger_connected && !di->ac.charger_connected)
+			no_stepping = true;
+
 		break;
 	default:
 		dev_err(di->dev, "%s current register not valid\n", __func__);
-		return -ENXIO;
+		ret = -ENXIO;
+		goto exit_set_current;
 	}
 
 	if (curr_index < 0) {
 		dev_err(di->dev, "requested current limit out-of-range\n");
-		return -ENXIO;
-	}
-
-	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-		reg, &reg_value);
-	if (ret < 0) {
-		dev_err(di->dev, "%s read failed\n", __func__);
-		return ret;
+		ret = -ENXIO;
+		goto exit_set_current;
 	}
-	prev_curr_index = (reg_value >> shift_value);
 
 	/* only update current if it's been changed */
-	if (prev_curr_index == curr_index)
-		return 0;
+	if (prev_curr_index == curr_index) {
+		dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
+			__func__, reg);
+		ret = 0;
+		goto exit_set_current;
+	}
 
 	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
 		__func__, ich, reg);
 
-	if (prev_curr_index > curr_index) {
+	if (no_stepping) {
+		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+					reg, (u8) curr_index << shift_value);
+		if (ret)
+			dev_err(di->dev, "%s write failed\n", __func__);
+	} else if (prev_curr_index > curr_index) {
 		for (i = prev_curr_index - 1; i >= curr_index; i--) {
+			dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
+				(u8) i << shift_value, reg);
 			ret = abx500_set_register_interruptible(di->dev,
 				AB8500_CHARGER, reg, (u8) i << shift_value);
 			if (ret) {
 				dev_err(di->dev, "%s write failed\n", __func__);
-				return ret;
+				goto exit_set_current;
 			}
-			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+			if (i != curr_index)
+				usleep_range(step_udelay, step_udelay * 2);
 		}
 	} else {
 		for (i = prev_curr_index + 1; i <= curr_index; i++) {
+			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
+				(u8) i << shift_value, reg);
 			ret = abx500_set_register_interruptible(di->dev,
 				AB8500_CHARGER, reg, (u8) i << shift_value);
 			if (ret) {
 				dev_err(di->dev, "%s write failed\n", __func__);
-				return ret;
+				goto exit_set_current;
 			}
-			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+			if (i != curr_index)
+				usleep_range(step_udelay, step_udelay * 2);
 		}
 	}
+
+exit_set_current:
+	atomic_dec(&di->current_stepping_sessions);
 	return ret;
 }
 
@@ -1132,6 +1245,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 		int ich_in)
 {
 	int min_value;
+	int ret;
 
 	/* We should always use to lowest current limit */
 	min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
@@ -1149,8 +1263,14 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 		break;
 	}
 
-	return ab8500_charger_set_current(di, min_value,
+	dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
+
+	mutex_lock(&di->usb_ipt_crnt_lock);
+	ret = ab8500_charger_set_current(di, min_value,
 		AB8500_USBCH_IPT_CRNTLVL_REG);
+	mutex_unlock(&di->usb_ipt_crnt_lock);
+
+	return ret;
 }
 
 /**
@@ -1460,25 +1580,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 			dev_err(di->dev, "%s write failed\n", __func__);
 			return ret;
 		}
-		/* USBChInputCurr: current that can be drawn from the usb */
-		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
-		if (ret) {
-			dev_err(di->dev, "setting USBChInputCurr failed\n");
-			return ret;
-		}
-		/* ChOutputCurentLevel: protected output current */
-		ret = ab8500_charger_set_output_curr(di, ich_out);
-		if (ret) {
-			dev_err(di->dev,
-			 "%s Failed to set ChOutputCurentLevel\n",
-			 __func__);
-			return ret;
-		}
 		/* Check if VBAT overshoot control should be enabled */
 		if (!di->bat->enable_overshoot)
 			overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
 
 		/* Enable USB Charger */
+		dev_dbg(di->dev,
+			"Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
 		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
 			AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
 		if (ret) {
@@ -1491,11 +1599,30 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 		if (ret < 0)
 			dev_err(di->dev, "failed to enable LED\n");
 
+		di->usb.charger_online = 1;
+
+		/* USBChInputCurr: current that can be drawn from the usb */
+		ret = ab8500_charger_set_vbus_in_curr(di,
+					di->max_usb_in_curr);
+		if (ret) {
+			dev_err(di->dev, "setting USBChInputCurr failed\n");
+			return ret;
+		}
+
+		/* ChOutputCurentLevel: protected output current */
+		ret = ab8500_charger_set_output_curr(di, ich_out);
+		if (ret) {
+			dev_err(di->dev,
+				"%s Failed to set ChOutputCurentLevel\n",
+				__func__);
+			return ret;
+		}
+
 		queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
 
-		di->usb.charger_online = 1;
 	} else {
 		/* Disable USB charging */
+		dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
 		ret = abx500_set_register_interruptible(di->dev,
 			AB8500_CHARGER,
 			AB8500_USBCH_CTRL1_REG, 0);
@@ -1508,7 +1635,21 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 		ret = ab8500_charger_led_en(di, false);
 		if (ret < 0)
 			dev_err(di->dev, "failed to disable LED\n");
+		/* USBChInputCurr: current that can be drawn from the usb */
+		ret = ab8500_charger_set_vbus_in_curr(di, 0);
+		if (ret) {
+			dev_err(di->dev, "setting USBChInputCurr failed\n");
+			return ret;
+		}
 
+		/* ChOutputCurentLevel: protected output current */
+		ret = ab8500_charger_set_output_curr(di, 0);
+		if (ret) {
+			dev_err(di->dev,
+				"%s Failed to reset ChOutputCurentLevel\n",
+				__func__);
+			return ret;
+		}
 		di->usb.charger_online = 0;
 		di->usb.wd_expired = false;
 
@@ -1791,7 +1932,7 @@ static void ab8500_charger_ac_work(struct work_struct *work)
 	 * synchronously, we have the check if the main charger is
 	 * connected by reading the status register
 	 */
-	ret = ab8500_charger_detect_chargers(di);
+	ret = ab8500_charger_detect_chargers(di, false);
 	if (ret < 0)
 		return;
 
@@ -1899,16 +2040,18 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
 	 * synchronously, we have the check if is
 	 * connected by reading the status register
 	 */
-	ret = ab8500_charger_detect_chargers(di);
+	ret = ab8500_charger_detect_chargers(di, false);
 	if (ret < 0)
 		return;
 
 	if (!(ret & USB_PW_CONN)) {
-		di->vbus_detected = 0;
+		dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
+		di->vbus_detected = false;
 		ab8500_charger_set_usb_connected(di, false);
 		ab8500_power_supply_changed(di, &di->usb_chg.psy);
 	} else {
-		di->vbus_detected = 1;
+		dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
+		di->vbus_detected = true;
 
 		if (is_ab8500_1p1_or_earlier(di->parent)) {
 			ret = ab8500_charger_detect_usb_type(di);
@@ -1918,7 +2061,8 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
 							    &di->usb_chg.psy);
 			}
 		} else {
-			/* For ABB cut2.0 and onwards we have an IRQ,
+			/*
+			 * For ABB cut2.0 and onwards we have an IRQ,
 			 * USB_LINK_STATUS that will be triggered when the USB
 			 * link status changes. The exception is USB connected
 			 * during startup. Then we don't get a
@@ -1939,7 +2083,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
 }
 
 /**
- * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
+ * ab8500_charger_usb_link_attach_work() - work to detect USB type
  * @work:	pointer to the work_struct structure
  *
  * Detect the type of USB plugged
@@ -1979,10 +2123,10 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 
 	/*
 	 * Since we can't be sure that the events are received
-	 * synchronously, we have the check if  is
+	 * synchronously, we have the check if	is
 	 * connected by reading the status register
 	 */
-	detected_chargers = ab8500_charger_detect_chargers(di);
+	detected_chargers = ab8500_charger_detect_chargers(di, false);
 	if (detected_chargers < 0)
 		return;
 
@@ -2009,7 +2153,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 			abx500_mask_and_set_register_interruptible(di->dev,
 						AB8500_CHARGER,
 						AB8500_USBCH_CTRL1_REG,
-						0x01, 0x01)
+						0x01, 0x01);
 			/*Enable charger detection*/
 			abx500_mask_and_set_register_interruptible(di->dev,
 						AB8500_USB,
@@ -2042,32 +2186,46 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	}
 
 	if (!(detected_chargers & USB_PW_CONN)) {
-		di->vbus_detected = 0;
+		di->vbus_detected = false;
 		ab8500_charger_set_usb_connected(di, false);
 		ab8500_power_supply_changed(di, &di->usb_chg.psy);
-	} else {
-		di->vbus_detected = 1;
-		ret = ab8500_charger_read_usb_type(di);
-		if (!ret) {
-			if (di->usb_device_is_unrecognised) {
-				dev_dbg(di->dev,
-					"Potential Legacy Charger device. "
-					"Delay work for %d msec for USB enum "
-					"to finish",
-					WAIT_FOR_USB_ENUMERATION);
-				queue_delayed_work(di->charger_wq,
-					&di->attach_work,
-					msecs_to_jiffies
-						(WAIT_FOR_USB_ENUMERATION));
-			} else {
-				queue_delayed_work(di->charger_wq,
-							&di->attach_work, 0);
-			}
-		} else if (ret == -ENXIO) {
+		return;
+	}
+
+	dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
+	di->vbus_detected = true;
+	ret = ab8500_charger_read_usb_type(di);
+	if (ret) {
+		if (ret == -ENXIO) {
 			/* No valid charger type detected */
 			ab8500_charger_set_usb_connected(di, false);
 			ab8500_power_supply_changed(di, &di->usb_chg.psy);
 		}
+		return;
+	}
+
+	if (di->usb_device_is_unrecognised) {
+		dev_dbg(di->dev,
+			"Potential Legacy Charger device. "
+			"Delay work for %d msec for USB enum "
+			"to finish",
+			WAIT_ACA_RID_ENUMERATION);
+		queue_delayed_work(di->charger_wq,
+			&di->attach_work,
+			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+	} else if (di->is_aca_rid == 1) {
+		/* Only wait once */
+		di->is_aca_rid++;
+		dev_dbg(di->dev,
+			"%s Wait %d msec for USB enum to finish",
+			__func__, WAIT_ACA_RID_ENUMERATION);
+		queue_delayed_work(di->charger_wq,
+			&di->attach_work,
+			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+	} else {
+		queue_delayed_work(di->charger_wq,
+			&di->attach_work,
+			0);
 	}
 }
 
@@ -2077,24 +2235,20 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
 	unsigned long flags;
 
 	struct ab8500_charger *di = container_of(work,
-		struct ab8500_charger, usb_state_changed_work);
+		struct ab8500_charger, usb_state_changed_work.work);
 
-	if (!di->vbus_detected)
+	if (!di->vbus_detected) {
+		dev_dbg(di->dev,
+			"%s !di->vbus_detected\n",
+			__func__);
 		return;
+	}
 
 	spin_lock_irqsave(&di->usb_state.usb_lock, flags);
-	di->usb_state.usb_changed = false;
+	di->usb_state.state = di->usb_state.state_tmp;
+	di->usb_state.usb_current = di->usb_state.usb_current_tmp;
 	spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
 
-	/*
-	 * wait for some time until you get updates from the usb stack
-	 * and negotiations are completed
-	 */
-	msleep(250);
-
-	if (di->usb_state.usb_changed)
-		return;
-
 	dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
 		__func__, di->usb_state.state, di->usb_state.usb_current);
 
@@ -2332,6 +2486,21 @@ static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
 	return IRQ_HANDLED;
 }
 
+static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
+{
+	struct ab8500_charger *di = container_of(work,
+		struct ab8500_charger, vbus_drop_end_work.work);
+
+	di->flags.vbus_drop_end = false;
+
+	/* Reset the drop counter */
+	abx500_set_register_interruptible(di->dev,
+				AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
+
+	if (di->usb.charger_connected)
+		ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+}
+
 /**
  * ab8500_charger_vbusdetf_handler() - VBUS falling detected
  * @irq:       interrupt number
@@ -2343,6 +2512,7 @@ static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
 {
 	struct ab8500_charger *di = _di;
 
+	di->vbus_detected = false;
 	dev_dbg(di->dev, "VBUS falling detected\n");
 	queue_work(di->charger_wq, &di->detect_usb_type_work);
 
@@ -2470,6 +2640,25 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
 }
 
 /**
+ * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
+{
+	struct ab8500_charger *di = _di;
+
+	dev_dbg(di->dev, "VBUS charger drop ended\n");
+	di->flags.vbus_drop_end = true;
+	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
+						round_jiffies(30 * HZ));
+
+	return IRQ_HANDLED;
+}
+
+/**
  * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
  * @irq:       interrupt number
  * @_di:       pointer to the ab8500_charger structure
@@ -2559,9 +2748,9 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
 
 /**
  * ab8500_charger_usb_get_property() - get the usb properties
- * @psy:        pointer to the power_supply structure
- * @psp:        pointer to the power_supply_property structure
- * @val:        pointer to the power_supply_propval union
+ * @psy:	pointer to the power_supply structure
+ * @psp:	pointer to the power_supply_property structure
+ * @val:	pointer to the power_supply_propval union
  *
  * This function gets called when an application tries to get the usb
  * properties by reading the sysfs files.
@@ -2739,6 +2928,12 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
 		goto out;
 	}
 
+	ret = ab8500_charger_led_en(di, false);
+	if (ret < 0) {
+		dev_err(di->dev, "failed to disable LED\n");
+		goto out;
+	}
+
 	/* Backup battery voltage and current */
 	ret = abx500_set_register_interruptible(di->dev,
 		AB8500_RTC,
@@ -2778,6 +2973,7 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
 	{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
 	{"VBUS_OVV", ab8500_charger_vbusovv_handler},
 	{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+	{"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
 };
 
 static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
@@ -2814,13 +3010,15 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
 		__func__, bm_usb_state, mA);
 
 	spin_lock(&di->usb_state.usb_lock);
-	di->usb_state.usb_changed = true;
+	di->usb_state.state_tmp = bm_usb_state;
+	di->usb_state.usb_current_tmp = mA;
 	spin_unlock(&di->usb_state.usb_lock);
 
-	di->usb_state.state = bm_usb_state;
-	di->usb_state.usb_current = mA;
-
-	queue_work(di->charger_wq, &di->usb_state_changed_work);
+	/*
+	 * wait for some time until you get updates from the usb stack
+	 * and negotiations are completed
+	 */
+	queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
 
 	return NOTIFY_OK;
 }
@@ -2860,6 +3058,9 @@ static int ab8500_charger_resume(struct platform_device *pdev)
 			&di->check_hw_failure_work, 0);
 	}
 
+	if (di->flags.vbus_drop_end)
+		queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
+
 	return 0;
 }
 
@@ -2872,6 +3073,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 	if (delayed_work_pending(&di->check_hw_failure_work))
 		cancel_delayed_work(&di->check_hw_failure_work);
 
+	if (delayed_work_pending(&di->vbus_drop_end_work))
+		cancel_delayed_work(&di->vbus_drop_end_work);
+
 	flush_delayed_work_sync(&di->attach_work);
 	flush_delayed_work_sync(&di->usb_charger_attached_work);
 	flush_delayed_work_sync(&di->ac_charger_attached_work);
@@ -2883,11 +3087,14 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 	flush_work_sync(&di->ac_work);
 	flush_work_sync(&di->detect_usb_type_work);
 
+	if (atomic_read(&di->current_stepping_sessions))
+		return -EAGAIN;
+
 	return 0;
 }
 #else
-#define ab8500_charger_suspend      NULL
-#define ab8500_charger_resume       NULL
+#define ab8500_charger_suspend	    NULL
+#define ab8500_charger_resume	    NULL
 #endif
 
 static int __devexit ab8500_charger_remove(struct platform_device *pdev)
@@ -2951,6 +3158,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 
 	/* initialize lock */
 	spin_lock_init(&di->usb_state.usb_lock);
+	mutex_init(&di->usb_ipt_crnt_lock);
 
 	/* get charger specific platform data */
 	plat_data = dev_get_platdata(di->parent->dev);
@@ -3046,12 +3254,18 @@ 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(&di->attach_work,
+	INIT_DELAYED_WORK_DEFERRABLE(&di->attach_work,
 		ab8500_charger_usb_link_attach_work);
 
 	INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work,
 		ab8500_charger_check_vbat_work);
 
+	INIT_DELAYED_WORK_DEFERRABLE(&di->usb_state_changed_work,
+		ab8500_charger_usb_state_changed_work);
+
+	INIT_DELAYED_WORK_DEFERRABLE(&di->vbus_drop_end_work,
+		ab8500_charger_vbus_drop_end_work);
+
 	/* Init work for charger detection */
 	INIT_WORK(&di->usb_link_status_work,
 		ab8500_charger_usb_link_status_work);
@@ -3059,9 +3273,6 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	INIT_WORK(&di->detect_usb_type_work,
 		ab8500_charger_detect_usb_type_work);
 
-	INIT_WORK(&di->usb_state_changed_work,
-		ab8500_charger_usb_state_changed_work);
-
 	/* Init work for checking HW status */
 	INIT_WORK(&di->check_main_thermal_prot_work,
 		ab8500_charger_check_main_thermal_prot_work);
@@ -3120,7 +3331,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	}
 
 	/* Identify the connected charger types during startup */
-	charger_status = ab8500_charger_detect_chargers(di);
+	charger_status = ab8500_charger_detect_chargers(di, true);
 	if (charger_status & AC_PW_CONN) {
 		di->ac.charger_connected = 1;
 		di->ac_conn = true;
@@ -3153,7 +3364,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, di);
 
-	ch_stat = ab8500_charger_detect_chargers(di);
+	ch_stat = ab8500_charger_detect_chargers(di, false);
 
 	if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
 		queue_delayed_work(di->charger_wq,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (32 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 33/57] power: u8500_charger: Delay for USB enumeration mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  0:01   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 35/57] power: ab8500_fg: Report unscaled capacity mathieu.poirier
                   ` (23 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rikard Olsson <rikard.p.olsson@stericsson.com>

Add support for a power cut feature which allows user to
configure when ab8505 should shut down system due to low
battery.

Signed-off-by: Rikard Olsson <rikard.p.olsson@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c            |  488 +++++++++++++++++++++++++++++++++-
 include/linux/mfd/abx500.h           |   10 +
 include/linux/mfd/abx500/ab8500-bm.h |    8 +
 3 files changed, 502 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 5e4a46b..fde189a 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2351,6 +2351,64 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
 		dev_err(di->dev, "BattOk init write failed.\n");
 		goto out;
 	}
+
+	if ((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+			abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) {
+		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+			AB8505_RTC_PCUT_MAX_TIME_REG,
+					 di->bat->fg_params->pcut_max_time);
+
+		if (ret) {
+			dev_err(di->dev,
+				"%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n",
+				 __func__);
+			goto out;
+		};
+
+		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+			AB8505_RTC_PCUT_FLAG_TIME_REG,
+					 di->bat->fg_params->pcut_flag_time);
+
+		if (ret) {
+			dev_err(di->dev,
+			"%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n",
+			 __func__);
+			goto out;
+		};
+
+		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+			AB8505_RTC_PCUT_RESTART_REG,
+			di->bat->fg_params->pcut_max_restart);
+
+		if (ret) {
+			dev_err(di->dev,
+			"%s write failed AB8505_RTC_PCUT_RESTART_REG\n",
+			 __func__);
+			goto out;
+		};
+
+		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+			AB8505_RTC_PCUT_DEBOUNCE_REG,
+					 di->bat->fg_params->pcut_debunce_time);
+
+		if (ret) {
+			dev_err(di->dev,
+			"%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n",
+			 __func__);
+			goto out;
+		};
+
+		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+			AB8505_RTC_PCUT_CTL_STATUS_REG,
+					 di->bat->fg_params->pcut_enable);
+
+		if (ret) {
+			dev_err(di->dev,
+			"%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n",
+			 __func__);
+			goto out;
+		};
+	}
 out:
 	return ret;
 }
@@ -2572,22 +2630,433 @@ static ssize_t ab8500_show_capacity(struct device *dev,
 	return scnprintf(buf, PAGE_SIZE, "%d\n", capacity);
 }
 
+static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	long unsigned reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (kstrtoul(buf, 10, &reg_value) != 0)
+		goto fail;
+
+	if (reg_value > 0x7F) {
+		dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
+		goto fail;
+	}
+
+	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
+
+	if (ret < 0)
+		dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
+
+fail:
+	return count;
+}
+
+static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_MAX_TIME_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+	return ret;
+
+}
+
+static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	long unsigned int reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (kstrtoul(buf, 10, &reg_value) != 0)
+		goto fail;
+
+	if (reg_value > 0x7F) {
+		dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
+		goto fail;
+	}
+
+	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
+
+	if (ret < 0)
+		dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
+
+fail:
+	return count;
+}
+
+static ssize_t ab8505_powercut_restart_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_RESTART_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_restart_write(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	unsigned long int reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (kstrtoul(buf, 10, &reg_value) != 0)
+		goto fail;
+
+	if (reg_value > 0xF) {
+		dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
+		goto fail;
+	}
+
+	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
+
+	if (ret < 0)
+		dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
+
+fail:
+	return count;
+
+}
+
+static ssize_t ab8505_powercut_timer_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_TIME_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_RESTART_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
+
+	if (ret < 0)
+		goto fail;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_write(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	unsigned long int reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (kstrtoul(buf, 10, &reg_value) != 0)
+		goto fail;
+
+	if (reg_value > 0x1) {
+		dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
+		goto fail;
+	}
+
+	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
+
+	if (ret < 0)
+		dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+
+fail:
+	return count;
+}
+
+static ssize_t ab8505_powercut_flag_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_CTL_STATUS_REG,  &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_debounce_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_DEBOUNCE_REG,  &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
+
+fail:
+	return ret;
+}
+
+static ssize_t ab8505_powercut_debounce_write(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	unsigned long int reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	if (kstrtoul(buf, 10, &reg_value) != 0)
+		goto fail;
+
+	if (reg_value > 0x7) {
+		dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
+		goto fail;
+	}
+
+	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
+
+	if (ret < 0)
+		dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
+
+fail:
+	return count;
+}
+
+static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	u8 reg_value;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
+
+	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+		AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+		goto fail;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
+
+fail:
+	return ret;
+}
+
 static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
 	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
 };
 
+static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
+	__ATTR(powercut_flagtime, (S_IRUGO | S_IWUGO),
+				ab8505_powercut_flagtime_read,
+				ab8505_powercut_flagtime_write),
+	__ATTR(powercut_maxtime, (S_IRUGO | S_IWUGO),
+				ab8505_powercut_maxtime_read,
+				ab8505_powercut_maxtime_write),
+	__ATTR(powercut_restart_max, (S_IRUGO | S_IWUGO),
+				ab8505_powercut_restart_read,
+				ab8505_powercut_restart_write),
+	__ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
+	__ATTR(powercut_restart_counter, S_IRUGO,
+				ab8505_powercut_restart_counter_read, NULL),
+	__ATTR(powercut_enable, (S_IRUGO | S_IWUGO), ab8505_powercut_read,
+							 ab8505_powercut_write),
+	__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
+	__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUGO),
+				ab8505_powercut_debounce_read,
+				ab8505_powercut_debounce_write),
+	__ATTR(powercut_enable_status, S_IRUGO,
+				ab8505_powercut_enable_status_read, NULL),
+};
+
 static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
 {
-	unsigned int i;
+	unsigned int i, j;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
 
 	for (i = 0; i < ARRAY_SIZE(ab8500_fg_sysfs_psy_attrs); i++)
 		if (device_create_file(dev, &ab8500_fg_sysfs_psy_attrs[i]))
-			goto sysfs_psy_create_attrs_failed;
+			goto sysfs_psy_create_attrs_failed_ab8500;
+
+	if ((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+			abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) {
+		for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
+			if (device_create_file(dev,
+					&ab8505_fg_sysfs_psy_attrs[j]))
+				goto sysfs_psy_create_attrs_failed_ab8505;
+	}
 
 	return 0;
 
-sysfs_psy_create_attrs_failed:
-	dev_err(dev, "Failed creating sysfs psy attrs.\n");
+sysfs_psy_create_attrs_failed_ab8505:
+	dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
+	while (j--)
+		device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
+
+sysfs_psy_create_attrs_failed_ab8500:
+	dev_err(dev, "Failed creating sysfs psy attrs for ab8500.\n");
 	while (i--)
 		device_remove_file(dev, &ab8500_fg_sysfs_psy_attrs[i]);
 
@@ -2597,9 +3066,20 @@ sysfs_psy_create_attrs_failed:
 static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
 {
 	unsigned int i;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct ab8500_fg *di;
+
+	di = to_ab8500_fg_device_info(psy);
 
 	for (i = 0; i < ARRAY_SIZE(ab8500_fg_sysfs_psy_attrs); i++)
 		(void)device_remove_file(dev, &ab8500_fg_sysfs_psy_attrs[i]);
+
+	if ((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+			abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) {
+		for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
+			(void)device_remove_file(dev,
+					&ab8505_fg_sysfs_psy_attrs[i]);
+	}
 }
 /* Exposure to the sysfs interface <<END>> */
 
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index eef1ddc..97e918f 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -204,6 +204,11 @@ struct abx500_fg;
  *				points.
  * @maint_thres			This is the threshold where we stop reporting
  *				battery full while in maintenance, in per cent
+ * @pcut_enable:		Enable power cut feature in ab8505
+ * @pcut_max_time:		Max time threshold
+ * @pcut_flag_time:		Flagtime threshold
+ * @pcut_max_restart:		Max number of restarts
+ * @pcut_debunce_time:		Sets battery debounce time
  */
 struct abx500_fg_parameters {
 	int recovery_sleep_timer;
@@ -221,6 +226,11 @@ struct abx500_fg_parameters {
 	int battok_raising_th_sel1;
 	int user_cap_limit;
 	int maint_thres;
+	bool pcut_enable;
+	u8 pcut_max_time;
+	u8 pcut_flag_time;
+	u8 pcut_max_restart;
+	u8 pcut_debunce_time;
 };
 
 /**
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 175197c..cd15ea3 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -235,6 +235,14 @@
 /* Battery type */
 #define BATTERY_UNKNOWN			00
 
+/* Registers for pcut feature in ab8505 and ab9540 */
+#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
+#define AB8505_RTC_PCUT_TIME_REG       0x13
+#define AB8505_RTC_PCUT_MAX_TIME_REG   0x14
+#define AB8505_RTC_PCUT_FLAG_TIME_REG  0x15
+#define AB8505_RTC_PCUT_RESTART_REG    0x16
+#define AB8505_RTC_PCUT_DEBOUNCE_REG   0x17
+
 /**
  * struct res_to_temp - defines one point in a temp to res curve. To
  * be used in battery packs that combines the identification resistor with a
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 35/57] power: ab8500_fg: Report unscaled capacity
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (33 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505 mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 36/57] power: add backup battery charge voltages mathieu.poirier
                   ` (22 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Martin Bergstrom <martin.bergstrom@stericsson.com>

Unscaled capacity should be reported for POWER_SUPPLY_PROP_CAPACITY.

Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_fg.c |    6 ++----
 1 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index fde189a..c5732e7 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2159,9 +2159,7 @@ static int ab8500_fg_get_property(struct power_supply *psy,
 			val->intval = di->bat_cap.prev_mah;
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
-		if (di->bat->capacity_scaling)
-			val->intval = di->bat_cap.cap_scale.scaled_cap;
-		else if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+		if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
 				di->flags.batt_id_received)
 			val->intval = 100;
 		else
@@ -2625,7 +2623,7 @@ static ssize_t ab8500_show_capacity(struct device *dev,
 	if (di->bat->capacity_scaling)
 		capacity = di->bat_cap.cap_scale.scaled_cap;
 	else
-		capacity = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
+		capacity = di->bat_cap.prev_percent;
 
 	return scnprintf(buf, PAGE_SIZE, "%d\n", capacity);
 }
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 36/57] power: add backup battery charge voltages.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (34 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 35/57] power: ab8500_fg: Report unscaled capacity mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour mathieu.poirier
                   ` (21 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Yang QU <yang.qu@stericsson.com>

Add 2.7v, 2.9v, 3.0v, 3.2v and 3.3v charging voltage
for backup battery. Before that only 2.5v, 2.6v, 2.8v,
3.1v are available.

Signed-off-by: Yang QU <yang.qu@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Maxime COQUELIN <maxime.coquelin@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Xiao Mei ZHANG <xiaomei.zhang@stericsson.com>
---
 drivers/power/ab8500_charger.c       |    9 +++++++--
 include/linux/mfd/abx500/ab8500-bm.h |   24 ++++++++++++++++++++----
 2 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index b3b8f77..8137ea5 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2834,6 +2834,7 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
 static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
 {
 	int ret = 0;
+	u8 bup_vch_range = 0, vbup33_vrtcn = 0;
 
 	/* Setup maximum charger current and voltage for ABB cut2.0 */
 	if (!is_ab8500_1p1_or_earlier(di->parent)) {
@@ -2935,11 +2936,15 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
 	}
 
 	/* Backup battery voltage and current */
+	if (di->bat->bkup_bat_v > BUP_VCH_SEL_3P1V)
+		bup_vch_range = BUP_VCH_RANGE;
+	if (di->bat->bkup_bat_v == BUP_VCH_SEL_3P3V)
+		vbup33_vrtcn = VBUP33_VRTCN;
+
 	ret = abx500_set_register_interruptible(di->dev,
 		AB8500_RTC,
 		AB8500_RTC_BACKUP_CHG_REG,
-		di->bat->bkup_bat_v |
-		di->bat->bkup_bat_i);
+		(di->bat->bkup_bat_v & 0x3) | di->bat->bkup_bat_i);
 	if (ret) {
 		dev_err(di->dev, "failed to setup backup battery charging\n");
 		goto out;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index cd15ea3..5ae8a6f 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -105,6 +105,7 @@
 #define AB8500_RTC_BACKUP_CHG_REG	0x0C
 #define AB8500_RTC_CC_CONF_REG		0x01
 #define AB8500_RTC_CTRL_REG		0x0B
+#define AB8500_RTC_CTRL1_REG		0x11
 
 /*
  * OTP register offsets
@@ -179,10 +180,25 @@
 #define BUP_ICH_SEL_300UA		0x08
 #define BUP_ICH_SEL_700UA		0x0C
 
-#define BUP_VCH_SEL_2P5V		0x00
-#define BUP_VCH_SEL_2P6V		0x01
-#define BUP_VCH_SEL_2P8V		0x02
-#define BUP_VCH_SEL_3P1V		0x03
+enum bup_vch_sel {
+	BUP_VCH_SEL_2P5V,
+	BUP_VCH_SEL_2P6V,
+	BUP_VCH_SEL_2P8V,
+	BUP_VCH_SEL_3P1V,
+	/*
+	 * Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v
+	 * are only available on ab8540. You can't choose these 5
+	 * voltage on ab8500/ab8505/ab9540.
+	 */
+	BUP_VCH_SEL_2P7V,
+	BUP_VCH_SEL_2P9V,
+	BUP_VCH_SEL_3P0V,
+	BUP_VCH_SEL_3P2V,
+	BUP_VCH_SEL_3P3V,
+};
+
+#define BUP_VCH_RANGE		0x02
+#define VBUP33_VRTCN		0x01
 
 /* Battery OVV constants */
 #define BATT_OVV_ENA			0x02
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (35 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 36/57] power: add backup battery charge voltages mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  0:13   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 38/57] power: l9540: Charge only mode fixes mathieu.poirier
                   ` (20 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Kalle Komierowski <karl.komierowski@stericsson.com>

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 <karl.komierowski@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
---
 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);
 };
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 38/57] power: l9540: Charge only mode fixes
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (36 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  0:27   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS mathieu.poirier
                   ` (19 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rupesh Kumar <rupesh.kumar@stericsson.com>

Fix for: charging not getting enabled in
charge only mode by external charger.

Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
---
 drivers/power/ab8500_charger.c            |   42 +++++++++++++++++++++++++++++
 drivers/power/abx500_chargalg.c           |   14 +++++++++
 include/linux/mfd/abx500/ux500_chargalg.h |    2 +
 3 files changed, 58 insertions(+), 0 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 70e7c5e..ebeb068 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -15,6 +15,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/notifier.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
@@ -94,6 +95,10 @@
 #define AB8500_SW_CONTROL_FALLBACK	0x03
 /* Wait for enumeration before charging in us */
 #define WAIT_ACA_RID_ENUMERATION	(5 * 1000)
+/*External charger control*/
+#define AB8500_SYS_CHARGER_CONTROL_REG		0x52
+#define EXTERNAL_CHARGER_DISABLE_REG_VAL	0x03
+#define EXTERNAL_CHARGER_ENABLE_REG_VAL		0x07
 
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
@@ -1672,6 +1677,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 	return ret;
 }
 
+static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
+				unsigned long event, void *data)
+{
+	int ret;
+	struct device *dev = data;
+	/*Toggle External charger control pin*/
+	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+				  AB8500_SYS_CHARGER_CONTROL_REG,
+				  EXTERNAL_CHARGER_DISABLE_REG_VAL);
+	if (ret < 0) {
+		dev_err(dev, "write reg failed %d\n", ret);
+		goto out;
+	}
+	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+				  AB8500_SYS_CHARGER_CONTROL_REG,
+				  EXTERNAL_CHARGER_ENABLE_REG_VAL);
+	if (ret < 0)
+		dev_err(dev, "Write reg failed %d\n", ret);
+
+out:
+	return ret;
+}
+
 /**
  * ab8500_charger_usb_check_enable() - enable usb charging
  * @charger:	pointer to the ux500_charger structure
@@ -3201,6 +3229,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 #define ab8500_charger_resume	    NULL
 #endif
 
+static struct notifier_block charger_nb = {
+	.notifier_call = ab8500_external_charger_prepare,
+};
+
 static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 {
 	struct ab8500_charger *di = platform_get_drvdata(pdev);
@@ -3233,6 +3265,11 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 	/* Delete the work queue */
 	destroy_workqueue(di->charger_wq);
 
+	/*Unregister external charger enable notifier*/
+	if (!di->ac_chg.enabled)
+		blocking_notifier_chain_unregister(
+			&charger_notifier_list, &charger_nb);
+
 	flush_scheduled_work();
 	if (di->usb_chg.enabled)
 		power_supply_unregister(&di->usb_chg.psy);
@@ -3307,6 +3344,11 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	di->ac_chg.enabled = di->pdata->ac_enabled;
 	di->ac_chg.external = false;
 
+	/*notifier for external charger enabling*/
+	if (!di->ac_chg.enabled)
+		blocking_notifier_chain_register(
+			&charger_notifier_list, &charger_nb);
+
 	/* USB supply */
 	/* power_supply base class */
 	di->usb_chg.psy.name = "ab8500_usb";
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 9568f63..3ca00dd 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -24,6 +24,7 @@
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ux500_chargalg.h>
 #include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/notifier.h>
 
 /* Watchdog kick interval */
 #define CHG_WD_INTERVAL			(6 * HZ)
@@ -255,6 +256,9 @@ static enum power_supply_property abx500_chargalg_props[] = {
 	POWER_SUPPLY_PROP_HEALTH,
 };
 
+/*External charger prepare notifier*/
+BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
+
 /**
  * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
  * @data:	pointer to the abx500_chargalg structure
@@ -508,6 +512,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
 	int vset, int iset)
 {
+	static int ab8500_chargalg_ex_ac_enable_toggle;
+
 	if (!di->ac_chg || !di->ac_chg->ops.enable)
 		return -ENXIO;
 
@@ -520,6 +526,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
 	di->chg_info.ac_iset = iset;
 	di->chg_info.ac_vset = vset;
 
+	/*enable external charger*/
+	if (enable && di->ac_chg->external &&
+		!ab8500_chargalg_ex_ac_enable_toggle) {
+		blocking_notifier_call_chain(&charger_notifier_list,
+					0, di->dev);
+		ab8500_chargalg_ex_ac_enable_toggle++;
+	}
+
 	return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
 }
 
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
index 110d12f..fa831f1 100644
--- a/include/linux/mfd/abx500/ux500_chargalg.h
+++ b/include/linux/mfd/abx500/ux500_chargalg.h
@@ -41,4 +41,6 @@ struct ux500_charger {
 	bool external;
 };
 
+extern struct blocking_notifier_head charger_notifier_list;
+
 #endif
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (37 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 38/57] power: l9540: Charge only mode fixes mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  0:52   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 40/57] power: ab8500: ADC for battery thermistor mathieu.poirier
                   ` (18 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Martin Sjoblom <martin.w.sjoblom@stericsson.com>

Do not set higher current in stepping functionality if VBUS is dropping.
After VBUS has dropped try to set current once again. If dropping again
then we have found the maximum capability of the charger.

Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Per FORLIN <per.forlin@stericsson.com>
---
 drivers/power/ab8500_charger.c |  166 ++++++++++++++++++++++++++++------------
 1 files changed, 117 insertions(+), 49 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index ebeb068..18931e4 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -55,6 +55,7 @@
 #define MAIN_CH_INPUT_CURR_SHIFT	4
 #define VBUS_IN_CURR_LIM_SHIFT		4
 #define AUTO_VBUS_IN_CURR_LIM_SHIFT	4
+#define VBUS_IN_CURR_LIM_RETRY_SET_TIME	30 /* seconds */
 
 #define LED_INDICATOR_PWM_ENA		0x01
 #define LED_INDICATOR_PWM_DIS		0x00
@@ -199,10 +200,15 @@ struct ab8500_charger_usb_state {
 	spinlock_t usb_lock;
 };
 
+struct ab8500_charger_max_usb_in_curr {
+	int usb_type_max;
+	int set_max;
+	int calculated_max;
+};
+
 /**
  * struct ab8500_charger - ab8500 Charger device information
  * @dev:		Pointer to the structure device
- * @max_usb_in_curr:	Max USB charger input current
  * @vbus_detected:	VBUS detected
  * @vbus_detected_start:
  *			VBUS detected during startup
@@ -227,6 +233,7 @@ struct ab8500_charger_usb_state {
  * @bat:		Pointer to the abx500_bm platform data
  * @flags:		Structure for information about events triggered
  * @usb_state:		Structure for usb stack information
+ * @max_usb_in_curr:	Max USB charger input current
  * @ac_chg:		AC charger power supply
  * @usb_chg:		USB charger power supply
  * @ac:			Structure that holds the AC charger properties
@@ -255,7 +262,6 @@ struct ab8500_charger_usb_state {
  */
 struct ab8500_charger {
 	struct device *dev;
-	int max_usb_in_curr;
 	bool vbus_detected;
 	bool vbus_detected_start;
 	bool ac_conn;
@@ -266,7 +272,6 @@ struct ab8500_charger {
 	bool usb_device_is_unrecognised;
 	bool autopower;
 	int invalid_charger_detect_state;
-	bool is_usb_host;
 	int is_aca_rid;
 	atomic_t current_stepping_sessions;
 	struct ab8500 *parent;
@@ -275,6 +280,7 @@ struct ab8500_charger {
 	struct abx500_bm_data *bat;
 	struct ab8500_charger_event_flags flags;
 	struct ab8500_charger_usb_state usb_state;
+	struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
 	struct ux500_charger ac_chg;
 	struct ux500_charger usb_chg;
 	struct ab8500_charger_info ac;
@@ -416,6 +422,10 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
 	if (connected != di->usb.charger_connected) {
 		dev_dbg(di->dev, "USB connected:%i\n", connected);
 		di->usb.charger_connected = connected;
+
+		if (!connected)
+			di->flags.vbus_drop_end = false;
+
 		sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
 
 		if (connected) {
@@ -665,23 +675,19 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 	case USB_STAT_STD_HOST_C_S:
 		dev_dbg(di->dev, "USB Type - Standard host is ");
 		dev_dbg(di->dev, "detected through USB driver\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		di->is_usb_host = true;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_HOST_CHG_HS_CHIRP:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		di->is_usb_host = true;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_HOST_CHG_HS:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		di->is_usb_host = true;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_ACA_RID_C_HS:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
-		di->is_usb_host = false;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_ACA_RID_A:
@@ -690,8 +696,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		 * can consume (900mA). Closest level is 500mA
 		 */
 		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		di->is_usb_host = false;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_ACA_RID_B:
@@ -699,38 +704,35 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		 * Dedicated charger level minus 120mA (20mA for ACA and
 		 * 100mA for potential accessory). Closest level is 1300mA
 		 */
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3;
 		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
-		di->is_usb_host = false;
+				di->max_usb_in_curr.usb_type_max);
 		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_HOST_CHG_NM:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-		di->is_usb_host = true;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_DEDICATED_CHG:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
-		di->is_usb_host = false;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_ACA_RID_C_HS_CHIRP:
 		case USB_STAT_ACA_RID_C_NM:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
-		di->is_usb_host = false;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
 		di->is_aca_rid = 1;
 		break;
 	case USB_STAT_NOT_CONFIGURED:
 		if (di->vbus_detected) {
 			di->usb_device_is_unrecognised = true;
 			dev_dbg(di->dev, "USB Type - Legacy charger.\n");
-			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+			di->max_usb_in_curr.usb_type_max =
+						USB_CH_IP_CUR_LVL_1P5;
 			break;
 		}
 	case USB_STAT_HM_IDGND:
 		dev_err(di->dev, "USB Type - Charging not allowed\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
 		ret = -ENXIO;
 		break;
 	case USB_STAT_RESERVED:
@@ -743,9 +745,11 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		}
 		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
 			dev_dbg(di->dev, "USB Type - Charging not allowed\n");
-			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+			di->max_usb_in_curr.usb_type_max =
+						USB_CH_IP_CUR_LVL_0P05;
 			dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
-					link_status, di->max_usb_in_curr);
+				link_status,
+				di->max_usb_in_curr.usb_type_max);
 			ret = -ENXIO;
 			break;
 		}
@@ -754,23 +758,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 	case USB_STAT_CARKIT_2:
 	case USB_STAT_ACA_DOCK_CHARGER:
 	case USB_STAT_CHARGER_LINE_1:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-				di->max_usb_in_curr);
+				di->max_usb_in_curr.usb_type_max);
 	case USB_STAT_NOT_VALID_LINK:
 		dev_err(di->dev, "USB Type invalid - try charging anyway\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		break;
 
 	default:
 		dev_err(di->dev, "USB Type - Unknown\n");
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
 		ret = -ENXIO;
 		break;
 	};
 
+	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
 	dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
-		link_status, di->max_usb_in_curr);
+		link_status, di->max_usb_in_curr.set_max);
 
 	return ret;
 }
@@ -1074,28 +1079,48 @@ static int ab8500_vbus_in_curr_to_regval(int curr)
  */
 static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
 {
+	int ret = 0;
 	switch (di->usb_state.usb_current) {
 	case 100:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
 		break;
 	case 200:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
 		break;
 	case 300:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
 		break;
 	case 400:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
 		break;
 	case 500:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
 		break;
 	default:
-		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
-		return -1;
+		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
+		ret = -EPERM;
 		break;
 	};
-	return 0;
+	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
+	return ret;
+}
+
+/**
+ * ab8500_charger_check_continue_stepping() - Check to allow stepping
+ * @di:		pointer to the ab8500_charger structure
+ * @reg:	select what charger register to check
+ *
+ * Check if current stepping should be allowed to continue.
+ * Checks if charger source has not collapsed. If it has, further stepping
+ * is not allowed.
+ */
+static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di,
+						   int reg)
+{
+	if (reg == AB8500_USBCH_IPT_CRNTLVL_REG)
+		return !di->flags.vbus_drop_end;
+	else
+		return true;
 }
 
 /**
@@ -1220,7 +1245,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 				usleep_range(step_udelay, step_udelay * 2);
 		}
 	} else {
-		for (i = prev_curr_index + 1; i <= curr_index; i++) {
+		bool allow = true;
+		for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
 			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
 				(u8) i << shift_value, reg);
 			ret = abx500_set_register_interruptible(di->dev,
@@ -1231,6 +1257,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 			}
 			if (i != curr_index)
 				usleep_range(step_udelay, step_udelay * 2);
+
+			allow = ab8500_charger_check_continue_stepping(di, reg);
 		}
 	}
 
@@ -1255,6 +1283,8 @@ 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);
+	if (di->max_usb_in_curr.set_max > 0)
+		min_value = min(di->max_usb_in_curr.set_max, min_value);
 
 	switch (min_value) {
 	case 100:
@@ -1609,7 +1639,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 
 		/* USBChInputCurr: current that can be drawn from the usb */
 		ret = ab8500_charger_set_vbus_in_curr(di,
-					di->max_usb_in_curr);
+					di->max_usb_in_curr.usb_type_max);
 		if (ret) {
 			dev_err(di->dev, "setting USBChInputCurr failed\n");
 			return ret;
@@ -1943,10 +1973,12 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work)
 		(di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
 		di->vbat > VBAT_TRESH_IP_CUR_RED))) {
 
-		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);
+		dev_dbg(di->dev, "Vbat did cross threshold, curr: %d,",
+					di->max_usb_in_curr.usb_type_max);
+		dev_dbg(di->dev, " new: %d, old: %d\n",
+						di->vbat, di->old_vbat);
+		ab8500_charger_set_vbus_in_curr(di,
+					di->max_usb_in_curr.usb_type_max);
 		power_supply_changed(&di->usb_chg.psy);
 	}
 
@@ -2224,7 +2256,8 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
 
 	/* Update maximum input current if USB enumeration is not detected */
 	if (!di->usb.charger_online) {
-		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+		ret = ab8500_charger_set_vbus_in_curr(di,
+					di->max_usb_in_curr.usb_type_max);
 		if (ret)
 			return;
 	}
@@ -2403,7 +2436,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
 		if (!ab8500_charger_get_usb_cur(di)) {
 			/* Update maximum input current */
 			ret = ab8500_charger_set_vbus_in_curr(di,
-					di->max_usb_in_curr);
+					di->max_usb_in_curr.usb_type_max);
 			if (ret)
 				return;
 
@@ -2617,15 +2650,45 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
 {
 	struct ab8500_charger *di = container_of(work,
 		struct ab8500_charger, vbus_drop_end_work.work);
+	int ret;
+	u8 reg_value;
 
 	di->flags.vbus_drop_end = false;
 
 	/* Reset the drop counter */
 	abx500_set_register_interruptible(di->dev,
 				AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
+	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+						AB8500_CH_USBCH_STAT2_REG,
+						&reg_value);
+	if (ret < 0) {
+		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+	} else {
+		int curr = ab8500_charger_vbus_in_curr_map[
+			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];
+		if (di->max_usb_in_curr.calculated_max != curr) {
+			/* USB source is collapsing */
+			di->max_usb_in_curr.calculated_max = curr;
+			dev_dbg(di->dev,
+			"VBUS input current limiting to %d mA\n",
+				di->max_usb_in_curr.calculated_max);
+		} else {
+			/*
+			 * USB source can not give more than this amount.
+			 * Taking more will collapse the source.
+			 */
+			di->max_usb_in_curr.set_max =
+				di->max_usb_in_curr.calculated_max;
+			dev_dbg(di->dev,
+				"VBUS input current limited to %d mA\n",
+				di->max_usb_in_curr.set_max);
+			return;
+		}
+	}
 
 	if (di->usb.charger_connected)
-		ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+		ab8500_charger_set_vbus_in_curr(di,
+			di->max_usb_in_curr.usb_type_max);
 }
 
 /**
@@ -2779,8 +2842,13 @@ static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
 
 	dev_dbg(di->dev, "VBUS charger drop ended\n");
 	di->flags.vbus_drop_end = true;
+
+	/*
+	 * VBUS might have dropped due to bad connection.
+	 * Schedule a new input limit set to the value SW requests.
+	 */
 	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
-						round_jiffies(30 * HZ));
+			round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
 
 	return IRQ_HANDLED;
 }
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 40/57] power: ab8500: ADC for battery thermistor
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (38 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  0:57   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 41/57] power: ab8500_btemp: Filter btemp readings mathieu.poirier
                   ` (17 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

When using ABx500_ADC_THERM_BATCTRL the battery ID resistor
is combined with a NTC resistor to both identify the battery and
to measure its temperature.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/power/ab8500_btemp.c |    4 +++-
 include/linux/mfd/abx500.h   |    2 ++
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 506f124..56a3bb9 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -557,7 +557,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 	/* BATTERY_UNKNOWN is defined on position 0, skip it! */
 	for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
 		if ((res <= di->bat->bat_type[i].resis_high) &&
-			(res >= di->bat->bat_type[i].resis_low)) {
+			(res >= di->bat->bat_type[i].resis_low) &&
+			(di->bat->bat_type[i].adc_therm ==
+							di->bat->adc_therm)) {
 			dev_dbg(di->dev, "Battery detected on %s"
 				" low %d < res %d < high: %d"
 				" index: %d\n",
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 97e918f..cb2b82a 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -270,6 +270,7 @@ struct abx500_maxim_parameters {
  * @low_high_cur_lvl:		charger current in temp low/high state in mA
  * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
  * @battery_resistance:		battery inner resistance in mOhm.
+ * @adc_therm:			battery uses controller or resistor for temp.
  * @n_r_t_tbl_elements:		number of elements in r_to_t_tbl
  * @r_to_t_tbl:			table containing resistance to temp points
  * @n_v_cap_tbl_elements:	number of elements in v_to_cap_tbl
@@ -297,6 +298,7 @@ struct abx500_battery_type {
 	int low_high_cur_lvl;
 	int low_high_vol_lvl;
 	int battery_resistance;
+	enum abx500_adc_therm adc_therm;
 	int n_temp_tbl_elements;
 	struct abx500_res_to_temp *r_to_t_tbl;
 	int n_v_cap_tbl_elements;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 41/57] power: ab8500_btemp: Filter btemp readings
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (39 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 40/57] power: ab8500: ADC for battery thermistor mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 42/57] power: charging: Allow capacity to raise from 1% mathieu.poirier
                   ` (16 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

Battery tempreature readings sometimes fails and results in
a value far from recent values. This patch adds a software
filter that disposes such readings, by allowing direct
updates on temperature only if two samples result in the
same temperature. Else only allow 1 degree change from previous
reported value in the direction of the new measurement.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/power/ab8500_btemp.c |   27 +++++++++++++++++++++------
 1 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 56a3bb9..b24835f 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -74,8 +74,8 @@ struct ab8500_btemp_ranges {
  * @dev:		Pointer to the structure device
  * @node:		List of AB8500 BTEMPs, hence prepared for reentrance
  * @curr_source:	What current source we use, in uA
- * @bat_temp:		Battery temperature in degree Celcius
- * @prev_bat_temp	Last dispatched battery temperature
+ * @bat_temp:		Dispatched battery temperature in degree Celcius
+ * @prev_bat_temp	Last measured battery temperature in degree Celcius
  * @parent:		Pointer to the struct ab8500
  * @gpadc:		Pointer to the struct gpadc
  * @fg:			Pointer to the struct fg
@@ -608,6 +608,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 static void ab8500_btemp_periodic_work(struct work_struct *work)
 {
 	int interval;
+	int bat_temp;
 	struct ab8500_btemp *di = container_of(work,
 		struct ab8500_btemp, btemp_periodic_work.work);
 
@@ -618,12 +619,26 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 			dev_warn(di->dev, "failed to identify the battery\n");
 	}
 
-	di->bat_temp = ab8500_btemp_measure_temp(di);
-
-	if (di->bat_temp != di->prev_bat_temp) {
-		di->prev_bat_temp = di->bat_temp;
+	bat_temp = ab8500_btemp_measure_temp(di);
+	/*
+	 * Filter battery temperature.
+	 * Allow direct updates on temperature only if two samples result in
+	 * same temperature. Else only allow 1 degree change from previous
+	 * reported value in the direction of the new measurement.
+	 */
+	if (bat_temp == di->prev_bat_temp || !di->initialized) {
+		if (di->bat_temp != di->prev_bat_temp || !di->initialized) {
+			di->bat_temp = bat_temp;
+			power_supply_changed(&di->btemp_psy);
+		}
+	} else if (bat_temp < di->prev_bat_temp) {
+		di->bat_temp--;
+		power_supply_changed(&di->btemp_psy);
+	} else if (bat_temp > di->prev_bat_temp) {
+		di->bat_temp++;
 		power_supply_changed(&di->btemp_psy);
 	}
+	di->prev_bat_temp = bat_temp;
 
 	if (di->events.ac_conn || di->events.usb_conn)
 		interval = di->bat->temp_interval_chg;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 42/57] power: charging: Allow capacity to raise from 1%
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (40 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 41/57] power: ab8500_btemp: Filter btemp readings mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 43/57] power: charging: Add AB8505_USB_LINK_STATUS mathieu.poirier
                   ` (15 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

When battery capacity was going below 1% fg in not supposed
to report 0% unless we've got the LOW_BAT IRQ, no matter
what the FG-algorithm says. This made fg get stuck at 1% if
charger is connected when capacity is 1%.
That problem is addressed with this patch.

Signed-off-by: Hakan BERG <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
---
 drivers/power/ab8500_fg.c |    6 ++----
 1 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index c5732e7..cf6d2b5 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -1357,9 +1357,6 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
 			 * algorithm says.
 			 */
 			di->bat_cap.prev_percent = 1;
-			di->bat_cap.permille = 1;
-			di->bat_cap.prev_mah = 1;
-			di->bat_cap.mah = 1;
 			percent = 1;
 
 			changed = true;
@@ -1771,9 +1768,10 @@ static void ab8500_fg_algorithm(struct ab8500_fg *di)
 			ab8500_fg_algorithm_discharging(di);
 	}
 
-	dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+	dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d "
 		"%d %d %d %d %d %d %d\n",
 		di->bat_cap.max_mah_design,
+		di->bat_cap.max_mah,
 		di->bat_cap.mah,
 		di->bat_cap.permille,
 		di->bat_cap.level,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 43/57] power: charging: Add AB8505_USB_LINK_STATUS
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (41 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 42/57] power: charging: Allow capacity to raise from 1% mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-25 16:12 ` [PATCH 44/57] power: ab8500: remove unecesary define flag mathieu.poirier
                   ` (14 subsequent siblings)
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

The ab8505 does not have the same address for USB link-status
as has ab8500. Add AB8505_USB_LINK_STATUS and code to switch
to correct constant.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/power/ab8500_charger.c |   46 ++++++++++++++++++++++++++++++++-------
 1 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 18931e4..9449a33 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -77,6 +77,7 @@
 
 /* UsbLineStatus register bit masks */
 #define AB8500_USB_LINK_STATUS		0x78
+#define AB8505_USB_LINK_STATUS		0xF8
 #define AB8500_STD_HOST_SUSP		0x18
 
 /* Watchdog timeout constant */
@@ -801,10 +802,12 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
 	if (is_ab8500(di->parent)) {
 		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
 				AB8500_USB_LINE_STAT_REG, &val);
-	} else {
-		if (is_ab9540(di->parent) || is_ab8505(di->parent))
+	} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
 			ret = abx500_get_register_interruptible(di->dev,
 				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
+	} else {
+		dev_err(di->dev, "%s unsupported analog baseband\n", __func__);
+		return -ENXIO;
 	}
 	if (ret < 0) {
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
@@ -812,7 +815,14 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
 	}
 
 	/* get the USB type */
-	val = (val & AB8500_USB_LINK_STATUS) >> 3;
+	if (is_ab8500(di->parent)) {
+		val = (val & AB8500_USB_LINK_STATUS) >> 3;
+	} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+		val = (val & AB8505_USB_LINK_STATUS) >> 3;
+	} else {
+		dev_err(di->dev, "%s unsupported analog baseband\n", __func__);
+		return -ENXIO;
+	}
 	ret = ab8500_charger_max_usb_curr(di,
 		(enum ab8500_charger_link_status) val);
 
@@ -848,12 +858,17 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 			return ret;
 		}
 
-		if (is_ab8500(di->parent))
+		if (is_ab8500(di->parent)) {
 			ret = abx500_get_register_interruptible(di->dev,
 				AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
-		else
+		} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
 			ret = abx500_get_register_interruptible(di->dev,
 				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
+		} else {
+			dev_err(di->dev,
+				 "%s unsupported analog baseband\n", __func__);
+			return -ENXIO;
+		}
 		if (ret < 0) {
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
@@ -867,7 +882,15 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 		 */
 
 		/* get the USB type */
-		val = (val & AB8500_USB_LINK_STATUS) >> 3;
+		if (is_ab8500(di->parent)) {
+			val = (val & AB8500_USB_LINK_STATUS) >> 3;
+		} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			val = (val & AB8505_USB_LINK_STATUS) >> 3;
+		} else {
+			dev_err(di->dev,
+				 "%s unsupported analog baseband\n", __func__);
+			return -ENXIO;
+		}
 		if (val)
 			break;
 	}
@@ -2277,6 +2300,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	int detected_chargers;
 	int ret;
 	u8 val;
+	u8 link_status;
 
 	struct ab8500_charger *di = container_of(work,
 		struct ab8500_charger, usb_link_status_work);
@@ -2303,9 +2327,13 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	else
 		dev_dbg(di->dev, "Error reading USB link status\n");
 
+	if (is_ab9540(di->parent) || is_ab8505(di->parent))
+		link_status = AB8505_USB_LINK_STATUS;
+	else
+		link_status = AB8500_USB_LINK_STATUS;
+
 	if (detected_chargers & USB_PW_CONN) {
-		if (((val & AB8500_USB_LINK_STATUS) >> 3) ==
-						USB_STAT_NOT_VALID_LINK &&
+		if (((val & link_status) >> 3) == USB_STAT_NOT_VALID_LINK &&
 				di->invalid_charger_detect_state == 0) {
 			dev_dbg(di->dev,
 				 "Invalid charger detected, state= 0\n");
@@ -2338,7 +2366,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 						AB8500_USB_LINE_STAT_REG,
 						&val);
 			dev_dbg(di->dev, "USB link status= 0x%02x\n",
-					(val & AB8500_USB_LINK_STATUS) >> 3);
+					(val & link_status) >> 3);
 			di->invalid_charger_detect_state = 2;
 		}
 	} else {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 44/57] power: ab8500: remove unecesary define flag
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (42 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 43/57] power: charging: Add AB8505_USB_LINK_STATUS mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  1:05   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 45/57] power: ab8500: defer btemp filtering while init mathieu.poirier
                   ` (13 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

Remove flag that serve no purpose from source code, Kconfig
and Makefile.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
---
 drivers/power/Kconfig          |    7 -------
 drivers/power/ab8500_charger.c |    2 +-
 2 files changed, 1 insertions(+), 8 deletions(-)

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c1892f3..f7c13ae 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -303,13 +303,6 @@ config AB8500_BM
 	depends on AB8500_CORE && AB8500_GPADC
 	help
 	  Say Y to include support for AB8500 battery management.
-
-config AB8500_BATTERY_THERM_ON_BATCTRL
-	bool "Thermistor connected on BATCTRL ADC"
-	depends on AB8500_BM
-	help
-	  Say Y to enable battery temperature measurements using
-	  thermistor connected on BATCTRL ADC.
 endif # POWER_SUPPLY
 
 source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 9449a33..d90fe9f 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -3369,7 +3369,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 	flush_scheduled_work();
 	if (di->usb_chg.enabled)
 		power_supply_unregister(&di->usb_chg.psy);
-	if (di->ac_chg.enabled)
+	if (di->ac_chg.enabled && !di->ac_chg.external)
 		power_supply_unregister(&di->ac_chg.psy);
 
 	platform_set_drvdata(pdev, NULL);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 45/57] power: ab8500: defer btemp filtering while init
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (43 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 44/57] power: ab8500: remove unecesary define flag mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  1:08   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 46/57] power: chargealg: Realign with upstream version mathieu.poirier
                   ` (12 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rupesh Kumar <rupesh.kumar@stericsson.com>

Due to btemp filtering enabled during init, temp values
reported to charge algorithm driver started from 0.
As a result,charge algorithm was going into wrong
state and charging was stopped.
This patch defers btemp filtering till init is done.

Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
---
 drivers/power/ab8500_btemp.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index b24835f..1f33122 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -613,7 +613,6 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 		struct ab8500_btemp, btemp_periodic_work.work);
 
 	if (!di->initialized) {
-		di->initialized = true;
 		/* Identify the battery */
 		if (ab8500_btemp_id(di) < 0)
 			dev_warn(di->dev, "failed to identify the battery\n");
@@ -626,8 +625,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 	 * same temperature. Else only allow 1 degree change from previous
 	 * reported value in the direction of the new measurement.
 	 */
-	if (bat_temp == di->prev_bat_temp || !di->initialized) {
-		if (di->bat_temp != di->prev_bat_temp || !di->initialized) {
+	if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
+		if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
+			di->initialized = true;
 			di->bat_temp = bat_temp;
 			power_supply_changed(&di->btemp_psy);
 		}
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 46/57] power: chargealg: Realign with upstream version
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (44 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 45/57] power: ab8500: defer btemp filtering while init mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  1:17   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 47/57] power: Harmonising platform data declaration/handling mathieu.poirier
                   ` (11 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Loic Pallardy <loic.pallardy@stericsson.com>

Upstream version of AB charge algo has been reverted
during kernel 3.4 port.
This patch restore state by:
- renaming ab8500_chargal.c in abx500_chargal.c
- renaming function from ab8500 to abx500
- moving generic structure in "include/mfd/abx500.h"

Goal is to ease next code reversion and realignment
with mainline

Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
---
 drivers/power/ab8500_charger.c       |  166 +++++++++++-----------------------
 include/linux/mfd/abx500.h           |   10 ++-
 include/linux/mfd/abx500/ab8500-bm.h |    5 +-
 3 files changed, 65 insertions(+), 116 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index d90fe9f..68a4128 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -79,6 +79,7 @@
 #define AB8500_USB_LINK_STATUS		0x78
 #define AB8505_USB_LINK_STATUS		0xF8
 #define AB8500_STD_HOST_SUSP		0x18
+#define USB_LINK_STATUS_SHIFT		3
 
 /* Watchdog timeout constant */
 #define WD_TIMER			0x30 /* 4min */
@@ -743,8 +744,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 			dev_err(di->dev, "VBUS has collapsed\n");
 			ret = -ENXIO;
 			break;
-		}
-		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+		} else {
 			dev_dbg(di->dev, "USB Type - Charging not allowed\n");
 			di->max_usb_in_curr.usb_type_max =
 						USB_CH_IP_CUR_LVL_0P05;
@@ -799,30 +799,22 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 		return ret;
 	}
-	if (is_ab8500(di->parent)) {
+	if (is_ab8500(di->parent))
 		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-				AB8500_USB_LINE_STAT_REG, &val);
-	} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-			ret = abx500_get_register_interruptible(di->dev,
-				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
-	} else {
-		dev_err(di->dev, "%s unsupported analog baseband\n", __func__);
-		return -ENXIO;
-	}
+			AB8500_USB_LINE_STAT_REG, &val);
+	else
+		ret = abx500_get_register_interruptible(di->dev,
+			AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
 	if (ret < 0) {
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 		return ret;
 	}
 
 	/* get the USB type */
-	if (is_ab8500(di->parent)) {
-		val = (val & AB8500_USB_LINK_STATUS) >> 3;
-	} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-		val = (val & AB8505_USB_LINK_STATUS) >> 3;
-	} else {
-		dev_err(di->dev, "%s unsupported analog baseband\n", __func__);
-		return -ENXIO;
-	}
+	if (is_ab8500(di->parent))
+		val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
+	else
+		val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
 	ret = ab8500_charger_max_usb_curr(di,
 		(enum ab8500_charger_link_status) val);
 
@@ -858,17 +850,12 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 			return ret;
 		}
 
-		if (is_ab8500(di->parent)) {
+		if (is_ab8500(di->parent))
 			ret = abx500_get_register_interruptible(di->dev,
 				AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
-		} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+		else
 			ret = abx500_get_register_interruptible(di->dev,
 				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
-		} else {
-			dev_err(di->dev,
-				 "%s unsupported analog baseband\n", __func__);
-			return -ENXIO;
-		}
 		if (ret < 0) {
 			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 			return ret;
@@ -882,15 +869,12 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
 		 */
 
 		/* get the USB type */
-		if (is_ab8500(di->parent)) {
-			val = (val & AB8500_USB_LINK_STATUS) >> 3;
-		} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-			val = (val & AB8505_USB_LINK_STATUS) >> 3;
-		} else {
-			dev_err(di->dev,
-				 "%s unsupported analog baseband\n", __func__);
-			return -ENXIO;
-		}
+		if (is_ab8500(di->parent))
+			val = (val & AB8500_USB_LINK_STATUS) >>
+						USB_LINK_STATUS_SHIFT;
+		else
+			val = (val & AB8505_USB_LINK_STATUS) >>
+						USB_LINK_STATUS_SHIFT;
 		if (val)
 			break;
 	}
@@ -985,51 +969,6 @@ static int ab8500_charger_voltage_map[] = {
 	4600 ,
 };
 
-/*
- * This array maps the raw hex value to charger current used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_charger_current_map[] = {
-	100 ,
-	200 ,
-	300 ,
-	400 ,
-	500 ,
-	600 ,
-	700 ,
-	800 ,
-	900 ,
-	1000 ,
-	1100 ,
-	1200 ,
-	1300 ,
-	1400 ,
-	1500 ,
-};
-
-/*
- * This array maps the raw hex value to VBUS input current used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_charger_vbus_in_curr_map[] = {
-	USB_CH_IP_CUR_LVL_0P05,
-	USB_CH_IP_CUR_LVL_0P09,
-	USB_CH_IP_CUR_LVL_0P19,
-	USB_CH_IP_CUR_LVL_0P29,
-	USB_CH_IP_CUR_LVL_0P38,
-	USB_CH_IP_CUR_LVL_0P45,
-	USB_CH_IP_CUR_LVL_0P5,
-	USB_CH_IP_CUR_LVL_0P6,
-	USB_CH_IP_CUR_LVL_0P7,
-	USB_CH_IP_CUR_LVL_0P8,
-	USB_CH_IP_CUR_LVL_0P9,
-	USB_CH_IP_CUR_LVL_1P0,
-	USB_CH_IP_CUR_LVL_1P1,
-	USB_CH_IP_CUR_LVL_1P3,
-	USB_CH_IP_CUR_LVL_1P4,
-	USB_CH_IP_CUR_LVL_1P5,
-};
-
 static int ab8500_voltage_to_regval(int voltage)
 {
 	int i;
@@ -1051,41 +990,41 @@ static int ab8500_voltage_to_regval(int voltage)
 		return -1;
 }
 
-static int ab8500_current_to_regval(int curr)
+static int ab8500_current_to_regval(struct ab8500_charger *di, int curr)
 {
 	int i;
 
-	if (curr < ab8500_charger_current_map[0])
+	if (curr < di->bat->chg_output_curr[0])
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) {
-		if (curr < ab8500_charger_current_map[i])
+	for (i = 0; i < di->bat->n_chg_out_curr; i++) {
+		if (curr < di->bat->chg_output_curr[i])
 			return i - 1;
 	}
 
 	/* If not last element, return error */
-	i = ARRAY_SIZE(ab8500_charger_current_map) - 1;
-	if (curr == ab8500_charger_current_map[i])
+	i = di->bat->n_chg_out_curr - 1;
+	if (curr == di->bat->chg_output_curr[i])
 		return i;
 	else
 		return -1;
 }
 
-static int ab8500_vbus_in_curr_to_regval(int curr)
+static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr)
 {
 	int i;
 
-	if (curr < ab8500_charger_vbus_in_curr_map[0])
+	if (curr < di->bat->chg_input_curr[0])
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) {
-		if (curr < ab8500_charger_vbus_in_curr_map[i])
+	for (i = 0; i < di->bat->n_chg_in_curr; i++) {
+		if (curr < di->bat->chg_input_curr[i])
 			return i - 1;
 	}
 
 	/* If not last element, return error */
-	i = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1;
-	if (curr == ab8500_charger_vbus_in_curr_map[i])
+	i = di->bat->n_chg_in_curr - 1;
+	if (curr == di->bat->chg_input_curr[i])
 		return i;
 	else
 		return -1;
@@ -1181,7 +1120,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 	case AB8500_MCH_IPT_CURLVL_REG:
 		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
 		prev_curr_index = (reg_value >> shift_value);
-		curr_index = ab8500_current_to_regval(ich);
+		curr_index = ab8500_current_to_regval(di, ich);
 		step_udelay = STEP_UDELAY;
 		if (!di->ac.charger_connected)
 			no_stepping = true;
@@ -1189,7 +1128,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 	case AB8500_USBCH_IPT_CRNTLVL_REG:
 		shift_value = VBUS_IN_CURR_LIM_SHIFT;
 		prev_curr_index = (reg_value >> shift_value);
-		curr_index = ab8500_vbus_in_curr_to_regval(ich);
+		curr_index = ab8500_vbus_in_curr_to_regval(di, ich);
 		step_udelay = STEP_UDELAY * 100;
 
 		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
@@ -1203,8 +1142,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
 
 		dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
-			__func__,
-			ab8500_charger_vbus_in_curr_map[auto_curr_index]);
+			__func__, di->bat->chg_input_curr[auto_curr_index]);
 
 		prev_curr_index = min(prev_curr_index, auto_curr_index);
 
@@ -1214,7 +1152,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
 	case AB8500_CH_OPT_CRNTLVL_REG:
 		shift_value = 0;
 		prev_curr_index = (reg_value >> shift_value);
-		curr_index = ab8500_current_to_regval(ich);
+		curr_index = ab8500_current_to_regval(di, ich);
 		if (curr_index == 0)
 			step_udelay = STEP_UDELAY;
 		else if ((curr_index - prev_curr_index) > 1)
@@ -1453,8 +1391,8 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
 
 		/* Check if the requested voltage or current is valid */
 		volt_index = ab8500_voltage_to_regval(vset);
-		curr_index = ab8500_current_to_regval(iset);
-		input_curr_index = ab8500_current_to_regval(
+		curr_index = ab8500_current_to_regval(di, iset);
+		input_curr_index = ab8500_current_to_regval(di,
 			di->bat->chg_params->ac_curr_max);
 		if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
 			dev_err(di->dev,
@@ -1624,7 +1562,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 
 		/* Check if the requested voltage or current is valid */
 		volt_index = ab8500_voltage_to_regval(vset);
-		curr_index = ab8500_current_to_regval(ich_out);
+		curr_index = ab8500_current_to_regval(di, ich_out);
 		if (volt_index < 0 || curr_index < 0) {
 			dev_err(di->dev,
 				"Charger voltage or current too high, "
@@ -2327,21 +2265,21 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	else
 		dev_dbg(di->dev, "Error reading USB link status\n");
 
-	if (is_ab9540(di->parent) || is_ab8505(di->parent))
-		link_status = AB8505_USB_LINK_STATUS;
-	else
+	if (is_ab8500(di->parent))
 		link_status = AB8500_USB_LINK_STATUS;
+	else
+		link_status = AB8505_USB_LINK_STATUS;
 
 	if (detected_chargers & USB_PW_CONN) {
-		if (((val & link_status) >> 3) == USB_STAT_NOT_VALID_LINK &&
+		if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
+				USB_STAT_NOT_VALID_LINK &&
 				di->invalid_charger_detect_state == 0) {
 			dev_dbg(di->dev,
 				 "Invalid charger detected, state= 0\n");
 			/*Enable charger*/
 			abx500_mask_and_set_register_interruptible(di->dev,
-						AB8500_CHARGER,
-						AB8500_USBCH_CTRL1_REG,
-						0x01, 0x01);
+					AB8500_CHARGER, AB8500_USBCH_CTRL1_REG,
+					USB_CH_ENA, USB_CH_ENA);
 			/*Enable charger detection*/
 			abx500_mask_and_set_register_interruptible(di->dev,
 						AB8500_USB,
@@ -2366,7 +2304,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 						AB8500_USB_LINE_STAT_REG,
 						&val);
 			dev_dbg(di->dev, "USB link status= 0x%02x\n",
-					(val & link_status) >> 3);
+				(val & link_status) >> USB_LINK_STATUS_SHIFT);
 			di->invalid_charger_detect_state = 2;
 		}
 	} else {
@@ -2678,7 +2616,7 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
 {
 	struct ab8500_charger *di = container_of(work,
 		struct ab8500_charger, vbus_drop_end_work.work);
-	int ret;
+	int ret, curr;
 	u8 reg_value;
 
 	di->flags.vbus_drop_end = false;
@@ -2692,7 +2630,7 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
 	if (ret < 0) {
 		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
 	} else {
-		int curr = ab8500_charger_vbus_in_curr_map[
+		curr = di->bat->chg_input_curr[
 			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];
 		if (di->max_usb_in_curr.calculated_max != curr) {
 			/* USB source is collapsing */
@@ -3434,8 +3372,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
 	di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
-	di->ac_chg.max_out_curr = ab8500_charger_current_map[
-		ARRAY_SIZE(ab8500_charger_current_map) - 1];
+	di->ac_chg.max_out_curr =
+		di->bat->chg_output_curr[di->bat->n_chg_out_curr - 1];
 	di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
 	di->ac_chg.enabled = di->pdata->ac_enabled;
 	di->ac_chg.external = false;
@@ -3461,8 +3399,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
 	di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
 		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
-	di->usb_chg.max_out_curr = ab8500_charger_current_map[
-		ARRAY_SIZE(ab8500_charger_current_map) - 1];
+	di->usb_chg.max_out_curr =
+		di->bat->chg_output_curr[di->bat->n_chg_out_curr - 1];
 	di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
 	di->usb_chg.enabled = di->pdata->usb_enabled;
 	di->usb_chg.external = false;
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index cb2b82a..a3bb93e 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -363,7 +363,11 @@ struct abx500_bm_charger_parameters {
  * @interval_not_charging charge alg cycle period time when not charging (sec)
  * @temp_hysteresis	temperature hysteresis
  * @gnd_lift_resistance	Battery ground to phone ground resistance (mOhm)
- * @maxi:		maximization parameters
+ * @n_chg_out_curr	number of elements in array chg_output_curr
+ * @n_chg_in_curr	number of elements in array chg_input_curr
+ * @chg_output_curr	charger output current level map
+ * @chg_input_curr	charger input current level map
+ * @maxi		maximization parameters
  * @cap_levels		capacity in percent for the different capacity levels
  * @bat_type		table of supported battery types
  * @chg_params		charger parameters
@@ -394,6 +398,10 @@ struct abx500_bm_data {
 	int interval_not_charging;
 	int temp_hysteresis;
 	int gnd_lift_resistance;
+	int n_chg_out_curr;
+	int n_chg_in_curr;
+	int *chg_output_curr;
+	int *chg_input_curr;
 	const struct abx500_maxim_parameters *maxi;
 	const struct abx500_bm_capacity_levels *cap_levels;
 	const struct abx500_battery_type *bat_type;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 5ae8a6f..721bd6d 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -33,7 +33,7 @@
 #define AB8500_CH_STATUS2_REG		0x01
 #define AB8500_CH_USBCH_STAT1_REG	0x02
 #define AB8500_CH_USBCH_STAT2_REG	0x03
-#define AB8500_CH_FSM_STAT_REG		0x04
+#define AB8540_CH_USBCH_STAT3_REG	0x04
 #define AB8500_CH_STAT_REG		0x05
 
 /*
@@ -155,6 +155,7 @@
 #define CH_OP_CUR_LVL_1P4		0x0D
 #define CH_OP_CUR_LVL_1P5		0x0E
 #define CH_OP_CUR_LVL_1P6		0x0F
+#define CH_OP_CUR_LVL_2P		0x3F
 
 /* BTEMP High thermal limits */
 #define BTEMP_HIGH_TH_57_0		0x00
@@ -244,6 +245,8 @@ enum bup_vch_sel {
 #define BAT_CTRL_20U_ENA		0x02
 #define BAT_CTRL_18U_ENA		0x01
 #define BAT_CTRL_16U_ENA		0x02
+#define BAT_CTRL_60U_ENA		0x01
+#define BAT_CTRL_120U_ENA		0x02
 #define BAT_CTRL_CMP_ENA		0x04
 #define FORCE_BAT_CTRL_CMP_HIGH		0x08
 #define BAT_CTRL_PULL_UP_ENA		0x10
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 47/57] power: Harmonising platform data declaration/handling
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (45 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 46/57] power: chargealg: Realign with upstream version mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  1:11   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 48/57] power: ab8500 : quick re-attach for ext charger mathieu.poirier
                   ` (10 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

Making platform data declaration and handling similar accross all
ab8500_xyc.c battery management files.  Also adding gards against
NULL platform data.

Signed-off-by: Philippe Langlais <philippe.langlais@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/ab8500_btemp.c    |   17 +++++++++--------
 drivers/power/ab8500_charger.c  |   18 +++++++++---------
 drivers/power/ab8500_fg.c       |   17 +++++++++--------
 drivers/power/abx500_chargalg.c |   25 ++++++++++++++++++++-----
 4 files changed, 47 insertions(+), 30 deletions(-)

diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 1f33122..cf4b653 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -1028,10 +1028,10 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 {
 	int irq, i, ret = 0;
 	u8 val;
-	struct ab8500_platform_data *plat_data;
-	struct ab8500_btemp *di;
+	struct ab8500_platform_data *plat;
 
-	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	struct ab8500_btemp *di =
+		kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
 
@@ -1042,22 +1042,23 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 
 	di->initialized = false;
 
+	plat = dev_get_platdata(di->parent->dev);
+
 	/* get btemp specific platform data */
-	plat_data = dev_get_platdata(di->parent->dev);
-	di->pdata = plat_data->btemp;
-	if (!di->pdata) {
+	if (!plat || !plat->btemp) {
 		dev_err(di->dev, "no btemp platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
+	di->pdata = plat->btemp;
 
 	/* get battery specific platform data */
-	di->bat = plat_data->battery;
-	if (!di->bat) {
+	if (!plat->battery) {
 		dev_err(di->dev, "no battery platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
+	di->bat = plat->battery;
 
 	/* BTEMP supply */
 	di->btemp_psy.name = "ab8500_btemp";
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 68a4128..1290470 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -3319,10 +3319,10 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 {
 	int irq, i, charger_status, ret = 0, ch_stat;
-	struct ab8500_platform_data *plat_data;
-	struct ab8500_charger *di;
+	struct ab8500_platform_data *plat;
 
-	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	struct ab8500_charger *di =
+		kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
 
@@ -3335,24 +3335,24 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	spin_lock_init(&di->usb_state.usb_lock);
 	mutex_init(&di->usb_ipt_crnt_lock);
 
-	/* get charger specific platform data */
-	plat_data = dev_get_platdata(di->parent->dev);
+	plat = dev_get_platdata(di->parent->dev);
 
-	di->pdata = plat_data->charger;
-	if (!di->pdata) {
+	/* get charger specific platform data */
+	if (!plat || !plat->charger) {
 		dev_err(di->dev, "no charger platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
+	di->pdata = plat->charger;
 
 	/* get battery specific platform data */
-	di->bat = plat_data->battery;
-	if (!di->bat) {
+	if (!plat->battery) {
 		dev_err(di->dev, "no battery platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
 
+	di->bat = plat->battery;
 	di->autopower = false;
 	di->invalid_charger_detect_state = 0;
 
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index cf6d2b5..5b5c7ea 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -3158,10 +3158,10 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 {
 	int i, irq;
 	int ret = 0;
-	struct ab8500_platform_data *plat_data;
-	struct ab8500_fg *di;
+	struct ab8500_platform_data *plat;
 
-	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	struct ab8500_fg *di =
+		kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
 	if (!di)
 		return -ENOMEM;
 
@@ -3172,22 +3172,23 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 	di->parent = dev_get_drvdata(pdev->dev.parent);
 	di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
+	plat = dev_get_platdata(di->parent->dev);
+
 	/* get fg specific platform data */
-	plat_data = dev_get_platdata(di->parent->dev);
-	di->pdata = plat_data->fg;
-	if (!di->pdata) {
+	if (!plat || !plat->fg) {
 		dev_err(di->dev, "no fg platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
+	di->pdata = plat->fg;
 
 	/* get battery specific platform data */
-	di->bat = plat_data->battery;
-	if (!di->bat) {
+	if (!plat->battery) {
 		dev_err(di->dev, "no battery platform data supplied\n");
 		ret = -EINVAL;
 		goto free_device_info;
 	}
+	di->bat = plat->battery;
 
 	di->fg_psy.name = "ab8500_fg";
 	di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 3ca00dd..180deab 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1918,7 +1918,7 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
 
 static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 {
-	struct ab8500_platform_data *plat_data;
+	struct ab8500_platform_data *plat;
 	int ret = 0;
 
 	struct abx500_chargalg *di =
@@ -1926,12 +1926,27 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 	if (!di)
 		return -ENOMEM;
 
-	/* get device struct */
+	/* get parent data */
 	di->dev = &pdev->dev;
 	di->parent = dev_get_drvdata(pdev->dev.parent);
-	plat_data = dev_get_platdata(di->parent->dev);
-	di->pdata = plat_data->chargalg;
-	di->bat = plat_data->battery;
+
+	plat = dev_get_platdata(di->parent->dev);
+
+	/* get chargalg specific platform data */
+	if (!plat || !plat->chargalg) {
+		dev_err(di->dev, "no chargalg platform data supplied\n");
+		ret = -EINVAL;
+		goto free_device_info;
+	}
+	di->pdata = plat->chargalg;
+
+	/* get battery specific platform data */
+	if (!plat->battery) {
+		dev_err(di->dev, "no battery platform data supplied\n");
+		ret = -EINVAL;
+		goto free_device_info;
+	}
+	di->bat = plat->battery;
 
 	/* chargalg supply */
 	di->chargalg_psy.name = "abx500_chargalg";
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 48/57] power: ab8500 : quick re-attach for ext charger
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (46 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 47/57] power: Harmonising platform data declaration/handling mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  1:19   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 49/57] power: Cancelling status charging notification mathieu.poirier
                   ` (9 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Rupesh Kumar <rupesh.kumar@stericsson.com>

Quick re-attach charging behaviour is not required
for external ac charger. Internal AC/USB Charger removal
detection problem is due to a bug in AB8500 ASICs.

Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
---
 drivers/power/abx500_chargalg.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 180deab..ce58f20 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -330,12 +330,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
 		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) {
+	} else if ((di->chg_info.charger_type & AC_CHG) &&
+					!(di->ac_chg->external)) {
 		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;
+	return 0;
 }
 
 /**
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 49/57] power: Cancelling status charging notification.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (47 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 48/57] power: ab8500 : quick re-attach for ext charger mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:16   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp mathieu.poirier
                   ` (8 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/abx500_chargalg.c |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index ce58f20..4db0ef0 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -641,10 +641,8 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
 	abx500_chargalg_usb_en(di, false, 0, 0);
 	abx500_chargalg_stop_safety_timer(di);
 	abx500_chargalg_stop_maintenance_timer(di);
-	di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
 	di->maintenance_chg = false;
 	cancel_delayed_work(&di->chargalg_wd_work);
-	power_supply_changed(&di->chargalg_psy);
 }
 
 /**
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (48 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 49/57] power: Cancelling status charging notification mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:21   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 51/57] power: ab8500: Re-alignment with internal developement mathieu.poirier
                   ` (7 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

When the charging safety timer is elapsed the battery health is shown as "Good".
This is misleading and also hard to distingiush problems reported on "phone
discharges although charger is attached".

When safety timer elapses that is an indication of a fault in the battery of
some kind. Hence report as POWER_SUPPLY_HEALTH_UNSPEC_FAILURE.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/power/abx500_chargalg.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 4db0ef0..1df238f 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1711,6 +1711,10 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
 				val->intval = POWER_SUPPLY_HEALTH_COLD;
 			else
 				val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+		} else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
+			di->charge_state ==
+			STATE_SAFETY_TIMER_EXPIRED_INIT) {
+			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
 		} else {
 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
 		}
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 51/57] power: ab8500: Re-alignment with internal developement.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (49 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:35   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 52/57] power: abx500_chargalg: Use hrtimer mathieu.poirier
                   ` (6 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

A lot of developement happened internally since the first
mainlining of the battery managmenent driver.  Most of the
new code can be historically accounted for but some of it
can't.

This patch is a gathering of the code for which history was
lost but still relevant to the well being of the driver.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/ab8500_charger.c  |    2 +-
 drivers/power/abx500_chargalg.c |   66 +++++++++++++++++++++++++++------------
 2 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 1290470..3a97012 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -720,7 +720,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
 		di->is_aca_rid = 0;
 		break;
 	case USB_STAT_ACA_RID_C_HS_CHIRP:
-		case USB_STAT_ACA_RID_C_NM:
+	case USB_STAT_ACA_RID_C_NM:
 		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
 		di->is_aca_rid = 1;
 		break;
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 1df238f..636d970 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -27,7 +27,7 @@
 #include <linux/notifier.h>
 
 /* Watchdog kick interval */
-#define CHG_WD_INTERVAL			(6 * HZ)
+#define CHG_WD_INTERVAL			(60 * HZ)
 
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT			10
@@ -513,7 +513,7 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
 	int vset, int iset)
 {
-	static int ab8500_chargalg_ex_ac_enable_toggle;
+	static int abx500_chargalg_ex_ac_enable_toggle;
 
 	if (!di->ac_chg || !di->ac_chg->ops.enable)
 		return -ENXIO;
@@ -529,10 +529,10 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
 
 	/*enable external charger*/
 	if (enable && di->ac_chg->external &&
-		!ab8500_chargalg_ex_ac_enable_toggle) {
+		!abx500_chargalg_ex_ac_enable_toggle) {
 		blocking_notifier_call_chain(&charger_notifier_list,
 					0, di->dev);
-		ab8500_chargalg_ex_ac_enable_toggle++;
+		abx500_chargalg_ex_ac_enable_toggle++;
 	}
 
 	return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
@@ -899,6 +899,27 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
 	}
 }
 
+static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
+{
+	/*
+	 * The safety timer will not be started until the capacity reported
+	 * from the FG algorithm is 100%. Then we know that the amount of
+	 * charge that's gone into the battery is enough for the battery
+	 * to be full. If it has not reached end-of-charge before the safety
+	 * timer has expired then we know that the battery is overcharged
+	 * and charging will be stopped to protect the battery.
+	 */
+	if (di->batt_data.percent == 100 &&
+		!timer_pending(&di->safety_timer)) {
+		abx500_chargalg_start_safety_timer(di);
+		dev_dbg(di->dev, "start safety timer\n");
+	} else if (di->batt_data.percent != 100 &&
+		timer_pending(&di->safety_timer)) {
+		abx500_chargalg_stop_safety_timer(di);
+		dev_dbg(di->dev, "stop safety timer\n");
+	}
+}
+
 static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 {
 	struct power_supply *psy;
@@ -1125,6 +1146,10 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 			switch (ext->type) {
 			case POWER_SUPPLY_TYPE_BATTERY:
 				di->batt_data.volt = ret.intval / 1000;
+				if (di->batt_data.volt >= BATT_OVV_VALUE)
+					di->events.batt_ovv = true;
+				else
+					di->events.batt_ovv = false;
 				break;
 			case POWER_SUPPLY_TYPE_MAINS:
 				di->chg_info.ac_volt = ret.intval / 1000;
@@ -1214,7 +1239,6 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 			}
 			break;
 		case POWER_SUPPLY_PROP_CAPACITY:
-			di->batt_data.percent = ret.intval;
 			if (!capacity_updated)
 				di->batt_data.percent = ret.intval;
 			break;
@@ -1465,12 +1489,12 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 			di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
 			di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
 		abx500_chargalg_state_to(di, STATE_NORMAL);
-		abx500_chargalg_start_safety_timer(di);
 		abx500_chargalg_stop_maintenance_timer(di);
 		init_maxim_chg_curr(di);
 		di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
 		di->eoc_cnt = 0;
 		di->maintenance_chg = false;
+		di->maint_state = MAINT_A;
 		power_supply_changed(&di->chargalg_psy);
 
 		break;
@@ -1478,17 +1502,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 	case STATE_NORMAL:
 		handle_maxim_chg_curr(di);
 		if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
-			di->maintenance_chg) {
-			if (di->bat->no_maintenance)
-				abx500_chargalg_state_to(di,
-					STATE_WAIT_FOR_RECHARGE_INIT);
-			else
-				abx500_chargalg_state_to(di,
-					STATE_MAINTENANCE_A_INIT);
-		}
+				di->maintenance_chg)
+			abx500_chargalg_state_to(di,
+				STATE_WAIT_FOR_RECHARGE_INIT);
+
+		/* Check whether we should start the safety timer or not */
+		abx500_chargalg_check_safety_timer(di);
 		break;
 
-	/* This state will be used when the maintenance state is disabled */
 	case STATE_WAIT_FOR_RECHARGE_INIT:
 		abx500_chargalg_hold_charging(di);
 		abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
@@ -1531,13 +1552,15 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 			di->bat->bat_type[
 				di->bat->batt_id].maint_a_cur_lvl);
 		abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+		di->maint_state = MAINT_B;
 		power_supply_changed(&di->chargalg_psy);
 		/* Intentional fallthrough*/
 
 	case STATE_MAINTENANCE_A:
 		if (di->events.maintenance_timer_expired) {
 			abx500_chargalg_stop_maintenance_timer(di);
-			abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+			abx500_chargalg_state_to(di,
+					STATE_WAIT_FOR_RECHARGE_INIT);
 		}
 		break;
 
@@ -1603,7 +1626,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 	/* Start charging directly if the new state is a charge state */
 	if (di->charge_state == STATE_NORMAL_INIT ||
 			di->charge_state == STATE_MAINTENANCE_A_INIT ||
-			di->charge_state == STATE_MAINTENANCE_B_INIT)
+			di->charge_state == STATE_MAINTENANCE_B_INIT ||
+			di->charge_state == STATE_WAIT_FOR_RECHARGE_INIT)
 		queue_work(di->chargalg_wq, &di->chargalg_work);
 }
 
@@ -1824,7 +1848,7 @@ static struct attribute *abx500_chargalg_chg[] = {
 	NULL
 };
 
-static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+const struct sysfs_ops abx500_chargalg_sysfs_ops = {
 	.show = abx500_chargalg_sysfs_show,
 	.store = abx500_chargalg_sysfs_charger,
 };
@@ -2009,10 +2033,12 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 		goto free_psy;
 	}
 
+	di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	abx500_chargalg_state_to(di, STATE_HANDHELD);
+
 	/* Run the charging algorithm */
 	queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
 
-	dev_info(di->dev, "probe success\n");
 	return ret;
 
 free_psy:
@@ -2052,4 +2078,4 @@ module_exit(abx500_chargalg_exit);
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
 MODULE_ALIAS("platform:abx500-chargalg");
-MODULE_DESCRIPTION("abx500 battery charging algorithm");
+MODULE_DESCRIPTION("abx500 battery temperatur driver");
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 52/57] power: abx500_chargalg: Use hrtimer
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (50 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 51/57] power: ab8500: Re-alignment with internal developement mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:47   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file mathieu.poirier
                   ` (5 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Hakan Berg <hakan.berg@stericsson.com>

Timers used for charging safety and maintenance must work even when
CPU is power collapsed. By using hrtimers with realtime clock, system
is able to trigger an alarm that wakes the CPU up and make it possible
to handle the event.

Allow a little slack of 5 minutes to the hrtimers to allow CPU to be
waked up in a more optimal power saving way. A 5 minute delay to
time out timers on hours does not impact on safety.

Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
---
 drivers/power/abx500_chargalg.c |   94 ++++++++++++++++++++++-----------------
 1 files changed, 53 insertions(+), 41 deletions(-)

diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 636d970..c8849af 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (c) 2012 Sony Mobile Communications AB
  *
  * Charging algorithm driver for abx500 variants
  *
@@ -8,11 +9,13 @@
  *	Johan Palsson <johan.palsson@stericsson.com>
  *	Karl Komierowski <karl.komierowski@stericsson.com>
  *	Arun R Murthy <arun.murthy@stericsson.com>
+ *	Imre Sunyi <imre.sunyi@sonymobile.com>
  */
 
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/hrtimer.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -32,6 +35,12 @@
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT			10
 
+/* One hour expressed in seconds */
+#define ONE_HOUR_IN_SECONDS		3600
+
+/* Five minutes expressed in seconds */
+#define FIVE_MINUTES_IN_SECONDS		300
+
 #define to_abx500_chargalg_device_info(x) container_of((x), \
 	struct abx500_chargalg, chargalg_psy);
 
@@ -245,8 +254,8 @@ struct abx500_chargalg {
 	struct delayed_work chargalg_periodic_work;
 	struct delayed_work chargalg_wd_work;
 	struct work_struct chargalg_work;
-	struct timer_list safety_timer;
-	struct timer_list maintenance_timer;
+	struct hrtimer safety_timer;
+	struct hrtimer maintenance_timer;
 	struct kobject chargalg_kobject;
 };
 
@@ -261,38 +270,47 @@ BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
 
 /**
  * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
- * @data:	pointer to the abx500_chargalg structure
+ * @timer:	pointer to the hrtimer structure
  *
  * This function gets called when the safety timer for the charger
  * expires
  */
-static void abx500_chargalg_safety_timer_expired(unsigned long data)
+static enum hrtimer_restart
+abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
 {
-	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+							safety_timer);
 	dev_err(di->dev, "Safety timer expired\n");
 	di->events.safety_timer_expired = true;
 
 	/* Trigger execution of the algorithm instantly */
 	queue_work(di->chargalg_wq, &di->chargalg_work);
+
+	return HRTIMER_NORESTART;
 }
 
 /**
  * abx500_chargalg_maintenance_timer_expired() - Expiration of
  * the maintenance timer
- * @i:		pointer to the abx500_chargalg structure
+ * @timer:	pointer to the timer structure
  *
  * This function gets called when the maintenence timer
  * expires
  */
-static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
+static enum hrtimer_restart
+abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
+
 {
 
-	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+							maintenance_timer);
 	dev_dbg(di->dev, "Maintenance timer expired\n");
 	di->events.maintenance_timer_expired = true;
 
 	/* Trigger execution of the algorithm instantly */
 	queue_work(di->chargalg_wq, &di->chargalg_work);
+
+	return HRTIMER_NORESTART;
 }
 
 /**
@@ -392,19 +410,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
  */
 static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
 {
-	unsigned long timer_expiration = 0;
+	/* Charger-dependent expiration time in hours*/
+	int timer_expiration = 0;
 
 	switch (di->chg_info.charger_type) {
 	case AC_CHG:
-		timer_expiration =
-		round_jiffies(jiffies +
-			(di->bat->main_safety_tmr_h * 3600 * HZ));
+		timer_expiration = di->bat->main_safety_tmr_h;
 		break;
 
 	case USB_CHG:
-		timer_expiration =
-		round_jiffies(jiffies +
-			(di->bat->usb_safety_tmr_h * 3600 * HZ));
+		timer_expiration = di->bat->usb_safety_tmr_h;
 		break;
 
 	default:
@@ -413,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
 	}
 
 	di->events.safety_timer_expired = false;
-	di->safety_timer.expires = timer_expiration;
-	if (!timer_pending(&di->safety_timer))
-		add_timer(&di->safety_timer);
-	else
-		mod_timer(&di->safety_timer, timer_expiration);
+	hrtimer_set_expires_range(&di->safety_timer,
+		ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
+		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
+	hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
 }
 
 /**
@@ -428,8 +442,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
  */
 static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
 {
-	di->events.safety_timer_expired = false;
-	del_timer(&di->safety_timer);
+	if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
+		di->events.safety_timer_expired = false;
 }
 
 /**
@@ -444,17 +458,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
 static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
 	int duration)
 {
-	unsigned long timer_expiration;
-
-	/* Convert from hours to jiffies */
-	timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
-
+	hrtimer_set_expires_range(&di->maintenance_timer,
+		ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
+		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
 	di->events.maintenance_timer_expired = false;
-	di->maintenance_timer.expires = timer_expiration;
-	if (!timer_pending(&di->maintenance_timer))
-		add_timer(&di->maintenance_timer);
-	else
-		mod_timer(&di->maintenance_timer, timer_expiration);
+	hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
 }
 
 /**
@@ -466,8 +474,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
  */
 static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
 {
-	di->events.maintenance_timer_expired = false;
-	del_timer(&di->maintenance_timer);
+	if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
+		di->events.maintenance_timer_expired = false;
 }
 
 /**
@@ -910,11 +918,11 @@ static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
 	 * and charging will be stopped to protect the battery.
 	 */
 	if (di->batt_data.percent == 100 &&
-		!timer_pending(&di->safety_timer)) {
+		!hrtimer_active(&di->safety_timer)) {
 		abx500_chargalg_start_safety_timer(di);
 		dev_dbg(di->dev, "start safety timer\n");
 	} else if (di->batt_data.percent != 100 &&
-		timer_pending(&di->safety_timer)) {
+		hrtimer_active(&di->safety_timer)) {
 		abx500_chargalg_stop_safety_timer(di);
 		dev_dbg(di->dev, "stop safety timer\n");
 	}
@@ -1932,10 +1940,16 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
 	/* sysfs interface to enable/disbale charging from user space */
 	abx500_chargalg_sysfs_exit(di);
 
+	hrtimer_cancel(&di->safety_timer);
+	hrtimer_cancel(&di->maintenance_timer);
+
+	cancel_delayed_work_sync(&di->chargalg_periodic_work);
+	cancel_delayed_work_sync(&di->chargalg_wd_work);
+	cancel_work_sync(&di->chargalg_work);
+
 	/* Delete the work queue */
 	destroy_workqueue(di->chargalg_wq);
 
-	flush_scheduled_work();
 	power_supply_unregister(&di->chargalg_psy);
 	platform_set_drvdata(pdev, NULL);
 	kfree(di);
@@ -1987,15 +2001,13 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
 		abx500_chargalg_external_power_changed;
 
 	/* Initilialize safety timer */
-	init_timer(&di->safety_timer);
+	hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
 	di->safety_timer.function = abx500_chargalg_safety_timer_expired;
-	di->safety_timer.data = (unsigned long) di;
 
 	/* Initilialize maintenance timer */
-	init_timer(&di->maintenance_timer);
+	hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
 	di->maintenance_timer.function =
 		abx500_chargalg_maintenance_timer_expired;
-	di->maintenance_timer.data = (unsigned long) di;
 
 	/* Create a work queue for the chargalg */
 	di->chargalg_wq =
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (51 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 52/57] power: abx500_chargalg: Use hrtimer mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:51   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register mathieu.poirier
                   ` (4 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/power/ab8500_fg.c |  196 +------------------------------------------
 drivers/power/ab8500_fg.h |  201 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 206 insertions(+), 191 deletions(-)
 create mode 100644 drivers/power/ab8500_fg.h

diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 5b5c7ea..145c1f1 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -32,51 +32,7 @@
 #include <linux/time.h>
 #include <linux/completion.h>
 #include <linux/kernel.h>
-
-#define MILLI_TO_MICRO			1000
-#define FG_LSB_IN_MA			1627
-#define QLSB_NANO_AMP_HOURS_X10		1129
-#define INS_CURR_TIMEOUT		(3 * HZ)
-
-#define SEC_TO_SAMPLE(S)		(S * 4)
-
-#define NBR_AVG_SAMPLES			20
-
-#define LOW_BAT_CHECK_INTERVAL		(HZ / 16) /* 62.5 ms */
-
-#define VALID_CAPACITY_SEC		(45 * 60) /* 45 minutes */
-#define BATT_OK_MIN			2360 /* mV */
-#define BATT_OK_INCREMENT		50 /* mV */
-#define BATT_OK_MAX_NR_INCREMENTS	0xE
-
-/* FG constants */
-#define BATT_OVV			0x01
-
-#define interpolate(x, x1, y1, x2, y2) \
-	((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
-
-#define to_ab8500_fg_device_info(x) container_of((x), \
-	struct ab8500_fg, fg_psy);
-
-/**
- * struct ab8500_fg_interrupts - ab8500 fg interupts
- * @name:	name of the interrupt
- * @isr		function pointer to the isr
- */
-struct ab8500_fg_interrupts {
-	char *name;
-	irqreturn_t (*isr)(int irq, void *data);
-};
-
-enum ab8500_fg_discharge_state {
-	AB8500_FG_DISCHARGE_INIT,
-	AB8500_FG_DISCHARGE_INITMEASURING,
-	AB8500_FG_DISCHARGE_INIT_RECOVERY,
-	AB8500_FG_DISCHARGE_RECOVERY,
-	AB8500_FG_DISCHARGE_READOUT_INIT,
-	AB8500_FG_DISCHARGE_READOUT,
-	AB8500_FG_DISCHARGE_WAKEUP,
-};
+#include "ab8500_fg.h"
 
 static char *discharge_state[] = {
 	"DISCHARGE_INIT",
@@ -87,159 +43,17 @@ static char *discharge_state[] = {
 	"DISCHARGE_READOUT",
 	"DISCHARGE_WAKEUP",
 };
-
-enum ab8500_fg_charge_state {
-	AB8500_FG_CHARGE_INIT,
-	AB8500_FG_CHARGE_READOUT,
-};
-
 static char *charge_state[] = {
 	"CHARGE_INIT",
 	"CHARGE_READOUT",
 };
 
-enum ab8500_fg_calibration_state {
-	AB8500_FG_CALIB_INIT,
-	AB8500_FG_CALIB_WAIT,
-	AB8500_FG_CALIB_END,
-};
-
-struct ab8500_fg_avg_cap {
-	int avg;
-	int samples[NBR_AVG_SAMPLES];
-	__kernel_time_t time_stamps[NBR_AVG_SAMPLES];
-	int pos;
-	int nbr_samples;
-	int sum;
-};
-
-struct ab8500_fg_cap_scaling {
-	bool enable;
-	int cap_to_scale[2];
-	int disable_cap_level;
-	int scaled_cap;
-};
-
-struct ab8500_fg_battery_capacity {
-	int max_mah_design;
-	int max_mah;
-	int mah;
-	int permille;
-	int level;
-	int prev_mah;
-	int prev_percent;
-	int prev_level;
-	int user_mah;
-	struct ab8500_fg_cap_scaling cap_scale;
-};
-
-struct ab8500_fg_flags {
-	bool fg_enabled;
-	bool conv_done;
-	bool charging;
-	bool fully_charged;
-	bool force_full;
-	bool low_bat_delay;
-	bool low_bat;
-	bool bat_ovv;
-	bool batt_unknown;
-	bool calibrate;
-	bool user_cap;
-	bool batt_id_received;
-};
+#define interpolate(x, x1, y1, x2, y2) \
+	((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
 
-struct inst_curr_result_list {
-	struct list_head list;
-	int *result;
-};
+#define to_ab8500_fg_device_info(x) container_of((x), \
+	struct ab8500_fg, fg_psy);
 
-/**
- * struct ab8500_fg - ab8500 FG device information
- * @dev:		Pointer to the structure device
- * @node:		a list of AB8500 FGs, hence prepared for reentrance
- * @irq			holds the CCEOC interrupt number
- * @vbat:		Battery voltage in mV
- * @vbat_nom:		Nominal battery voltage in mV
- * @inst_curr:		Instantenous battery current in mA
- * @avg_curr:		Average battery current in mA
- * @bat_temp		battery temperature
- * @fg_samples:		Number of samples used in the FG accumulation
- * @accu_charge:	Accumulated charge from the last conversion
- * @recovery_cnt:	Counter for recovery mode
- * @high_curr_cnt:	Counter for high current mode
- * @init_cnt:		Counter for init mode
- * @low_bat_cnt		Counter for number of consecutive low battery measures
- * @nbr_cceoc_irq_cnt	Counter for number of CCEOC irqs received since enabled
- * @recovery_needed:	Indicate if recovery is needed
- * @high_curr_mode:	Indicate if we're in high current mode
- * @init_capacity:	Indicate if initial capacity measuring should be done
- * @turn_off_fg:	True if fg was off before current measurement
- * @calib_state		State during offset calibration
- * @discharge_state:	Current discharge state
- * @charge_state:	Current charge state
- * @ab8500_fg_started	Completion struct used for the instant current start
- * @ab8500_fg_complete	Completion struct used for the instant current reading
- * @flags:		Structure for information about events triggered
- * @bat_cap:		Structure for battery capacity specific parameters
- * @avg_cap:		Average capacity filter
- * @parent:		Pointer to the struct ab8500
- * @gpadc:		Pointer to the struct gpadc
- * @pdata:		Pointer to the abx500_fg platform data
- * @bat:		Pointer to the abx500_bm platform data
- * @fg_psy:		Structure that holds the FG specific battery properties
- * @fg_wq:		Work queue for running the FG algorithm
- * @fg_periodic_work:	Work to run the FG algorithm periodically
- * @fg_low_bat_work:	Work to check low bat condition
- * @fg_reinit_work	Work used to reset and reinitialise the FG algorithm
- * @fg_work:		Work to run the FG algorithm instantly
- * @fg_acc_cur_work:	Work to read the FG accumulator
- * @fg_check_hw_failure_work:	Work for checking HW state
- * @cc_lock:		Mutex for locking the CC
- * @fg_kobject:		Structure of type kobject
- */
-struct ab8500_fg {
-	struct device *dev;
-	struct list_head node;
-	int irq;
-	int vbat;
-	int vbat_nom;
-	int inst_curr;
-	int avg_curr;
-	int bat_temp;
-	int fg_samples;
-	int accu_charge;
-	int recovery_cnt;
-	int high_curr_cnt;
-	int init_cnt;
-	int low_bat_cnt;
-	int nbr_cceoc_irq_cnt;
-	bool recovery_needed;
-	bool high_curr_mode;
-	bool init_capacity;
-	bool turn_off_fg;
-	enum ab8500_fg_calibration_state calib_state;
-	enum ab8500_fg_discharge_state discharge_state;
-	enum ab8500_fg_charge_state charge_state;
-	struct completion ab8500_fg_started;
-	struct completion ab8500_fg_complete;
-	struct ab8500_fg_flags flags;
-	struct ab8500_fg_battery_capacity bat_cap;
-	struct ab8500_fg_avg_cap avg_cap;
-	struct ab8500 *parent;
-	struct ab8500_gpadc *gpadc;
-	struct abx500_fg_platform_data *pdata;
-	struct abx500_bm_data *bat;
-	struct power_supply fg_psy;
-	struct workqueue_struct *fg_wq;
-	struct delayed_work fg_periodic_work;
-	struct delayed_work fg_low_bat_work;
-	struct delayed_work fg_reinit_work;
-	struct work_struct fg_work;
-	struct work_struct fg_acc_cur_work;
-	struct delayed_work fg_check_hw_failure_work;
-	struct mutex cc_lock;
-	struct kobject fg_kobject;
-};
 static LIST_HEAD(ab8500_fg_list);
 
 /**
diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
new file mode 100644
index 0000000..5cfadc2
--- /dev/null
+++ b/drivers/power/ab8500_fg.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#define MILLI_TO_MICRO			1000
+#define FG_LSB_IN_MA			1627
+#define QLSB_NANO_AMP_HOURS_X10		1129
+#define INS_CURR_TIMEOUT		(3 * HZ)
+
+#define SEC_TO_SAMPLE(S)		(S * 4)
+
+#define NBR_AVG_SAMPLES			20
+
+#define LOW_BAT_CHECK_INTERVAL		(HZ / 16) /* 62.5 ms */
+
+#define VALID_CAPACITY_SEC		(45 * 60) /* 45 minutes */
+#define BATT_OK_MIN			2360 /* mV */
+#define BATT_OK_INCREMENT		50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS	0xE
+
+/* FG constants */
+#define BATT_OVV			0x01
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name:	name of the interrupt
+ * @isr		function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+	char *name;
+	irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+	AB8500_FG_DISCHARGE_INIT,
+	AB8500_FG_DISCHARGE_INITMEASURING,
+	AB8500_FG_DISCHARGE_INIT_RECOVERY,
+	AB8500_FG_DISCHARGE_RECOVERY,
+	AB8500_FG_DISCHARGE_READOUT_INIT,
+	AB8500_FG_DISCHARGE_READOUT,
+	AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+enum ab8500_fg_charge_state {
+	AB8500_FG_CHARGE_INIT,
+	AB8500_FG_CHARGE_READOUT,
+};
+
+enum ab8500_fg_calibration_state {
+	AB8500_FG_CALIB_INIT,
+	AB8500_FG_CALIB_WAIT,
+	AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+	int avg;
+	int samples[NBR_AVG_SAMPLES];
+	__kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+	int pos;
+	int nbr_samples;
+	int sum;
+};
+
+struct ab8500_fg_cap_scaling {
+	bool enable;
+	int cap_to_scale[2];
+	int disable_cap_level;
+	int scaled_cap;
+};
+
+struct ab8500_fg_battery_capacity {
+	int max_mah_design;
+	int max_mah;
+	int mah;
+	int permille;
+	int level;
+	int prev_mah;
+	int prev_percent;
+	int prev_level;
+	int user_mah;
+	struct ab8500_fg_cap_scaling cap_scale;
+};
+
+struct ab8500_fg_flags {
+	bool fg_enabled;
+	bool conv_done;
+	bool charging;
+	bool fully_charged;
+	bool force_full;
+	bool low_bat_delay;
+	bool low_bat;
+	bool bat_ovv;
+	bool batt_unknown;
+	bool calibrate;
+	bool user_cap;
+	bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+	struct list_head list;
+	int *result;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev:		Pointer to the structure device
+ * @node:		a list of AB8500 FGs, hence prepared for reentrance
+ * @irq			holds the CCEOC interrupt number
+ * @vbat:		Battery voltage in mV
+ * @vbat_nom:		Nominal battery voltage in mV
+ * @inst_curr:		Instantenous battery current in mA
+ * @avg_curr:		Average battery current in mA
+ * @bat_temp		battery temperature
+ * @fg_samples:		Number of samples used in the FG accumulation
+ * @accu_charge:	Accumulated charge from the last conversion
+ * @recovery_cnt:	Counter for recovery mode
+ * @high_curr_cnt:	Counter for high current mode
+ * @init_cnt:		Counter for init mode
+ * @low_bat_cnt		Counter for number of consecutive low battery measures
+ * @nbr_cceoc_irq_cnt	Counter for number of CCEOC irqs received since enabled
+ * @recovery_needed:	Indicate if recovery is needed
+ * @high_curr_mode:	Indicate if we're in high current mode
+ * @init_capacity:	Indicate if initial capacity measuring should be done
+ * @turn_off_fg:	True if fg was off before current measurement
+ * @calib_state		State during offset calibration
+ * @discharge_state:	Current discharge state
+ * @charge_state:	Current charge state
+ * @ab8500_fg_started	Completion struct used for the instant current start
+ * @ab8500_fg_complete	Completion struct used for the instant current reading
+ * @flags:		Structure for information about events triggered
+ * @bat_cap:		Structure for battery capacity specific parameters
+ * @avg_cap:		Average capacity filter
+ * @parent:		Pointer to the struct ab8500
+ * @gpadc:		Pointer to the struct gpadc
+ * @pdata:		Pointer to the abx500_fg platform data
+ * @bat:		Pointer to the abx500_bm platform data
+ * @fg_psy:		Structure that holds the FG specific battery properties
+ * @fg_wq:		Work queue for running the FG algorithm
+ * @fg_periodic_work:	Work to run the FG algorithm periodically
+ * @fg_low_bat_work:	Work to check low bat condition
+ * @fg_reinit_work	Work used to reset and reinitialise the FG algorithm
+ * @fg_work:		Work to run the FG algorithm instantly
+ * @fg_acc_cur_work:	Work to read the FG accumulator
+ * @fg_check_hw_failure_work:	Work for checking HW state
+ * @cc_lock:		Mutex for locking the CC
+ * @fg_kobject:		Structure of type kobject
+ */
+struct ab8500_fg {
+	struct device *dev;
+	struct list_head node;
+	int irq;
+	int vbat;
+	int vbat_nom;
+	int inst_curr;
+	int avg_curr;
+	int bat_temp;
+	int fg_samples;
+	int accu_charge;
+	int recovery_cnt;
+	int high_curr_cnt;
+	int init_cnt;
+	int low_bat_cnt;
+	int nbr_cceoc_irq_cnt;
+	bool recovery_needed;
+	bool high_curr_mode;
+	bool init_capacity;
+	bool turn_off_fg;
+	enum ab8500_fg_calibration_state calib_state;
+	enum ab8500_fg_discharge_state discharge_state;
+	enum ab8500_fg_charge_state charge_state;
+	struct completion ab8500_fg_started;
+	struct completion ab8500_fg_complete;
+	struct ab8500_fg_flags flags;
+	struct ab8500_fg_battery_capacity bat_cap;
+	struct ab8500_fg_avg_cap avg_cap;
+	struct ab8500 *parent;
+	struct ab8500_gpadc *gpadc;
+	struct abx500_fg_platform_data *pdata;
+	struct abx500_bm_data *bat;
+	struct power_supply fg_psy;
+	struct workqueue_struct *fg_wq;
+	struct delayed_work fg_periodic_work;
+	struct delayed_work fg_low_bat_work;
+	struct delayed_work fg_reinit_work;
+	struct work_struct fg_work;
+	struct work_struct fg_acc_cur_work;
+	struct delayed_work fg_check_hw_failure_work;
+	struct mutex cc_lock;
+	struct kobject fg_kobject;
+};
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (52 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:56   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference mathieu.poirier
                   ` (3 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

The newer AB's such as the AB8505, AB9540 etc include a
USBLink1 Status register which detects a larger range of
external devices. This should be used instead of the
USBLine Status register.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Yang QU <yang.qu@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c |   22 ++++++++++++++++------
 1 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 3a97012..7f8f362 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2258,8 +2258,13 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 	 * to start the charging process. but by jumping
 	 * thru a few hoops it can be forced to start.
 	 */
-	ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-			AB8500_USB_LINE_STAT_REG, &val);
+	if (is_ab8500(di->parent))
+		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+					AB8500_USB_LINE_STAT_REG, &val);
+	else
+		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+					AB8500_USB_LINK1_STAT_REG, &val);
+
 	if (ret >= 0)
 		dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
 	else
@@ -2299,10 +2304,15 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 						AB8500_MCH_IPT_CURLVL_REG,
 						0x01, 0x00);
 			/*Check link status*/
-			ret = abx500_get_register_interruptible(di->dev,
-						AB8500_USB,
-						AB8500_USB_LINE_STAT_REG,
-						&val);
+			if (is_ab8500(di->parent))
+				ret = abx500_get_register_interruptible(di->dev,
+					AB8500_USB, AB8500_USB_LINE_STAT_REG,
+					&val);
+			else
+				ret = abx500_get_register_interruptible(di->dev,
+					AB8500_USB, AB8500_USB_LINK1_STAT_REG,
+					&val);
+
 			dev_dbg(di->dev, "USB link status= 0x%02x\n",
 				(val & link_status) >> USB_LINK_STATUS_SHIFT);
 			di->invalid_charger_detect_state = 2;
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (53 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  2:58   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue mathieu.poirier
                   ` (2 subsequent siblings)
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

When the state of USB Charge detection is changed then the calls
use a define for another register in other bank. This change
creates a new define for the correct register and removes the
magic numbers that are present.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>

Conflicts:

	drivers/power/ab8500_charger.c
---
 drivers/power/ab8500_charger.c       |   11 +++++------
 include/linux/mfd/abx500/ab8500-bm.h |    1 +
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 7f8f362..afb4fda 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -51,6 +51,7 @@
 #define VBUS_DET_DBNC1			0x01
 #define OTP_ENABLE_WD			0x01
 #define DROP_COUNT_RESET		0x01
+#define USB_CH_DET			0x01
 
 #define MAIN_CH_INPUT_CURR_SHIFT	4
 #define VBUS_IN_CURR_LIM_SHIFT		4
@@ -2287,9 +2288,8 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 					USB_CH_ENA, USB_CH_ENA);
 			/*Enable charger detection*/
 			abx500_mask_and_set_register_interruptible(di->dev,
-						AB8500_USB,
-						AB8500_MCH_IPT_CURLVL_REG,
-						0x01, 0x01);
+					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
+					USB_CH_DET, USB_CH_DET);
 			di->invalid_charger_detect_state = 1;
 			/*exit and wait for new link status interrupt.*/
 			return;
@@ -2300,9 +2300,8 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
 				"Invalid charger detected, state= 1\n");
 			/*Stop charger detection*/
 			abx500_mask_and_set_register_interruptible(di->dev,
-						AB8500_USB,
-						AB8500_MCH_IPT_CURLVL_REG,
-						0x01, 0x00);
+					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
+					USB_CH_DET, 0x00);
 			/*Check link status*/
 			if (is_ab8500(di->parent))
 				ret = abx500_get_register_interruptible(di->dev,
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 721bd6d..6b69ad5 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -23,6 +23,7 @@
  * Bank : 0x5
  */
 #define AB8500_USB_LINE_STAT_REG	0x80
+#define AB8500_USB_LINE_CTRL2_REG	0x82
 #define AB8500_USB_LINK1_STAT_REG	0x94
 
 /*
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue.
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (54 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-28  3:00   ` Anton Vorontsov
  2012-09-25 16:12 ` [PATCH 57/57] power: ab8500_charger: Limit USB charger current mathieu.poirier
  2012-09-27  3:38 ` [PATCH 00/57] power: Upgrade to ux500 battery management driver Anton Vorontsov
  57 siblings, 1 reply; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Marcus Cooper <marcus.xm.cooper@stericsson.com>

The patch for 426250 added a change to check for the quick
re-attachment of the charger connection as an error in the
AB8500 HW meant that a quick detach/attach wouldn't be
detected.
This patch isolates the original change so that newer AB's
are not affected.

Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/abx500_chargalg.c |   11 ++++++-----
 1 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index c8849af..7a81e4e 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1299,11 +1299,12 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *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__);
-
+	if (is_ab8500(di->parent)) {
+		ret = abx500_chargalg_check_charger_enable(di);
+		if (ret < 0)
+			dev_err(di->dev, "Checking charger is enabled error");
+			dev_err(di->dev, ": Returned Value %d\n", ret);
+	}
 	/*
 	 * First check if we have a charger connected.
 	 * Also we don't allow charging of unknown batteries if configured
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 57/57] power: ab8500_charger: Limit USB charger current
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (55 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue mathieu.poirier
@ 2012-09-25 16:12 ` mathieu.poirier
  2012-09-27  3:38 ` [PATCH 00/57] power: Upgrade to ux500 battery management driver Anton Vorontsov
  57 siblings, 0 replies; 108+ messages in thread
From: mathieu.poirier @ 2012-09-25 16:12 UTC (permalink / raw)
  To: linux-kernel, cbou, dwmw2; +Cc: mathieu.poirier

From: Martin Bergstrom <martin.bergstrom@stericsson.com>

The USB charger current is limited according to information comming
from the USB driver

Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/power/ab8500_charger.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index afb4fda..3c6f11c 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -1248,6 +1248,9 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 	if (di->max_usb_in_curr.set_max > 0)
 		min_value = min(di->max_usb_in_curr.set_max, min_value);
 
+	if (di->usb_state.usb_current >= 0)
+		min_value = min(di->usb_state.usb_current, min_value);
+
 	switch (min_value) {
 	case 100:
 		if (di->vbat < VBAT_TRESH_IP_CUR_RED)
@@ -3413,6 +3416,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 	di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
 	di->usb_chg.enabled = di->pdata->usb_enabled;
 	di->usb_chg.external = false;
+	di->usb_state.usb_current = -1;
 
 	/* Create a work queue for the charger */
 	di->charger_wq =
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep
  2012-09-25 16:12 ` [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep mathieu.poirier
@ 2012-09-27  2:36   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  2:36 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:06AM -0600, mathieu.poirier@linaro.org wrote:
> From: Jonas Aaberg <jonas.aberg@stericsson.com>
> 
> Signed-off-by: Jonas ABERG <jonas.aberg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 1e02b00..0db17c7 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -957,7 +957,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
>  	do {
>  		vbat += ab8500_fg_bat_voltage(di);
>  		i++;
> -		msleep(5);
> +		usleep_range(5000, 5001);

Um.. why? From the commit message it's not obvious the rationale behind
this change. :-)

>  	} while (!ab8500_fg_inst_curr_done(di));
>  
>  	ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment
  2012-09-25 16:12 ` [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment mathieu.poirier
@ 2012-09-27  2:42   ` Anton Vorontsov
  2012-09-27  2:53   ` Anton Vorontsov
  1 sibling, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  2:42 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:01AM -0600, mathieu.poirier@linaro.org wrote:
> From: Philippe Langlais <philippe.langlais@linaro.org>

The empty message turned me into suspicious mode, and I took another look
at the patch... :-)

> Signed-off-by: Philippe Langlais <philippe.langlais@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/power/ab8500_btemp.c      |    8 ++------
>  drivers/power/ab8500_charger.c    |    9 +++------
>  drivers/power/ab8500_fg.c         |    8 ++------
>  drivers/power/abx500_chargalg.c   |    7 ++++---
>  include/linux/mfd/abx500/ab8500.h |    7 ++++++-
>  5 files changed, 17 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index 94a3ee8..41a8ce4 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -973,14 +973,9 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
>  {
>  	int irq, i, ret = 0;
>  	u8 val;
> -	struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
> +	struct ab8500_platform_data *plat_data;
>  	struct ab8500_btemp *di;
>  
> -	if (!plat_data) {
> -		dev_err(&pdev->dev, "No platform data\n");
> -		return -EINVAL;
> -	}
> -

Why? This basically reverts recent change:

commit ec511672b97383107d87e86921b1f0392bc1d000
Author: Linus Walleij <linus.walleij@linaro.org>
Date:   Fri Apr 13 10:16:06 2012 +0200

    ab8500_btemp: Harden platform data check

..and other commits from Linus' series.

The patch description doesn't tell anything why this is no longer needed.

[...]
> --- a/include/linux/mfd/abx500/ab8500.h
> +++ b/include/linux/mfd/abx500/ab8500.h
> @@ -170,7 +170,7 @@ enum ab8500_version {
>  #define AB8500_INT_ID_DET_R2F		99
>  #define AB8500_INT_ID_DET_R3F		100
>  #define AB8500_INT_ID_DET_R4F		101
> -#define AB8500_INT_CHAUTORESTARTAFTSEC  102
> +#define AB8500_INT_CHAUTORESTARTAFTSEC	102

This is surely stray change.

>  #define AB8500_INT_CHSTOPBYSEC		103
>  /* ab8500_irq_regoffset[13] -> IT[Source|Latch|Mask]22 */
>  #define AB8500_INT_USB_CH_TH_PROT_F	104
> @@ -289,6 +289,11 @@ struct ab8500_platform_data {
>  	struct regulator_init_data *regulator;
>  	struct ab8500_gpio_platform_data *gpio;
>  	struct ab8500_codec_platform_data *codec;
> +	struct abx500_bm_data *battery;
> +	struct abx500_charger_platform_data *charger;
> +	struct abx500_btemp_platform_data *btemp;
> +	struct abx500_fg_platform_data *fg;
> +	struct abx500_chargalg_platform_data *chargalg;

These seem like stray changes too. At least I don't see how you the new
members in this patch.

>  };
>  
>  extern int __devinit ab8500_init(struct ab8500 *ab8500,
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment
  2012-09-25 16:12 ` [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment mathieu.poirier
  2012-09-27  2:42   ` Anton Vorontsov
@ 2012-09-27  2:53   ` Anton Vorontsov
  1 sibling, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  2:53 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:01AM -0600, mathieu.poirier@linaro.org wrote:
> From: Philippe Langlais <philippe.langlais@linaro.org>

Oh, and there's also a typo in the subject line.

> Signed-off-by: Philippe Langlais <philippe.langlais@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
...

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 07/57] power: ab8500_bm: Detect removed charger
  2012-09-25 16:12 ` [PATCH 07/57] power: ab8500_bm: Detect removed charger mathieu.poirier
@ 2012-09-27  3:09   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:09 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:04AM -0600, mathieu.poirier@linaro.org wrote:
> From: Jonas Aaberg <jonas.aberg@stericsson.com>

No description forced me to look into this more closely. :-)

> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/power/ab8500_charger.c |  122 +++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 121 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index a7d0c3a..f7bba07 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -66,6 +66,11 @@
>  #define MAIN_CH_NOK			0x01
>  #define VBUS_DET			0x80
>  
> +#define MAIN_CH_STATUS2_MAINCHGDROP		0x80
> +#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC	0x40
> +#define USB_CH_VBUSDROP				0x40
> +#define USB_CH_VBUSDETDBNC			0x01
> +
>  /* UsbLineStatus register bit masks */
>  #define AB8500_USB_LINK_STATUS		0x78
>  #define AB8500_STD_HOST_SUSP		0x18
> @@ -80,6 +85,8 @@
>  /* Step up/down delay in us */
>  #define STEP_UDELAY			1000
>  
> +#define CHARGER_STATUS_POLL 10 /* in ms */
> +
>  /* UsbLineStatus register - usb types */
>  enum ab8500_charger_link_status {
>  	USB_STAT_NOT_CONFIGURED,
> @@ -201,6 +208,10 @@ struct ab8500_charger_usb_state {
>   * @check_usbchgnotok_work:	Work for checking USB charger not ok status
>   * @kick_wd_work:		Work for kicking the charger watchdog in case
>   *				of ABB rev 1.* due to the watchog logic bug
> + * @ac_charger_attached_work:	Work for checking if AC charger is still
> + *				connected
> + * @usb_charger_attached_work:	Work for checking if USB charger is still
> + *				connected
>   * @ac_work:			Work for checking AC charger connection
>   * @detect_usb_type_work:	Work for detecting the USB type connected
>   * @usb_link_status_work:	Work for checking the new USB link status
> @@ -237,6 +248,8 @@ struct ab8500_charger {
>  	struct delayed_work check_hw_failure_work;
>  	struct delayed_work check_usbchgnotok_work;
>  	struct delayed_work kick_wd_work;
> +	struct delayed_work ac_charger_attached_work;
> +	struct delayed_work usb_charger_attached_work;
>  	struct work_struct ac_work;
>  	struct work_struct detect_usb_type_work;
>  	struct work_struct usb_link_status_work;
> @@ -347,6 +360,15 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
>  		dev_dbg(di->dev, "USB connected:%i\n", connected);
>  		di->usb.charger_connected = connected;
>  		sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
> +
> +		if (connected) {
> +			queue_delayed_work(di->charger_wq,
> +					   &di->usb_charger_attached_work,
> +					   HZ);
> +		} else {
> +			cancel_delayed_work_sync
> +					(&di->usb_charger_attached_work);
> +		}

While at it... this seem to be quite inconsistent (sometimes you wrap '('
sometimes not).

>  	}
>  }
>  
> @@ -1703,6 +1725,81 @@ static void ab8500_charger_ac_work(struct work_struct *work)
>  	sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
>  }
>  
> +static void ab8500_charger_usb_attached_work(struct work_struct *work)
> +{
> +	int i;
> +	int ret;
> +	u8 statval;
> +	struct ab8500_charger *di = container_of(work,
> +					 struct ab8500_charger,
> +					 usb_charger_attached_work.work);
> +
> +	for (i = 0 ; i < 10; i++) {

Unneeded space before ';'. Why 10?..

> +		ret = abx500_get_register_interruptible(di->dev,
> +						AB8500_CHARGER,
> +						AB8500_CH_USBCH_STAT1_REG,
> +						&statval);
> +		if (ret < 0) {
> +			dev_err(di->dev, "ab8500 read failed %d\n",
> +				__LINE__);
> +			goto reschedule;
> +		}
> +		if ((statval & (USB_CH_VBUSDROP |
> +				USB_CH_VBUSDETDBNC)) !=
> +		    (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC))
> +			goto reschedule;
> +
> +		msleep(CHARGER_STATUS_POLL);
> +	}
> +
> +	(void) ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);

"(void) " is probably not needed. If the function returns something,
why don't we check the result? Maybe we should turn it into void,
and just print some warning inside it?..

> +
> +	return;
> +reschedule:
> +	queue_delayed_work(di->charger_wq,
> +			   &di->usb_charger_attached_work,
> +			   HZ);
> +}
> +
> +static void ab8500_charger_ac_attached_work(struct work_struct *work)
> +{
> +
> +	int i;
> +	int ret;
> +	u8 statval;
> +	struct ab8500_charger *di = container_of(work,
> +					 struct ab8500_charger,
> +					 ac_charger_attached_work.work);
> +
> +	for (i = 0 ; i < 10; i++) {

unneeded space before ';', mysterious 10.

> +		ret = abx500_get_register_interruptible(di->dev,
> +							AB8500_CHARGER,
> +							AB8500_CH_STATUS2_REG,
> +							&statval);
> +		if (ret < 0) {
> +			dev_err(di->dev, "ab8500 read failed %d\n",
> +				__LINE__);
> +			goto reschedule;
> +		}
> +		if ((statval & (MAIN_CH_STATUS2_MAINCHGDROP |
> +				MAIN_CH_STATUS2_MAINCHARGERDETDBNC)) !=
> +		     (MAIN_CH_STATUS2_MAINCHGDROP |
> +		      MAIN_CH_STATUS2_MAINCHARGERDETDBNC))

This is unreadable. How about

	int mainch = MAIN_CH_STATUS2_MAINCHGDROP |
		     MAIN_CH_STATUS2_MAINCHARGERDETDBNC;

	...
	if ((statval & mainch) != mainch)
		goto reschedule;

> +			goto reschedule;
> +
> +		msleep(CHARGER_STATUS_POLL);
> +	}
> +
> +	(void) ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);

(void) unneeded.

[...]
> +	INIT_DELAYED_WORK(&di->ac_charger_attached_work,
> +			  ab8500_charger_ac_attached_work);
> +	INIT_DELAYED_WORK(&di->usb_charger_attached_work,
> +			  ab8500_charger_usb_attached_work);
> +

The amount of workqueues in this driver makes me wonder: does it make
sense to just move things into one workqueue, name it
ab8500_charger_update(), and so it will work as finite-state-machine,
managing all the charging states?

I'm not saying that you should change it in this series, but please do
consider this for the future at least, because the current logic would be
hard to impossible to debug, and it adds lots of workqueue churn.

>  	/*
>  	 * 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
> @@ -2826,6 +2933,19 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
>  
>  	platform_set_drvdata(pdev, di);
>  
> +	ch_stat = ab8500_charger_detect_chargers(di);
> +
> +	if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
> +		queue_delayed_work(di->charger_wq,
> +					   &di->ac_charger_attached_work,
> +					   HZ);
> +	}
> +	if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
> +		queue_delayed_work(di->charger_wq,
> +					   &di->usb_charger_attached_work,
> +					   HZ);
> +	}
> +
>  	return ret;
>  
>  free_irq:
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 08/57] power: ab8500_fg: flush sync on suspend
  2012-09-25 16:12 ` [PATCH 08/57] power: ab8500_fg: flush sync on suspend mathieu.poirier
@ 2012-09-27  3:11   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:11 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:05AM -0600, mathieu.poirier@linaro.org wrote:
> From: Jonas Aaberg <jonas.aberg@stericsson.com>
> 
> Do flush sync on the fg workqueue at suspend instead of
> just flushing it.

OK, but why? I guess I know why you're doing it, but still...

> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 342d118..1e02b00 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -2437,7 +2437,7 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
>  {
>  	struct ab8500_fg *di = platform_get_drvdata(pdev);
>  
> -	flush_delayed_work(&di->fg_periodic_work);
> +	flush_delayed_work_sync(&di->fg_periodic_work);
>  
>  	/*
>  	 * If the FG is enabled we will disable it before going to suspend
> -- 
> 1.7.5.4
> 
> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 11/57] power: Recharge condition not optimal for battery
  2012-09-25 16:12 ` [PATCH 11/57] power: Recharge condition not optimal for battery mathieu.poirier
@ 2012-09-27  3:29   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:29 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:08AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> Today the battery recharge is determined with a voltage threshold. This
> voltage threshold is only valid when the battery is relaxed. In charging
> algorithm the voltage read is the loaded battery voltage and no
> compensation is done to get the relaxed voltage. When maintenance
> charging is not selected, this makes the recharging condition to almost
> immediately activate when there is a discharge present on the battery.
> 
> Depending on which vendor the battery comes from this behavior can wear
> out the battery much faster than normal.
> 
> The fuelgauge driver is responsible to monitor the actual battery
> capacity and is able to estimate the remaining capacity. It is better to
> use the remaining capacity as a limit to determine when battery should
> be recharged.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
[...]
> @@ -2092,6 +2212,9 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
>  						break;
>  					di->flags.charging = false;
>  					di->flags.fully_charged = false;
> +					if (di->bat->capacity_scaling)
> +						ab8500_fg_update_cap_scalers
> +									(di);

The fact that you need to wrap lines in this strange way is just an
evidence that this function needs some refactorings to lower indentation
level.

[...]
> +		} else {
> +			/* Maintenance A */
> +			if (di->maint_state == MAINT_A &&
> +					di->batt_data.volt <
> +					di->bat->bat_type[di->bat->batt_id].
> +					maint_a_vol_lvl) {
> +				abx500_chargalg_state_to(di,
> +						STATE_MAINTENANCE_A_INIT);
> +			}
> +			/* Maintenance B */
> +			else if (di->maint_state == MAINT_B &&
> +					di->batt_data.volt <
> +					di->bat->bat_type[di->bat->batt_id].
> +					maint_b_vol_lvl)  {
> +				abx500_chargalg_state_to(di,
> +						STATE_MAINTENANCE_B_INIT);
> +			}

I understand that the logic is complex, but things are really really
unreadable and incomprehensible.

Can we make it more nicer by putting some love into the code:

struct something_bat *bat = di->bat;
int maint_a_volt = bat->bat_type[bat->batt_id].maint_a_vol_lvl;
int maint_b_volt = bat->bat_type[bat->batt_id].maint_b_vol_lvl;
int cur_volt = di->batt_data.volt;
bool maint_a = di->maint_state == MAINT_A;
bool maint_b = di->maint_state == MAINT_B;
int state;

if (maint_a && cur_volt < maint_a_volt)
	state = STATE_MAINTENANCE_A_INIT;
else if (maint_b && cur_volt < main_b_volt)
	state = STATE_MAINTENANCE_B_INIT;
else
	break;

abx500_chargalg_state_to(di, state);

(And this is across all the driver...)

At least this way I can actually understand what it does (and without
comments).

I'm OK with a patch on top of this series (since rebasing the whole thing
on top of the changed code would probably create tons of conflicts), but
something has to be done with this.

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt
  2012-09-25 16:12 ` [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt mathieu.poirier
@ 2012-09-27  3:32   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:32 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:10AM -0600, mathieu.poirier@linaro.org wrote:
> From: Hakan Berg <hakan.berg@stericsson.com>
> 
> Ignore the low btemp interrupts for ab8500 3.0 and 3.3
> 
> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_btemp.c      |   22 +++++++++++-----------
>  include/linux/mfd/abx500/ab8500.h |    5 +++++
>  2 files changed, 16 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index 45b10ad..04f9dec 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -622,9 +622,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
>  {
>  	struct ab8500_btemp *di = _di;
>  
> -	if (is_ab8500_2p0_or_earlier(di->parent)) {
> -		dev_dbg(di->dev, "Ignore false btemp low irq"
> -			" for ABB cut 1.0, 1.1 and 2.0\n");
> +	if (is_ab8500_3p3_or_earlier(di->parent)) {
> +		dev_dbg(di->dev, "Ignore false btemp low irq");
> +		dev_dbg(di->dev, " for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
>  	} else {
>  		dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
>  
> @@ -738,30 +738,30 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
>  	int temp = 0;
>  
>  	/*
> -	 * The BTEMP events are not reliabe on AB8500 cut2.0
> +	 * The BTEMP events are not reliabe on AB8500 cut3.3
>  	 * and prior versions
>  	 */
> -	if (is_ab8500_2p0_or_earlier(di->parent)) {
> +	if (is_ab8500_3p3_or_earlier(di->parent)) {

OK, this change reflects patch description.

>  		temp = di->bat_temp * 10;
>  	} else {
>  		if (di->events.btemp_low) {
>  			if (temp > di->btemp_ranges.btemp_low_limit)
> -				temp = di->btemp_ranges.btemp_low_limit;
> +				temp = di->btemp_ranges.btemp_low_limit * 10;

But why these changes?

>  			else
>  				temp = di->bat_temp * 10;
>  		} else if (di->events.btemp_high) {
>  			if (temp < di->btemp_ranges.btemp_high_limit)
> -				temp = di->btemp_ranges.btemp_high_limit;
> +				temp = di->btemp_ranges.btemp_high_limit * 10;
>  			else
>  				temp = di->bat_temp * 10;
>  		} else if (di->events.btemp_lowmed) {
>  			if (temp > di->btemp_ranges.btemp_med_limit)
> -				temp = di->btemp_ranges.btemp_med_limit;
> +				temp = di->btemp_ranges.btemp_med_limit * 10;
>  			else
>  				temp = di->bat_temp * 10;
>  		} else if (di->events.btemp_medhigh) {
>  			if (temp < di->btemp_ranges.btemp_med_limit)
> -				temp = di->btemp_ranges.btemp_med_limit;
> +				temp = di->btemp_ranges.btemp_med_limit * 10;
>  			else
>  				temp = di->bat_temp * 10;
>  		} else
> @@ -1026,8 +1026,8 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
>  		ab8500_btemp_periodic_work);
>  
>  	/* Set BTEMP thermal limits. Low and Med are fixed */
> -	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
> -	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
> +	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT * 10;
> +	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT * 10;
>  
>  	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>  		AB8500_BTEMP_HIGH_TH, &val);
> diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
> index db8a1e3..087b445 100644
> --- a/include/linux/mfd/abx500/ab8500.h
> +++ b/include/linux/mfd/abx500/ab8500.h
> @@ -340,6 +340,11 @@ static inline int is_ab8500_2p0_or_earlier(struct ab8500 *ab)
>  	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT2P0));
>  }
>  
> +static inline int is_ab8500_3p3_or_earlier(struct ab8500 *ab)
> +{
> +	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT3P3));
> +}
> +
>  /* exclude also ab8505, ab9540... */
>  static inline int is_ab8500_2p0(struct ab8500 *ab)
>  {
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV
  2012-09-25 16:12 ` [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV mathieu.poirier
@ 2012-09-27  3:36   ` Anton Vorontsov
  2012-09-28 18:24     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:36 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:14AM -0600, mathieu.poirier@linaro.org wrote:
> From: Hakan Berg <hakan.berg@stericsson.com>
> 
> Add support for the battery over-voltage situation
> 
> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |   32 ++++++++++++++++----------------
>  1 files changed, 16 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index c4d9307..8507254 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -1842,24 +1842,26 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
>  	 * If we have had a battery over-voltage situation,
>  	 * check ovv-bit to see if it should be reset.
>  	 */
> -	if (di->flags.bat_ovv) {
> -		ret = abx500_get_register_interruptible(di->dev,
> -			AB8500_CHARGER, AB8500_CH_STAT_REG,
> -			&reg_value);
> -		if (ret < 0) {
> -			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
> -			return;
> -		}
> -		if ((reg_value & BATT_OVV) != BATT_OVV) {
> -			dev_dbg(di->dev, "Battery recovered from OVV\n");
> -			di->flags.bat_ovv = false;
> +	ret = abx500_get_register_interruptible(di->dev,
> +		AB8500_CHARGER, AB8500_CH_STAT_REG,
> +		&reg_value);
> +	if (ret < 0) {
> +		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
> +		return;
> +	}
> +	if ((reg_value & BATT_OVV) == BATT_OVV) {
> +		if (!di->flags.bat_ovv) {
> +			dev_dbg(di->dev, "Battery OVV\n");
> +			di->flags.bat_ovv = true;
>  			power_supply_changed(&di->fg_psy);
> -			return;
>  		}
> -
>  		/* Not yet recovered from ovv, reschedule this test */
>  		queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
> -				   round_jiffies(HZ));
> +			HZ);

Why this change? I.e. round_jiffies(HZ) -> HZ?

Yes, it seems like round_jiffies(HZ) is not needed since HZ itself is a
full second.. But the change itself does not belong to this patch.

> +		} else {
> +			dev_dbg(di->dev, "Battery recovered from OVV\n");
> +			di->flags.bat_ovv = false;
> +			power_supply_changed(&di->fg_psy);
>  	}
>  }
>  
> @@ -2039,8 +2041,6 @@ static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
>  	struct ab8500_fg *di = _di;
>  
>  	dev_dbg(di->dev, "Battery OVV\n");
> -	di->flags.bat_ovv = true;
> -	power_supply_changed(&di->fg_psy);
>  
>  	/* Schedule a new HW failure check */
>  	queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 00/57] power: Upgrade to ux500 battery management driver
  2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
                   ` (56 preceding siblings ...)
  2012-09-25 16:12 ` [PATCH 57/57] power: ab8500_charger: Limit USB charger current mathieu.poirier
@ 2012-09-27  3:38 ` Anton Vorontsov
  2012-09-27 22:08   ` Mathieu Poirier
  57 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  3:38 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:11:57AM -0600, mathieu.poirier@linaro.org wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
> 
> This patch set upgrades the current ux500 battery management driver
> to the latest HW and functionality.
> 
> Pull request for convenience:
> 
> The following changes since commit 56d27adcb536b7430d5f8a6240df8ad261eb00bd:
> 
>   Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile (2012-09-24 16:17:17 -0700)
> 
> are available in the git repository at:
> 
>   git://git.linaro.org/people/mpoirier/linux.git ux500-battery-management

I reviewed this series before, and all looked quite good overall. But I
took another look, especially at the patches w/ empty commit messages, and
there are some issues.

I tried to write some descriptions myself, but I failed: some changes are
not obvious to me, so I couldn't write rationale for them. :-/

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 18/57] power: Add sysfs interfaces for capacity
  2012-09-25 16:12 ` [PATCH 18/57] power: Add sysfs interfaces for capacity mathieu.poirier
@ 2012-09-27  7:08   ` Anton Vorontsov
  2012-09-28 18:26     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:08 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:15AM -0600, mathieu.poirier@linaro.org wrote:
> From: Daniel WILLERUD <daniel.willerud@stericsson.com>
> 
> Switchable depending on whether capacity scaling is enabled
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Daniel WILLERUD <daniel.willerud@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |   57 ++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 56 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 8507254..46010ec 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -266,7 +266,6 @@ static enum power_supply_property ab8500_fg_props[] = {
>  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
>  	POWER_SUPPLY_PROP_CHARGE_FULL,
>  	POWER_SUPPLY_PROP_CHARGE_NOW,
> -	POWER_SUPPLY_PROP_CAPACITY,
[...]
> +static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
> +	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
> +};

I don't understand the rationale behind this patch. Why remove normal
power supply property, and make your own with the same name? Something
isn't right...

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 20/57] power: Adds support for legacy USB chargers
  2012-09-25 16:12 ` [PATCH 20/57] power: Adds support for legacy USB chargers mathieu.poirier
@ 2012-09-27  7:15   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:15 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:17AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> A Legacy USB charger should be handled directly by the charger
> driver.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c |   66 ++++++++++++++++++++++++++++++++++-----
>  1 files changed, 57 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 4155feb..7cd4165 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -85,6 +85,9 @@
>  /* Step up/down delay in us */
>  #define STEP_UDELAY			1000
>  
> +/* Wait for enumeration before charging in ms */
> +#define WAIT_FOR_USB_ENUMERATION	(5 * 1000)
> +
>  #define CHARGER_STATUS_POLL 10 /* in ms */
>  
>  /* UsbLineStatus register - usb types */
> @@ -198,6 +201,7 @@ struct ab8500_charger_usb_state {
>   *			charger is enabled
>   * @vbat		Battery voltage
>   * @old_vbat		Previously measured battery voltage
> + * @usb_device_is_unrecognised	USB device is unrecognised by the hardware
>   * @autopower		Indicate if we should have automatic pwron after pwrloss
>   * @parent:		Pointer to the struct ab8500
>   * @gpadc:		Pointer to the struct gpadc
> @@ -216,6 +220,7 @@ struct ab8500_charger_usb_state {
>   * @check_usbchgnotok_work:	Work for checking USB charger not ok status
>   * @kick_wd_work:		Work for kicking the charger watchdog in case
>   *				of ABB rev 1.* due to the watchog logic bug
> + * @attach_work:		Work for checking the usb enumeration
>   * @ac_charger_attached_work:	Work for checking if AC charger is still
>   *				connected
>   * @usb_charger_attached_work:	Work for checking if USB charger is still
> @@ -239,6 +244,7 @@ struct ab8500_charger {
>  	bool vddadc_en_usb;
>  	int vbat;
>  	int old_vbat;
> +	bool usb_device_is_unrecognised;
>  	bool autopower;
>  	struct ab8500 *parent;
>  	struct ab8500_gpadc *gpadc;
> @@ -256,6 +262,7 @@ struct ab8500_charger {
>  	struct delayed_work check_hw_failure_work;
>  	struct delayed_work check_usbchgnotok_work;
>  	struct delayed_work kick_wd_work;
> +	struct delayed_work attach_work;
>  	struct delayed_work ac_charger_attached_work;
>  	struct delayed_work usb_charger_attached_work;

Quite a lot of these. I doubt that the driver really needs all this stuff
executed in parallel. I guess something is wrong with the driver design.

Maybe there should be just one delayed work: you can probably queue it
early from the irq handler, and do all the work there...

>  	struct work_struct ac_work;
> @@ -588,6 +595,8 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  {
>  	int ret = 0;
>  
> +	di->usb_device_is_unrecognised = false;
> +
>  	switch (link_status) {
>  	case USB_STAT_STD_HOST_NC:
>  	case USB_STAT_STD_HOST_C_NS:
> @@ -633,9 +642,15 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>  				di->max_usb_in_curr);
>  		break;
> +	case USB_STAT_NOT_CONFIGURED:
> +		if (di->vbus_detected) {
> +			di->usb_device_is_unrecognised = true;
> +			dev_dbg(di->dev, "USB Type - Legacy charger.\n");
> +			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> +			break;
> +		}
>  	case USB_STAT_HM_IDGND:
>  	case USB_STAT_NOT_VALID_LINK:
> -	case USB_STAT_NOT_CONFIGURED:
>  		dev_err(di->dev, "USB Type - Charging not allowed\n");
>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
>  		ret = -ENXIO;
> @@ -1899,6 +1914,30 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>  }
>  
>  /**
> + * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
> + * @work:	pointer to the work_struct structure
> + *
> + * Detect the type of USB plugged
> + */
> +static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
> +{
> +	int ret;
> +

This empty line is not needed.

> +	struct ab8500_charger *di = container_of(work,
> +		struct ab8500_charger, attach_work.work);
> +
> +	/* Update maximum input current if USB enumeration is not detected */
> +	if (!di->usb.charger_online) {
> +		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
> +		if (ret)
> +			return;
> +	}
> +
> +	ab8500_charger_set_usb_connected(di, true);
> +	ab8500_power_supply_changed(di, &di->usb_chg.psy);
> +}
> +
> +/**
>   * ab8500_charger_usb_link_status_work() - work to detect USB type
>   * @work:	pointer to the work_struct structure
>   *
> @@ -1928,14 +1967,20 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  		di->vbus_detected = 1;
>  		ret = ab8500_charger_read_usb_type(di);
>  		if (!ret) {
> -			/* Update maximum input current */
> -			ret = ab8500_charger_set_vbus_in_curr(di,
> -					di->max_usb_in_curr);
> -			if (ret)
> -				return;
> -
> -			ab8500_charger_set_usb_connected(di, true);
> -			ab8500_power_supply_changed(di, &di->usb_chg.psy);
> +			if (di->usb_device_is_unrecognised) {
> +				dev_dbg(di->dev,
> +					"Potential Legacy Charger device. "
> +					"Delay work for %d msec for USB enum "
> +					"to finish",
> +					WAIT_FOR_USB_ENUMERATION);
> +				queue_delayed_work(di->charger_wq,
> +					&di->attach_work,
> +					msecs_to_jiffies
> +						(WAIT_FOR_USB_ENUMERATION));

Very weird way to wrap lines. I'd prefer "(" to stay on the previous line.

I also think that it's possible to refactor this function to make it less
indented.

> +			} else {
> +				queue_delayed_work(di->charger_wq,
> +							&di->attach_work, 0);
> +			}
>  		} else if (ret == -ENXIO) {
>  			/* No valid charger type detected */
>  			ab8500_charger_set_usb_connected(di, false);
> @@ -2886,6 +2931,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(&di->attach_work,
> +		ab8500_charger_usb_link_attach_work);

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 27/57] power: sysfs interface update
  2012-09-25 16:12 ` [PATCH 27/57] power: sysfs interface update mathieu.poirier
@ 2012-09-27  7:20   ` Anton Vorontsov
  2012-09-28 18:26     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:20 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:24AM -0600, mathieu.poirier@linaro.org wrote:
> From: Michel JAOUEN <michel.jaouen@stericsson.com>
> 
> Add new sysfs interface to get current charge status
> 
> Signed-off-by: Michel JAOUEN <michel.jaouen@stericsson.com>
> Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c  |    3 +++
>  drivers/power/abx500_chargalg.c |   24 +++++++++++++++++++++++-
>  2 files changed, 26 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 4129599..0a781a0 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -2759,6 +2759,9 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
>  	enum ab8500_usb_state bm_usb_state;
>  	unsigned mA = *((unsigned *)power);
>  
> +	if (di == NULL)
> +		return NOTIFY_DONE;
> +

I'd write !di.

[...]
> +static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
> +	struct attribute *attr, char *buf)
> +{
> +	struct abx500_chargalg *di = container_of(kobj,
> +		struct abx500_chargalg, chargalg_kobject);
> +
> +	if ((di->susp_status.ac_suspended == true) &&
> +		(di->susp_status.usb_suspended == true))
> +		return sprintf(buf, "0\n");
> +	else
> +		return sprintf(buf, "1\n");

just

return sprintf(buf, "%d\n", di->susp_status.ac_suspended &&
			    di->susp_status.usb_suspended);

> +}
> +
>  /* Exposure to the sysfs interface */
>  
>  /**
> @@ -1749,7 +1770,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
>  static struct attribute abx500_chargalg_en_charger = \
>  {
>  	.name = "chargalg",
> -	.mode = S_IWUGO,
> +	.mode = S_IRUGO | S_IWUSR,
>  };
>  
>  static struct attribute *abx500_chargalg_chg[] = {
> @@ -1758,6 +1779,7 @@ static struct attribute *abx500_chargalg_chg[] = {
>  };
>  
>  static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
> +	.show = abx500_chargalg_sysfs_show,
>  	.store = abx500_chargalg_sysfs_charger,
>  };
>  
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 30/57] power: ab8500: Flush & sync all works
  2012-09-25 16:12 ` [PATCH 30/57] power: ab8500: Flush & sync all works mathieu.poirier
@ 2012-09-27  7:23   ` Anton Vorontsov
  2012-09-28 18:28     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:23 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:27AM -0600, mathieu.poirier@linaro.org wrote:
> From: Jonas Aaberg <jonas.aberg@stericsson.com>
> 
> Flush and sync all workqueues at suspend to avoid
> that we suspend in the middle of a work.
> 
> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c |   11 +++++++++++
>  drivers/power/ab8500_fg.c      |    5 +++++
>  2 files changed, 16 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index ee5ad7b..071c7c2 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -2862,6 +2862,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>  	if (delayed_work_pending(&di->check_hw_failure_work))
>  		cancel_delayed_work(&di->check_hw_failure_work);
>  
> +	flush_delayed_work_sync(&di->attach_work);
> +	flush_delayed_work_sync(&di->usb_charger_attached_work);
> +	flush_delayed_work_sync(&di->ac_charger_attached_work);
> +	flush_delayed_work_sync(&di->check_usbchgnotok_work);
> +	flush_delayed_work_sync(&di->check_vbat_work);
> +	flush_delayed_work_sync(&di->kick_wd_work);
> +
> +	flush_work_sync(&di->usb_link_status_work);
> +	flush_work_sync(&di->ac_work);
> +	flush_work_sync(&di->detect_usb_type_work);

I belive each of these have to be added by the patches that add the
appropriate work structs. But really, it's better to avoid these many
delayed work.

>  	return 0;
>  }
>  #else
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index e7a0e1f..0e71e7e 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -2626,6 +2626,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
>  	struct ab8500_fg *di = platform_get_drvdata(pdev);
>  
>  	flush_delayed_work_sync(&di->fg_periodic_work);
> +	flush_work_sync(&di->fg_work);
> +	flush_work_sync(&di->fg_acc_cur_work);
> +	flush_delayed_work_sync(&di->fg_reinit_work);
> +	flush_delayed_work_sync(&di->fg_low_bat_work);
> +	flush_delayed_work_sync(&di->fg_check_hw_failure_work);
>  
>  	/*
>  	 * If the FG is enabled we will disable it before going to suspend
> -- 
> 1.7.5.4
> 
> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design
  2012-09-25 16:12 ` [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design mathieu.poirier
@ 2012-09-27  7:27   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:27 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:28AM -0600, mathieu.poirier@linaro.org wrote:
> From: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
> 
> If battery is not identified while fg probe, mah_max_design gets
> initialized with unknown battery's charge full design. Reinitialize
> mah_max_design if battery is identified after fg probe.
> 
> Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
> Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
> Reviewed-by: Rupesh KUMAR <rupesh.kumar@stericsson.com>
> Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 0e71e7e..5e4a46b 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -2258,9 +2258,9 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
>  		case POWER_SUPPLY_PROP_TECHNOLOGY:
>  			switch (ext->type) {
>  			case POWER_SUPPLY_TYPE_BATTERY:
> -				if (!di->flags.batt_id_received) {
> +				if (!di->flags.batt_id_received &&
> +					di->bat->batt_id != BATTERY_UNKNOWN) {

Wrong indentation...

Yes, it will no longer fit into 80 columns, which probably means that the
code gone out of control, and need some refactorings before adding new
stuff.

>  					const struct abx500_battery_type *b;
> -
>  					b = &(di->bat->bat_type[di->bat->batt_id]);
>  
>  					di->flags.batt_id_received = true;
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 33/57] power: u8500_charger: Delay for USB enumeration
  2012-09-25 16:12 ` [PATCH 33/57] power: u8500_charger: Delay for USB enumeration mathieu.poirier
@ 2012-09-27  7:42   ` Anton Vorontsov
  2012-09-28 16:56     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-27  7:42 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:30AM -0600, mathieu.poirier@linaro.org wrote:
> From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
> 
> If charging is started before USB enumeration of an
> Accessory Charger Adapter has finished, the AB8500 will
> generate a VBUS_ERROR. This in turn results in timeouts
> and delays the enumeration with around 15 seconds.
> This patch delays the charging and then ramps currents
> slowly to avoid VBUS errors. The delay allows the enumeration
> to have finished before charging is turned on.
> 
> Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
[...]
> @@ -264,17 +275,19 @@ struct ab8500_charger {
>  	struct ab8500_charger_info usb;
>  	struct regulator *regu;
>  	struct workqueue_struct *charger_wq;
> +	struct mutex usb_ipt_crnt_lock;
>  	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;
> +	struct delayed_work usb_state_changed_work;
>  	struct delayed_work attach_work;
>  	struct delayed_work ac_charger_attached_work;
>  	struct delayed_work usb_charger_attached_work;
> +	struct delayed_work vbus_drop_end_work;
>  	struct work_struct ac_work;
>  	struct work_struct detect_usb_type_work;
>  	struct work_struct usb_link_status_work;
> -	struct work_struct usb_state_changed_work;

This just moves line around. Doesn't belong to this patch.

>  	struct work_struct check_main_thermal_prot_work;
>  	struct work_struct check_usb_thermal_prot_work;
>  	struct usb_phy *usb_phy;
> @@ -560,6 +573,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
>  /**
>   * ab8500_charger_detect_chargers() - Detect the connected chargers
>   * @di:		pointer to the ab8500_charger structure
> + * @probe:     if probe, don't delay and wait for HW
>   *
>   * Returns the type of charger connected.
>   * For USB it will not mean we can actually charge from it
> @@ -570,10 +584,10 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
>   * Returns an integer value, that means,
>   * NO_PW_CONN  no power supply is connected
>   * AC_PW_CONN  if the AC power supply is connected
> - * USB_PW_CONN  if the USB power supply is connected
> + * USB_PW_CONN	if the USB power supply is connected

Cosmetic change... doesn't belong to this patch, I guess.

>   * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
>   */
> -static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
> +static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
>  {
>  	int result = NO_PW_CONN;
>  	int ret;
> @@ -591,6 +605,15 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
>  		result = AC_PW_CONN;
>  
>  	/* Check for USB charger */
> +	if (!probe) {
> +		/*
> +		 * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
> +		 * when disconnecting ACA even though no
> +		 * charger was connected. Try waiting a little
> +		 * longer than the 100 ms of VBUS_DET_DBNC100...
> +		 */
> +		msleep(110);
> +	}
>  	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>  		AB8500_CH_USBCH_STAT1_REG, &val);
>  	if (ret < 0) {
> @@ -598,6 +621,9 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
>  		return ret;
>  	}
>  
> +	dev_dbg(di->dev,
> +		"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__, val);
> +
>  	if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
>  		result |= USB_PW_CONN;
>  
> @@ -620,33 +646,47 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  
>  	di->usb_device_is_unrecognised = false;
>  
> +	/*
> +	 * Platform only supports USB 2.0.
> +	 * This means that charging current from USB source
> +	 * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
> +	 * should set USB_CH_IP_CUR_LVL_0P5.
> +	 */
> +
>  	switch (link_status) {
>  	case USB_STAT_STD_HOST_NC:
>  	case USB_STAT_STD_HOST_C_NS:
>  	case USB_STAT_STD_HOST_C_S:
>  		dev_dbg(di->dev, "USB Type - Standard host is ");
>  		dev_dbg(di->dev, "detected through USB driver\n");
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->is_usb_host = true;
> +		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_HOST_CHG_HS_CHIRP:
>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> +		di->is_usb_host = true;
> +		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_HOST_CHG_HS:
> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->is_usb_host = true;
> +		di->is_aca_rid = 0;
> +		break;
>  	case USB_STAT_ACA_RID_C_HS:
>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> +		di->is_usb_host = false;
> +		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_ACA_RID_A:
>  		/*
>  		 * Dedicated charger level minus maximum current accessory
> -		 * can consume (300mA). Closest level is 1100mA
> +		 * can consume (900mA). Closest level is 500mA
>  		 */
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> +		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->is_usb_host = false;
> +		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_ACA_RID_B:
>  		/*
> @@ -656,14 +696,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
>  		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>  				di->max_usb_in_curr);
> +		di->is_usb_host = false;
> +		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_HOST_CHG_NM:
> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->is_usb_host = true;
> +		di->is_aca_rid = 0;
> +		break;
>  	case USB_STAT_DEDICATED_CHG:
> -	case USB_STAT_ACA_RID_C_NM:
> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> +		di->is_usb_host = false;
> +		di->is_aca_rid = 0;
> +		break;
>  	case USB_STAT_ACA_RID_C_HS_CHIRP:
> +		case USB_STAT_ACA_RID_C_NM:
>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> +		di->is_usb_host = false;
> +		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_NOT_CONFIGURED:
>  		if (di->vbus_detected) {
> @@ -780,6 +830,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
>  		ret = abx500_get_register_interruptible(di->dev,
>  			AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
>  			&val);
> +		dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
> +							 __func__, val);
>  		if (ret < 0) {
>  			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>  			return ret;
> @@ -795,6 +847,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
>  			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>  			return ret;
>  		}
> +		dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n",
> +							 __func__, val);
>  		/*
>  		 * Until the IT source register is read the UsbLineStatus
>  		 * register is not updated, hence doing the same
> @@ -1054,69 +1108,128 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
>  static int ab8500_charger_set_current(struct ab8500_charger *di,
>  	int ich, int reg)
>  {
> -	int ret, i;
> -	int curr_index, prev_curr_index, shift_value;
> +	int ret = 0;

= 0 initializer is not needed.

> +	int auto_curr_index, curr_index, prev_curr_index, shift_value, i;

Should be one variable definition per line.

>  	u8 reg_value;
> +	u32 step_udelay;
> +	bool no_stepping = false;
> +
> +	atomic_inc(&di->current_stepping_sessions);
> +
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
> +		reg, &reg_value);
> +	if (ret < 0) {
> +		dev_err(di->dev, "%s read failed\n", __func__);
> +		goto exit_set_current;
> +	}
>  
>  	switch (reg) {
>  	case AB8500_MCH_IPT_CURLVL_REG:
>  		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
> +		prev_curr_index = (reg_value >> shift_value);
>  		curr_index = ab8500_current_to_regval(ich);
> +		step_udelay = STEP_UDELAY;
> +		if (!di->ac.charger_connected)
> +			no_stepping = true;
>  		break;
>  	case AB8500_USBCH_IPT_CRNTLVL_REG:
>  		shift_value = VBUS_IN_CURR_LIM_SHIFT;
> +		prev_curr_index = (reg_value >> shift_value);
>  		curr_index = ab8500_vbus_in_curr_to_regval(ich);
> +		step_udelay = STEP_UDELAY * 100;
> +
> +		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
> +					AB8500_CH_USBCH_STAT2_REG, &reg_value);
> +
> +		if (ret < 0) {
> +			dev_err(di->dev, "%s read failed\n", __func__);
> +			goto exit_set_current;
> +		}
> +		auto_curr_index =
> +			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;

This fits into one line ,no need for line wrap.

> +
> +		dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
> +			__func__,

__func__ can go into previous line.

> +			ab8500_charger_vbus_in_curr_map[auto_curr_index]);
> +
> +		prev_curr_index = min(prev_curr_index, auto_curr_index);
> +
> +		if (!di->usb.charger_connected)
> +			no_stepping = true;
>  		break;
>  	case AB8500_CH_OPT_CRNTLVL_REG:
>  		shift_value = 0;
> +		prev_curr_index = (reg_value >> shift_value);
>  		curr_index = ab8500_current_to_regval(ich);
> +		if (curr_index == 0)
> +			step_udelay = STEP_UDELAY;
> +		else if ((curr_index - prev_curr_index) > 1)
> +			step_udelay = STEP_UDELAY * 100;
> +		else
> +			step_udelay = STEP_UDELAY;
		
Just

		step_udelay = STEP_UDELAY;
		if (curr_index && (curr_index - prev_curr_index) > 1)
			step_udelay *= 100;

> +
> +		if (!di->usb.charger_connected && !di->ac.charger_connected)
> +			no_stepping = true;
> +
>  		break;
>  	default:
>  		dev_err(di->dev, "%s current register not valid\n", __func__);
> -		return -ENXIO;
> +		ret = -ENXIO;
> +		goto exit_set_current;
>  	}
>  
>  	if (curr_index < 0) {
>  		dev_err(di->dev, "requested current limit out-of-range\n");
> -		return -ENXIO;
> -	}
> -
> -	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
> -		reg, &reg_value);
> -	if (ret < 0) {
> -		dev_err(di->dev, "%s read failed\n", __func__);
> -		return ret;
> +		ret = -ENXIO;
> +		goto exit_set_current;
>  	}
> -	prev_curr_index = (reg_value >> shift_value);
>  
>  	/* only update current if it's been changed */
> -	if (prev_curr_index == curr_index)
> -		return 0;
> +	if (prev_curr_index == curr_index) {
> +		dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
> +			__func__, reg);
> +		ret = 0;
> +		goto exit_set_current;
> +	}
>  
>  	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
>  		__func__, ich, reg);
>  
> -	if (prev_curr_index > curr_index) {
> +	if (no_stepping) {
> +		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> +					reg, (u8) curr_index << shift_value);

No space before curr_index.

> +		if (ret)
> +			dev_err(di->dev, "%s write failed\n", __func__);
> +	} else if (prev_curr_index > curr_index) {
>  		for (i = prev_curr_index - 1; i >= curr_index; i--) {
> +			dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
> +				(u8) i << shift_value, reg);

ditto.

>  			ret = abx500_set_register_interruptible(di->dev,
>  				AB8500_CHARGER, reg, (u8) i << shift_value);
>  			if (ret) {
>  				dev_err(di->dev, "%s write failed\n", __func__);
> -				return ret;
> +				goto exit_set_current;
>  			}
> -			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
> +			if (i != curr_index)
> +				usleep_range(step_udelay, step_udelay * 2);
>  		}
>  	} else {
>  		for (i = prev_curr_index + 1; i <= curr_index; i++) {
> +			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
> +				(u8) i << shift_value, reg);

ditto.

>  			ret = abx500_set_register_interruptible(di->dev,
>  				AB8500_CHARGER, reg, (u8) i << shift_value);
>  			if (ret) {
>  				dev_err(di->dev, "%s write failed\n", __func__);
> -				return ret;
> +				goto exit_set_current;
>  			}
> -			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
> +			if (i != curr_index)
> +				usleep_range(step_udelay, step_udelay * 2);
>  		}
>  	}
> +
> +exit_set_current:
> +	atomic_dec(&di->current_stepping_sessions);
>  	return ret;
>  }
>  
> @@ -1132,6 +1245,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>  		int ich_in)
>  {
>  	int min_value;
> +	int ret;
>  
>  	/* We should always use to lowest current limit */
>  	min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
> @@ -1149,8 +1263,14 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>  		break;
>  	}
>  
> -	return ab8500_charger_set_current(di, min_value,
> +	dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
> +
> +	mutex_lock(&di->usb_ipt_crnt_lock);
> +	ret = ab8500_charger_set_current(di, min_value,
>  		AB8500_USBCH_IPT_CRNTLVL_REG);
> +	mutex_unlock(&di->usb_ipt_crnt_lock);
> +
> +	return ret;
>  }
>  
>  /**
> @@ -1460,25 +1580,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  			dev_err(di->dev, "%s write failed\n", __func__);
>  			return ret;
>  		}
> -		/* USBChInputCurr: current that can be drawn from the usb */
> -		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
> -		if (ret) {
> -			dev_err(di->dev, "setting USBChInputCurr failed\n");
> -			return ret;
> -		}
> -		/* ChOutputCurentLevel: protected output current */
> -		ret = ab8500_charger_set_output_curr(di, ich_out);
> -		if (ret) {
> -			dev_err(di->dev,
> -			 "%s Failed to set ChOutputCurentLevel\n",
> -			 __func__);
> -			return ret;
> -		}
>  		/* Check if VBAT overshoot control should be enabled */
>  		if (!di->bat->enable_overshoot)
>  			overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
>  
>  		/* Enable USB Charger */
> +		dev_dbg(di->dev,
> +			"Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
>  		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
>  			AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
>  		if (ret) {
> @@ -1491,11 +1599,30 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  		if (ret < 0)
>  			dev_err(di->dev, "failed to enable LED\n");
>  
> +		di->usb.charger_online = 1;
> +
> +		/* USBChInputCurr: current that can be drawn from the usb */
> +		ret = ab8500_charger_set_vbus_in_curr(di,
> +					di->max_usb_in_curr);
> +		if (ret) {
> +			dev_err(di->dev, "setting USBChInputCurr failed\n");
> +			return ret;
> +		}
> +
> +		/* ChOutputCurentLevel: protected output current */
> +		ret = ab8500_charger_set_output_curr(di, ich_out);
> +		if (ret) {
> +			dev_err(di->dev,
> +				"%s Failed to set ChOutputCurentLevel\n",
> +				__func__);
> +			return ret;
> +		}
> +
>  		queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
>  
> -		di->usb.charger_online = 1;
>  	} else {
>  		/* Disable USB charging */
> +		dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
>  		ret = abx500_set_register_interruptible(di->dev,
>  			AB8500_CHARGER,
>  			AB8500_USBCH_CTRL1_REG, 0);
> @@ -1508,7 +1635,21 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  		ret = ab8500_charger_led_en(di, false);
>  		if (ret < 0)
>  			dev_err(di->dev, "failed to disable LED\n");
> +		/* USBChInputCurr: current that can be drawn from the usb */
> +		ret = ab8500_charger_set_vbus_in_curr(di, 0);
> +		if (ret) {
> +			dev_err(di->dev, "setting USBChInputCurr failed\n");
> +			return ret;
> +		}
>  
> +		/* ChOutputCurentLevel: protected output current */
> +		ret = ab8500_charger_set_output_curr(di, 0);
> +		if (ret) {
> +			dev_err(di->dev,
> +				"%s Failed to reset ChOutputCurentLevel\n",
> +				__func__);
> +			return ret;
> +		}
>  		di->usb.charger_online = 0;
>  		di->usb.wd_expired = false;
>  
> @@ -1791,7 +1932,7 @@ static void ab8500_charger_ac_work(struct work_struct *work)
>  	 * synchronously, we have the check if the main charger is
>  	 * connected by reading the status register
>  	 */
> -	ret = ab8500_charger_detect_chargers(di);
> +	ret = ab8500_charger_detect_chargers(di, false);
>  	if (ret < 0)
>  		return;
>  
> @@ -1899,16 +2040,18 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>  	 * synchronously, we have the check if is
>  	 * connected by reading the status register
>  	 */
> -	ret = ab8500_charger_detect_chargers(di);
> +	ret = ab8500_charger_detect_chargers(di, false);
>  	if (ret < 0)
>  		return;
>  
>  	if (!(ret & USB_PW_CONN)) {
> -		di->vbus_detected = 0;
> +		dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
> +		di->vbus_detected = false;
>  		ab8500_charger_set_usb_connected(di, false);
>  		ab8500_power_supply_changed(di, &di->usb_chg.psy);
>  	} else {
> -		di->vbus_detected = 1;
> +		dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
> +		di->vbus_detected = true;
>  
>  		if (is_ab8500_1p1_or_earlier(di->parent)) {
>  			ret = ab8500_charger_detect_usb_type(di);
> @@ -1918,7 +2061,8 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>  							    &di->usb_chg.psy);
>  			}
>  		} else {
> -			/* For ABB cut2.0 and onwards we have an IRQ,
> +			/*
> +			 * For ABB cut2.0 and onwards we have an IRQ,

This change is correct, but it doesn't belong to this patch.

>  			 * USB_LINK_STATUS that will be triggered when the USB
>  			 * link status changes. The exception is USB connected
>  			 * during startup. Then we don't get a
> @@ -1939,7 +2083,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>  }
>  
>  /**
> - * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
> + * ab8500_charger_usb_link_attach_work() - work to detect USB type
>   * @work:	pointer to the work_struct structure
>   *
>   * Detect the type of USB plugged
> @@ -1979,10 +2123,10 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  
>  	/*
>  	 * Since we can't be sure that the events are received
> -	 * synchronously, we have the check if  is
> +	 * synchronously, we have the check if	is

cosmetic change, it's OK, but in separate patch.

>  	 * connected by reading the status register
>  	 */
> -	detected_chargers = ab8500_charger_detect_chargers(di);
> +	detected_chargers = ab8500_charger_detect_chargers(di, false);
>  	if (detected_chargers < 0)
>  		return;
>  
> @@ -2009,7 +2153,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  			abx500_mask_and_set_register_interruptible(di->dev,
>  						AB8500_CHARGER,
>  						AB8500_USBCH_CTRL1_REG,
> -						0x01, 0x01)
> +						0x01, 0x01);

Ouch. Was it compilable before this change? It's not bisectable.

>  			/*Enable charger detection*/
>  			abx500_mask_and_set_register_interruptible(di->dev,
>  						AB8500_USB,
> @@ -2042,32 +2186,46 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  	}
>  
>  	if (!(detected_chargers & USB_PW_CONN)) {
> -		di->vbus_detected = 0;
> +		di->vbus_detected = false;

Nope. Firstly, 0 is OK for bool. Secondly, even if you want to use
false instead of 0, this is cosmetic change, and should go separately.

>  		ab8500_charger_set_usb_connected(di, false);
>  		ab8500_power_supply_changed(di, &di->usb_chg.psy);
> -	} else {
> -		di->vbus_detected = 1;
> -		ret = ab8500_charger_read_usb_type(di);
> -		if (!ret) {
> -			if (di->usb_device_is_unrecognised) {
> -				dev_dbg(di->dev,
> -					"Potential Legacy Charger device. "
> -					"Delay work for %d msec for USB enum "
> -					"to finish",
> -					WAIT_FOR_USB_ENUMERATION);
> -				queue_delayed_work(di->charger_wq,
> -					&di->attach_work,
> -					msecs_to_jiffies
> -						(WAIT_FOR_USB_ENUMERATION));
> -			} else {
> -				queue_delayed_work(di->charger_wq,
> -							&di->attach_work, 0);
> -			}
> -		} else if (ret == -ENXIO) {
> +		return;
> +	}
> +
> +	dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
> +	di->vbus_detected = true;
> +	ret = ab8500_charger_read_usb_type(di);
> +	if (ret) {
> +		if (ret == -ENXIO) {
>  			/* No valid charger type detected */
>  			ab8500_charger_set_usb_connected(di, false);
>  			ab8500_power_supply_changed(di, &di->usb_chg.psy);
>  		}
> +		return;
> +	}
> +
> +	if (di->usb_device_is_unrecognised) {
> +		dev_dbg(di->dev,
> +			"Potential Legacy Charger device. "
> +			"Delay work for %d msec for USB enum "
> +			"to finish",
> +			WAIT_ACA_RID_ENUMERATION);
> +		queue_delayed_work(di->charger_wq,
> +			&di->attach_work,
> +			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
> +	} else if (di->is_aca_rid == 1) {
> +		/* Only wait once */
> +		di->is_aca_rid++;
> +		dev_dbg(di->dev,
> +			"%s Wait %d msec for USB enum to finish",

This can go into previous line.

> +			__func__, WAIT_ACA_RID_ENUMERATION);
> +		queue_delayed_work(di->charger_wq,
> +			&di->attach_work,

These two lines can be merged.

> +			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
> +	} else {
> +		queue_delayed_work(di->charger_wq,
> +			&di->attach_work,
> +			0);

This fits into one line.

>  	}
>  }
>  
> @@ -2077,24 +2235,20 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
>  	unsigned long flags;
>  
>  	struct ab8500_charger *di = container_of(work,
> -		struct ab8500_charger, usb_state_changed_work);
> +		struct ab8500_charger, usb_state_changed_work.work);
>  
> -	if (!di->vbus_detected)
> +	if (!di->vbus_detected) {
> +		dev_dbg(di->dev,
> +			"%s !di->vbus_detected\n",
> +			__func__);

No wrapping necessary.

>  		return;
> +	}
>  
>  	spin_lock_irqsave(&di->usb_state.usb_lock, flags);
> -	di->usb_state.usb_changed = false;
> +	di->usb_state.state = di->usb_state.state_tmp;
> +	di->usb_state.usb_current = di->usb_state.usb_current_tmp;
>  	spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
>  
> -	/*
> -	 * wait for some time until you get updates from the usb stack
> -	 * and negotiations are completed
> -	 */
> -	msleep(250);
> -
> -	if (di->usb_state.usb_changed)
> -		return;
> -
>  	dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
>  		__func__, di->usb_state.state, di->usb_state.usb_current);
>  
> @@ -2332,6 +2486,21 @@ static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
>  	return IRQ_HANDLED;
>  }
>  
> +static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
> +{
> +	struct ab8500_charger *di = container_of(work,
> +		struct ab8500_charger, vbus_drop_end_work.work);
> +
> +	di->flags.vbus_drop_end = false;
> +
> +	/* Reset the drop counter */
> +	abx500_set_register_interruptible(di->dev,
> +				AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
> +
> +	if (di->usb.charger_connected)
> +		ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
> +}
> +
>  /**
>   * ab8500_charger_vbusdetf_handler() - VBUS falling detected
>   * @irq:       interrupt number
> @@ -2343,6 +2512,7 @@ static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
>  {
>  	struct ab8500_charger *di = _di;
>  
> +	di->vbus_detected = false;
>  	dev_dbg(di->dev, "VBUS falling detected\n");
>  	queue_work(di->charger_wq, &di->detect_usb_type_work);
>  
> @@ -2470,6 +2640,25 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
>  }
>  
>  /**
> + * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
> + * @irq:       interrupt number
> + * @_di:       pointer to the ab8500_charger structure
> + *
> + * Returns IRQ status(IRQ_HANDLED)
> + */
> +static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
> +{
> +	struct ab8500_charger *di = _di;
> +
> +	dev_dbg(di->dev, "VBUS charger drop ended\n");
> +	di->flags.vbus_drop_end = true;
> +	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
> +						round_jiffies(30 * HZ));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
>   * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
>   * @irq:       interrupt number
>   * @_di:       pointer to the ab8500_charger structure
> @@ -2559,9 +2748,9 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
>  
>  /**
>   * ab8500_charger_usb_get_property() - get the usb properties
> - * @psy:        pointer to the power_supply structure
> - * @psp:        pointer to the power_supply_property structure
> - * @val:        pointer to the power_supply_propval union
> + * @psy:	pointer to the power_supply structure
> + * @psp:	pointer to the power_supply_property structure
> + * @val:	pointer to the power_supply_propval union

Stray cosmetic changes, should go via a separate patch.

>   * This function gets called when an application tries to get the usb
>   * properties by reading the sysfs files.
> @@ -2739,6 +2928,12 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
>  		goto out;
>  	}
>  
> +	ret = ab8500_charger_led_en(di, false);
> +	if (ret < 0) {
> +		dev_err(di->dev, "failed to disable LED\n");
> +		goto out;
> +	}
> +
>  	/* Backup battery voltage and current */
>  	ret = abx500_set_register_interruptible(di->dev,
>  		AB8500_RTC,
> @@ -2778,6 +2973,7 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
>  	{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
>  	{"VBUS_OVV", ab8500_charger_vbusovv_handler},
>  	{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
> +	{"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
>  };
>  
>  static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
> @@ -2814,13 +3010,15 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
>  		__func__, bm_usb_state, mA);
>  
>  	spin_lock(&di->usb_state.usb_lock);
> -	di->usb_state.usb_changed = true;
> +	di->usb_state.state_tmp = bm_usb_state;
> +	di->usb_state.usb_current_tmp = mA;
>  	spin_unlock(&di->usb_state.usb_lock);
>  
> -	di->usb_state.state = bm_usb_state;
> -	di->usb_state.usb_current = mA;
> -
> -	queue_work(di->charger_wq, &di->usb_state_changed_work);
> +	/*
> +	 * wait for some time until you get updates from the usb stack
> +	 * and negotiations are completed
> +	 */
> +	queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
>  
>  	return NOTIFY_OK;
>  }
> @@ -2860,6 +3058,9 @@ static int ab8500_charger_resume(struct platform_device *pdev)
>  			&di->check_hw_failure_work, 0);
>  	}
>  
> +	if (di->flags.vbus_drop_end)
> +		queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
> +
>  	return 0;
>  }
>  
> @@ -2872,6 +3073,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>  	if (delayed_work_pending(&di->check_hw_failure_work))
>  		cancel_delayed_work(&di->check_hw_failure_work);
>  
> +	if (delayed_work_pending(&di->vbus_drop_end_work))
> +		cancel_delayed_work(&di->vbus_drop_end_work);
> +
>  	flush_delayed_work_sync(&di->attach_work);
>  	flush_delayed_work_sync(&di->usb_charger_attached_work);
>  	flush_delayed_work_sync(&di->ac_charger_attached_work);
> @@ -2883,11 +3087,14 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>  	flush_work_sync(&di->ac_work);
>  	flush_work_sync(&di->detect_usb_type_work);
>  
> +	if (atomic_read(&di->current_stepping_sessions))
> +		return -EAGAIN;
> +
>  	return 0;
>  }
>  #else
> -#define ab8500_charger_suspend      NULL
> -#define ab8500_charger_resume       NULL
> +#define ab8500_charger_suspend	    NULL
> +#define ab8500_charger_resume	    NULL

Cosmetic, doesn't belong to this patch.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 00/57] power: Upgrade to ux500 battery management driver
  2012-09-27  3:38 ` [PATCH 00/57] power: Upgrade to ux500 battery management driver Anton Vorontsov
@ 2012-09-27 22:08   ` Mathieu Poirier
  2012-09-28  0:36     ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-27 22:08 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-26 09:38 PM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:11:57AM -0600, mathieu.poirier@linaro.org wrote:
>> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
>>
>> This patch set upgrades the current ux500 battery management driver
>> to the latest HW and functionality.
>>
>> Pull request for convenience:
>>
>> The following changes since commit 56d27adcb536b7430d5f8a6240df8ad261eb00bd:
>>
>>   Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile (2012-09-24 16:17:17 -0700)
>>
>> are available in the git repository at:
>>
>>   git://git.linaro.org/people/mpoirier/linux.git ux500-battery-management
> 
> I reviewed this series before, and all looked quite good overall. But I
> took another look, especially at the patches w/ empty commit messages, and
> there are some issues.
> 
> I tried to write some descriptions myself, but I failed: some changes are
> not obvious to me, so I couldn't write rationale for them. :-/
> 
> Thanks,
> Anton.
> 

Thank you very much for the review.

I am half way through your comments - some I can handle myself, for
others I needed to go back to the original author for clarification.
Another set will follow shortly.

Your time is much appreciated,
Mathieu.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505
  2012-09-25 16:12 ` [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505 mathieu.poirier
@ 2012-09-28  0:01   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:01 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:31AM -0600, mathieu.poirier@linaro.org wrote:
> From: Rikard Olsson <rikard.p.olsson@stericsson.com>
> 
> Add support for a power cut feature which allows user to
> configure when ab8505 should shut down system due to low
> battery.
> 
> Signed-off-by: Rikard Olsson <rikard.p.olsson@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c            |  488 +++++++++++++++++++++++++++++++++-
>  include/linux/mfd/abx500.h           |   10 +
>  include/linux/mfd/abx500/ab8500-bm.h |    8 +
>  3 files changed, 502 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 5e4a46b..fde189a 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -2351,6 +2351,64 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
>  		dev_err(di->dev, "BattOk init write failed.\n");
>  		goto out;
>  	}
> +
> +	if ((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
> +			abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) {
> +		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
> +			AB8505_RTC_PCUT_MAX_TIME_REG,
> +					 di->bat->fg_params->pcut_max_time);
> +

No need for this empty line.

> +		if (ret) {
> +			dev_err(di->dev,
> +				"%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n",
> +				 __func__);
> +			goto out;
> +		};
> +
> +		ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
> +			AB8505_RTC_PCUT_FLAG_TIME_REG,
> +					 di->bat->fg_params->pcut_flag_time);
> +

Ditto...

> +		if (ret) {
> +			dev_err(di->dev,
> +			"%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n",
> +			 __func__);

Wrong indentation.

> +			goto out;

No need for these gotos. Just return.

[...]
>  	return ret;
>  }
> @@ -2572,22 +2630,433 @@ static ssize_t ab8500_show_capacity(struct device *dev,
>  	return scnprintf(buf, PAGE_SIZE, "%d\n", capacity);
>  }
>  
> +static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
> +			     struct device_attribute *attr,
> +			     char *buf)
> +{
> +	int ret;
> +	u8 reg_value;
> +	struct power_supply *psy = dev_get_drvdata(dev);
> +	struct ab8500_fg *di;
> +
> +	di = to_ab8500_fg_device_info(psy);

This can go into the initializer.

> +
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
> +		AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
> +
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
> +		goto fail;

As there's just one fail case, we can just return early, no need for
the label.

> +	}
> +
> +	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
> +
> +fail:
> +	return ret;
> +}
> +
> +static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	int ret;
> +	long unsigned reg_value;
> +	struct power_supply *psy = dev_get_drvdata(dev);
> +	struct ab8500_fg *di;
> +
> +	di = to_ab8500_fg_device_info(psy);

Into initializer...

> +
> +	if (kstrtoul(buf, 10, &reg_value) != 0)
> +		goto fail;
> +
> +	if (reg_value > 0x7F) {
> +		dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
> +		goto fail;

No need for 'fail' label, we can just return in both cases.
Plus, currently the code would return success, even if kstroul failed
or on incorrect reg_value. That's not right, I guess the function
should return -EINVAL.

> +	}
> +
> +	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
> +		AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
> +
> +	if (ret < 0)
> +		dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
> +
> +fail:
> +	return count;
> +}
> +
> +static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
> +			     struct device_attribute *attr,
> +			     char *buf)
> +{
> +	int ret;
> +	u8 reg_value;
> +	struct power_supply *psy = dev_get_drvdata(dev);
> +	struct ab8500_fg *di;
> +
> +	di = to_ab8500_fg_device_info(psy);

Can go into initializer.

> +
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
> +		AB8505_RTC_PCUT_MAX_TIME_REG, &reg_value);
> +
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
> +		goto fail;

No need for goto.

> +	}
> +
> +	return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
> +
> +fail:
> +	return ret;
> +
> +}
> +
> +static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	int ret;
> +	long unsigned int reg_value;
> +	struct power_supply *psy = dev_get_drvdata(dev);
> +	struct ab8500_fg *di;
> +
> +	di = to_ab8500_fg_device_info(psy);

Initializer.

> +
> +	if (kstrtoul(buf, 10, &reg_value) != 0)
> +		goto fail;
> +
> +	if (reg_value > 0x7F) {
> +		dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
> +		goto fail;

No need for gotos, and should return -EINVAL on errors.

> +	}
> +
> +	ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
> +		AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
> +
> +	if (ret < 0)
> +		dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
> +
> +fail:
> +	return count;
> +}
> +
> +static ssize_t ab8505_powercut_restart_read(struct device *dev,
> +			     struct device_attribute *attr,
> +			     char *buf)
> +{
> +	int ret;
> +	u8 reg_value;
> +	struct power_supply *psy = dev_get_drvdata(dev);
> +	struct ab8500_fg *di;
> +
> +	di = to_ab8500_fg_device_info(psy);

can be initializer.

> +
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
> +		AB8505_RTC_PCUT_RESTART_REG, &reg_value);
> +

unneeded empty line.

> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
> +		goto fail;

no need for goto.

.....and more similar stuff in this stuff....

Does it make sense to write a macro that constructs all these
similar functions? Would save numerous lines of code.

[...]
>  static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
>  	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
>  };
>  
> +static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
> +	__ATTR(powercut_flagtime, (S_IRUGO | S_IWUGO),
> +				ab8505_powercut_flagtime_read,
> +				ab8505_powercut_flagtime_write),
> +	__ATTR(powercut_maxtime, (S_IRUGO | S_IWUGO),
> +				ab8505_powercut_maxtime_read,
> +				ab8505_powercut_maxtime_write),
> +	__ATTR(powercut_restart_max, (S_IRUGO | S_IWUGO),
> +				ab8505_powercut_restart_read,
> +				ab8505_powercut_restart_write),
> +	__ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
> +	__ATTR(powercut_restart_counter, S_IRUGO,
> +				ab8505_powercut_restart_counter_read, NULL),
> +	__ATTR(powercut_enable, (S_IRUGO | S_IWUGO), ab8505_powercut_read,
> +							 ab8505_powercut_write),
> +	__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
> +	__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUGO),
> +				ab8505_powercut_debounce_read,
> +				ab8505_powercut_debounce_write),
> +	__ATTR(powercut_enable_status, S_IRUGO,
> +				ab8505_powercut_enable_status_read, NULL),

Quite inconsistent wrapping. But that's just nitpicking...

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour
  2012-09-25 16:12 ` [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour mathieu.poirier
@ 2012-09-28  0:13   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:13 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:34AM -0600, mathieu.poirier@linaro.org wrote:
> From: Kalle Komierowski <karl.komierowski@stericsson.com>
> 
> 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 <karl.komierowski@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> ---
>  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)

Wrong indentation (should be at least two tabs; sometimes the driver
aligning to opening parenthesis, sometimes not... it's better to be
consistent).

> +{
> +	u8 usbch_ctrl1 = 0;
> +	int ret = 0;
> +

No need for this empty line.

> +	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)) {

By returning early you can greatly reduce indentation and make the
code more readable.

	if (usbch_ctrl1 & USB_CH_ENA)
		return ret;

	...the rest...

> +		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)

This function is very very similar to the previous one. The only
difference seem to be whether you use MAIN_CH_ENA or USB_CH_ENA..

So would it make sense to introduce a helper function that accepts one
additional parameter:

ab8500_charger_check_enable(....., int ch_ena)

?

[...]
> +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);

di->bat is very repetative, and can be avoided by introducing a variable.

(Not that it would greatly help readability, but a little, yes.)

> +	}
> +	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);
> -

Stray change.

>  	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);
>  };
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 38/57] power: l9540: Charge only mode fixes
  2012-09-25 16:12 ` [PATCH 38/57] power: l9540: Charge only mode fixes mathieu.poirier
@ 2012-09-28  0:27   ` Anton Vorontsov
  2012-09-28 18:32     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:27 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:35AM -0600, mathieu.poirier@linaro.org wrote:
> From: Rupesh Kumar <rupesh.kumar@stericsson.com>
> 
> Fix for: charging not getting enabled in
> charge only mode by external charger.

Subject says l9540.. what is this?

> Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c            |   42 +++++++++++++++++++++++++++++
>  drivers/power/abx500_chargalg.c           |   14 +++++++++
>  include/linux/mfd/abx500/ux500_chargalg.h |    2 +
>  3 files changed, 58 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 70e7c5e..ebeb068 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -15,6 +15,7 @@
>  #include <linux/device.h>
>  #include <linux/interrupt.h>
>  #include <linux/delay.h>
> +#include <linux/notifier.h>
>  #include <linux/slab.h>
>  #include <linux/platform_device.h>
>  #include <linux/power_supply.h>
> @@ -94,6 +95,10 @@
>  #define AB8500_SW_CONTROL_FALLBACK	0x03
>  /* Wait for enumeration before charging in us */
>  #define WAIT_ACA_RID_ENUMERATION	(5 * 1000)
> +/*External charger control*/
> +#define AB8500_SYS_CHARGER_CONTROL_REG		0x52
> +#define EXTERNAL_CHARGER_DISABLE_REG_VAL	0x03
> +#define EXTERNAL_CHARGER_ENABLE_REG_VAL		0x07
>  
>  /* UsbLineStatus register - usb types */
>  enum ab8500_charger_link_status {
> @@ -1672,6 +1677,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  	return ret;
>  }
>  
> +static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
> +				unsigned long event, void *data)
> +{
> +	int ret;
> +	struct device *dev = data;

Need an empty line here.

> +	/*Toggle External charger control pin*/

Spaces after /* and before */.

> +	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
> +				  AB8500_SYS_CHARGER_CONTROL_REG,
> +				  EXTERNAL_CHARGER_DISABLE_REG_VAL);
> +	if (ret < 0) {
> +		dev_err(dev, "write reg failed %d\n", ret);
> +		goto out;

No need for goto.

> +	}
> +	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
> +				  AB8500_SYS_CHARGER_CONTROL_REG,
> +				  EXTERNAL_CHARGER_ENABLE_REG_VAL);
> +	if (ret < 0)
> +		dev_err(dev, "Write reg failed %d\n", ret);
> +
> +out:
> +	return ret;
> +}
> +
>  /**
>   * ab8500_charger_usb_check_enable() - enable usb charging
>   * @charger:	pointer to the ux500_charger structure
> @@ -3201,6 +3229,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>  #define ab8500_charger_resume	    NULL
>  #endif
>  
> +static struct notifier_block charger_nb = {
> +	.notifier_call = ab8500_external_charger_prepare,
> +};
> +
>  static int __devexit ab8500_charger_remove(struct platform_device *pdev)
>  {
>  	struct ab8500_charger *di = platform_get_drvdata(pdev);
> @@ -3233,6 +3265,11 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
>  	/* Delete the work queue */
>  	destroy_workqueue(di->charger_wq);
>  
> +	/*Unregister external charger enable notifier*/

Spaces.

> +	if (!di->ac_chg.enabled)
> +		blocking_notifier_chain_unregister(
> +			&charger_notifier_list, &charger_nb);
> +
>  	flush_scheduled_work();
>  	if (di->usb_chg.enabled)
>  		power_supply_unregister(&di->usb_chg.psy);
> @@ -3307,6 +3344,11 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
>  	di->ac_chg.enabled = di->pdata->ac_enabled;
>  	di->ac_chg.external = false;
>  
> +	/*notifier for external charger enabling*/

Spaces.

> +	if (!di->ac_chg.enabled)
> +		blocking_notifier_chain_register(
> +			&charger_notifier_list, &charger_nb);
> +
>  	/* USB supply */
>  	/* power_supply base class */
>  	di->usb_chg.psy.name = "ab8500_usb";
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 9568f63..3ca00dd 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -24,6 +24,7 @@
>  #include <linux/mfd/abx500.h>
>  #include <linux/mfd/abx500/ux500_chargalg.h>
>  #include <linux/mfd/abx500/ab8500-bm.h>
> +#include <linux/notifier.h>
>  
>  /* Watchdog kick interval */
>  #define CHG_WD_INTERVAL			(6 * HZ)
> @@ -255,6 +256,9 @@ static enum power_supply_property abx500_chargalg_props[] = {
>  	POWER_SUPPLY_PROP_HEALTH,
>  };
>  
> +/*External charger prepare notifier*/
> +BLOCKING_NOTIFIER_HEAD(charger_notifier_list);

Can you place the notifier list in di->? Oh, you can't, di is
something different for registrat

struct abx500_chargalg *di;
struct ab8500_charger *di;

But having the global list is not friendly to multi-device situations.

If there's a subtle reason why you do it this way, it have to be described
in the patch description (which is very scarce, btw).

Also, the charger_notifier_list name is in global name space, it's too
generic. Has to be ab8500_chg_notifier_list or something.

>  /**
>   * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
>   * @data:	pointer to the abx500_chargalg structure
> @@ -508,6 +512,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
>  static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>  	int vset, int iset)
>  {
> +	static int ab8500_chargalg_ex_ac_enable_toggle;

Static? No, this is not multi-device friendly, seems like a hack.

> +
>  	if (!di->ac_chg || !di->ac_chg->ops.enable)
>  		return -ENXIO;
>  
> @@ -520,6 +526,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>  	di->chg_info.ac_iset = iset;
>  	di->chg_info.ac_vset = vset;
>  
> +	/*enable external charger*/

spaces.

> +	if (enable && di->ac_chg->external &&
> +		!ab8500_chargalg_ex_ac_enable_toggle) {

Wrong indentation.

> +		blocking_notifier_call_chain(&charger_notifier_list,
> +					0, di->dev);
> +		ab8500_chargalg_ex_ac_enable_toggle++;
> +	}
> +
>  	return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
>  }
>  
> diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
> index 110d12f..fa831f1 100644
> --- a/include/linux/mfd/abx500/ux500_chargalg.h
> +++ b/include/linux/mfd/abx500/ux500_chargalg.h
> @@ -41,4 +41,6 @@ struct ux500_charger {
>  	bool external;
>  };
>  
> +extern struct blocking_notifier_head charger_notifier_list;
> +
>  #endif
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 00/57] power: Upgrade to ux500 battery management driver
  2012-09-27 22:08   ` Mathieu Poirier
@ 2012-09-28  0:36     ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:36 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Thu, Sep 27, 2012 at 04:08:43PM -0600, Mathieu Poirier wrote:
[...]
> I am half way through your comments - some I can handle myself, for
> others I needed to go back to the original author for clarification.
> Another set will follow shortly.

Yeah, I understand that it's mostly not your patches. All the patches
passed my 'sanity' tests, i.e. it was all checkpatch clean, warnings and
sparse clean, so I was quite relaxed about this series.

But taking a closer look, I see there are some serious issues. And of
course that the main problem with the patches is that patch descriptions
often insufficiently (if at all) decribe the reasoning behind the changes.

With time it will get even worse: nobody would be able to reach the
authors, or they will no longer remember the details.

So, I guess it's in everybody's interest to fix it.

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS
  2012-09-25 16:12 ` [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS mathieu.poirier
@ 2012-09-28  0:52   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:52 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:36AM -0600, mathieu.poirier@linaro.org wrote:
> From: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
> 
> Do not set higher current in stepping functionality if VBUS is dropping.
> After VBUS has dropped try to set current once again. If dropping again
> then we have found the maximum capability of the charger.
> 
> Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Per FORLIN <per.forlin@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c |  166 ++++++++++++++++++++++++++++------------
>  1 files changed, 117 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index ebeb068..18931e4 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -55,6 +55,7 @@
>  #define MAIN_CH_INPUT_CURR_SHIFT	4
>  #define VBUS_IN_CURR_LIM_SHIFT		4
>  #define AUTO_VBUS_IN_CURR_LIM_SHIFT	4
> +#define VBUS_IN_CURR_LIM_RETRY_SET_TIME	30 /* seconds */
>  
>  #define LED_INDICATOR_PWM_ENA		0x01
>  #define LED_INDICATOR_PWM_DIS		0x00
> @@ -199,10 +200,15 @@ struct ab8500_charger_usb_state {
>  	spinlock_t usb_lock;
>  };
>  
> +struct ab8500_charger_max_usb_in_curr {
> +	int usb_type_max;
> +	int set_max;
> +	int calculated_max;
> +};
[...]
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		di->is_usb_host = true;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;

It seems that you introduced struct max_usb_in_curr for no obvious
reason.

Because of this, there are a lot of churn, and much much longer lines all
over, which hurts readability.

Why not just introduce a usb_curr_set_max and usb_curr_calc_max in di->?
Or something alike, or make things shorter in general, somehow. E.g., why
do you have to repeat usb_? And why repeat max?

struct ab8500_chg_max_usb_in_curr {
	int type;
	int set;
	int calc;
};

This would be at least shorter and still understandable in a context. But
again, I guess we don't need this new struct -- you don't pass it anywhere
anyway.

>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_HOST_CHG_HS_CHIRP:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		di->is_usb_host = true;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_HOST_CHG_HS:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		di->is_usb_host = true;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_ACA_RID_C_HS:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
> -		di->is_usb_host = false;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9;
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_ACA_RID_A:
> @@ -690,8 +696,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		 * can consume (900mA). Closest level is 500mA
>  		 */
>  		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		di->is_usb_host = false;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_ACA_RID_B:
> @@ -699,38 +704,35 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		 * Dedicated charger level minus 120mA (20mA for ACA and
>  		 * 100mA for potential accessory). Closest level is 1300mA
>  		 */
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3;
>  		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> -		di->is_usb_host = false;
> +				di->max_usb_in_curr.usb_type_max);
>  		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_HOST_CHG_NM:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> -		di->is_usb_host = true;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_DEDICATED_CHG:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> -		di->is_usb_host = false;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_ACA_RID_C_HS_CHIRP:
>  		case USB_STAT_ACA_RID_C_NM:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> -		di->is_usb_host = false;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
>  		di->is_aca_rid = 1;
>  		break;
>  	case USB_STAT_NOT_CONFIGURED:
>  		if (di->vbus_detected) {
>  			di->usb_device_is_unrecognised = true;
>  			dev_dbg(di->dev, "USB Type - Legacy charger.\n");
> -			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
> +			di->max_usb_in_curr.usb_type_max =
> +						USB_CH_IP_CUR_LVL_1P5;
>  			break;
>  		}
>  	case USB_STAT_HM_IDGND:
>  		dev_err(di->dev, "USB Type - Charging not allowed\n");
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
>  		ret = -ENXIO;
>  		break;
>  	case USB_STAT_RESERVED:
> @@ -743,9 +745,11 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		}
>  		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
>  			dev_dbg(di->dev, "USB Type - Charging not allowed\n");
> -			di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
> +			di->max_usb_in_curr.usb_type_max =
> +						USB_CH_IP_CUR_LVL_0P05;
>  			dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
> -					link_status, di->max_usb_in_curr);
> +				link_status,
> +				di->max_usb_in_curr.usb_type_max);
>  			ret = -ENXIO;
>  			break;
>  		}
> @@ -754,23 +758,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  	case USB_STAT_CARKIT_2:
>  	case USB_STAT_ACA_DOCK_CHARGER:
>  	case USB_STAT_CHARGER_LINE_1:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
> -				di->max_usb_in_curr);
> +				di->max_usb_in_curr.usb_type_max);
>  	case USB_STAT_NOT_VALID_LINK:
>  		dev_err(di->dev, "USB Type invalid - try charging anyway\n");
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		break;
>  
>  	default:
>  		dev_err(di->dev, "USB Type - Unknown\n");
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
>  		ret = -ENXIO;
>  		break;
>  	};
>  
> +	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
>  	dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
> -		link_status, di->max_usb_in_curr);
> +		link_status, di->max_usb_in_curr.set_max);
>  
>  	return ret;
>  }
> @@ -1074,28 +1079,48 @@ static int ab8500_vbus_in_curr_to_regval(int curr)
>   */
>  static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
>  {
> +	int ret = 0;
>  	switch (di->usb_state.usb_current) {
>  	case 100:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
>  		break;
>  	case 200:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
>  		break;
>  	case 300:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
>  		break;
>  	case 400:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
>  		break;
>  	case 500:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
>  		break;
>  	default:
> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
> -		return -1;
> +		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
> +		ret = -EPERM;
>  		break;
>  	};
> -	return 0;
> +	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
> +	return ret;
> +}
> +
> +/**
> + * ab8500_charger_check_continue_stepping() - Check to allow stepping
> + * @di:		pointer to the ab8500_charger structure
> + * @reg:	select what charger register to check
> + *
> + * Check if current stepping should be allowed to continue.
> + * Checks if charger source has not collapsed. If it has, further stepping
> + * is not allowed.
> + */
> +static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di,
> +						   int reg)
> +{
> +	if (reg == AB8500_USBCH_IPT_CRNTLVL_REG)
> +		return !di->flags.vbus_drop_end;
> +	else
> +		return true;

No need for else.

>  }
>  
>  /**
> @@ -1220,7 +1245,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
>  				usleep_range(step_udelay, step_udelay * 2);
>  		}
>  	} else {
> -		for (i = prev_curr_index + 1; i <= curr_index; i++) {
> +		bool allow = true;

Needs empty line here.

> +		for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
>  			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
>  				(u8) i << shift_value, reg);
>  			ret = abx500_set_register_interruptible(di->dev,
> @@ -1231,6 +1257,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
>  			}
>  			if (i != curr_index)
>  				usleep_range(step_udelay, step_udelay * 2);
> +
> +			allow = ab8500_charger_check_continue_stepping(di, reg);
>  		}
>  	}
>  
> @@ -1255,6 +1283,8 @@ 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);
> +	if (di->max_usb_in_curr.set_max > 0)
> +		min_value = min(di->max_usb_in_curr.set_max, min_value);
>  
>  	switch (min_value) {
>  	case 100:
> @@ -1609,7 +1639,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  
>  		/* USBChInputCurr: current that can be drawn from the usb */
>  		ret = ab8500_charger_set_vbus_in_curr(di,
> -					di->max_usb_in_curr);
> +					di->max_usb_in_curr.usb_type_max);
>  		if (ret) {
>  			dev_err(di->dev, "setting USBChInputCurr failed\n");
>  			return ret;
> @@ -1943,10 +1973,12 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work)
>  		(di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
>  		di->vbat > VBAT_TRESH_IP_CUR_RED))) {
>  
> -		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);
> +		dev_dbg(di->dev, "Vbat did cross threshold, curr: %d,",
> +					di->max_usb_in_curr.usb_type_max);
> +		dev_dbg(di->dev, " new: %d, old: %d\n",
> +						di->vbat, di->old_vbat);
> +		ab8500_charger_set_vbus_in_curr(di,
> +					di->max_usb_in_curr.usb_type_max);
>  		power_supply_changed(&di->usb_chg.psy);
>  	}
>  
> @@ -2224,7 +2256,8 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
>  
>  	/* Update maximum input current if USB enumeration is not detected */
>  	if (!di->usb.charger_online) {
> -		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
> +		ret = ab8500_charger_set_vbus_in_curr(di,
> +					di->max_usb_in_curr.usb_type_max);
>  		if (ret)
>  			return;
>  	}
> @@ -2403,7 +2436,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
>  		if (!ab8500_charger_get_usb_cur(di)) {
>  			/* Update maximum input current */
>  			ret = ab8500_charger_set_vbus_in_curr(di,
> -					di->max_usb_in_curr);
> +					di->max_usb_in_curr.usb_type_max);
>  			if (ret)
>  				return;
>  
> @@ -2617,15 +2650,45 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
>  {
>  	struct ab8500_charger *di = container_of(work,
>  		struct ab8500_charger, vbus_drop_end_work.work);
> +	int ret;
> +	u8 reg_value;
>  
>  	di->flags.vbus_drop_end = false;
>  
>  	/* Reset the drop counter */
>  	abx500_set_register_interruptible(di->dev,
>  				AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
> +						AB8500_CH_USBCH_STAT2_REG,
> +						&reg_value);
> +	if (ret < 0) {
> +		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
> +	} else {
> +		int curr = ab8500_charger_vbus_in_curr_map[
> +			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];

empty line needed.

> +		if (di->max_usb_in_curr.calculated_max != curr) {
> +			/* USB source is collapsing */
> +			di->max_usb_in_curr.calculated_max = curr;
> +			dev_dbg(di->dev,
> +			"VBUS input current limiting to %d mA\n",
> +				di->max_usb_in_curr.calculated_max);
> +		} else {
> +			/*
> +			 * USB source can not give more than this amount.
> +			 * Taking more will collapse the source.
> +			 */
> +			di->max_usb_in_curr.set_max =
> +				di->max_usb_in_curr.calculated_max;
> +			dev_dbg(di->dev,
> +				"VBUS input current limited to %d mA\n",
> +				di->max_usb_in_curr.set_max);
> +			return;
> +		}
> +	}
>  
>  	if (di->usb.charger_connected)
> -		ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
> +		ab8500_charger_set_vbus_in_curr(di,
> +			di->max_usb_in_curr.usb_type_max);
>  }
>  
>  /**
> @@ -2779,8 +2842,13 @@ static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
>  
>  	dev_dbg(di->dev, "VBUS charger drop ended\n");
>  	di->flags.vbus_drop_end = true;
> +
> +	/*
> +	 * VBUS might have dropped due to bad connection.
> +	 * Schedule a new input limit set to the value SW requests.
> +	 */
>  	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
> -						round_jiffies(30 * HZ));
> +			round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
>  
>  	return IRQ_HANDLED;
>  }
> -- 
> 1.7.5.4
> 
> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 40/57] power: ab8500: ADC for battery thermistor
  2012-09-25 16:12 ` [PATCH 40/57] power: ab8500: ADC for battery thermistor mathieu.poirier
@ 2012-09-28  0:57   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  0:57 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:37AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> When using ABx500_ADC_THERM_BATCTRL the battery ID resistor
> is combined with a NTC resistor to both identify the battery and
> to measure its temperature.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
> Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
> ---
>  drivers/power/ab8500_btemp.c |    4 +++-
>  include/linux/mfd/abx500.h   |    2 ++
>  2 files changed, 5 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index 506f124..56a3bb9 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -557,7 +557,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
>  	/* BATTERY_UNKNOWN is defined on position 0, skip it! */
>  	for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
>  		if ((res <= di->bat->bat_type[i].resis_high) &&
> -			(res >= di->bat->bat_type[i].resis_low)) {
> +			(res >= di->bat->bat_type[i].resis_low) &&

I understand that you just change the already wrongly indentend code, so
just something to consider for the future: the driver needs lot's of
readability fixups, i.e. repeating di->bat, wrong indentation, too much
indentation (some things can be factored out to separate functions), etc.

> +			(di->bat->bat_type[i].adc_therm ==
> +							di->bat->adc_therm)) {
>  			dev_dbg(di->dev, "Battery detected on %s"
>  				" low %d < res %d < high: %d"
>  				" index: %d\n",
> diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
> index 97e918f..cb2b82a 100644
> --- a/include/linux/mfd/abx500.h
> +++ b/include/linux/mfd/abx500.h
> @@ -270,6 +270,7 @@ struct abx500_maxim_parameters {
>   * @low_high_cur_lvl:		charger current in temp low/high state in mA
>   * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
>   * @battery_resistance:		battery inner resistance in mOhm.
> + * @adc_therm:			battery uses controller or resistor for temp.
>   * @n_r_t_tbl_elements:		number of elements in r_to_t_tbl
>   * @r_to_t_tbl:			table containing resistance to temp points
>   * @n_v_cap_tbl_elements:	number of elements in v_to_cap_tbl
> @@ -297,6 +298,7 @@ struct abx500_battery_type {
>  	int low_high_cur_lvl;
>  	int low_high_vol_lvl;
>  	int battery_resistance;
> +	enum abx500_adc_therm adc_therm;
>  	int n_temp_tbl_elements;
>  	struct abx500_res_to_temp *r_to_t_tbl;
>  	int n_v_cap_tbl_elements;
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 44/57] power: ab8500: remove unecesary define flag
  2012-09-25 16:12 ` [PATCH 44/57] power: ab8500: remove unecesary define flag mathieu.poirier
@ 2012-09-28  1:05   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  1:05 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:41AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> Remove flag that serve no purpose from source code, Kconfig
> and Makefile.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
> ---
>  drivers/power/Kconfig          |    7 -------
>  drivers/power/ab8500_charger.c |    2 +-
>  2 files changed, 1 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index c1892f3..f7c13ae 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -303,13 +303,6 @@ config AB8500_BM
>  	depends on AB8500_CORE && AB8500_GPADC
>  	help
>  	  Say Y to include support for AB8500 battery management.
> -
> -config AB8500_BATTERY_THERM_ON_BATCTRL
> -	bool "Thermistor connected on BATCTRL ADC"
> -	depends on AB8500_BM
> -	help
> -	  Say Y to enable battery temperature measurements using
> -	  thermistor connected on BATCTRL ADC.

OK...

>  endif # POWER_SUPPLY
>  
>  source "drivers/power/avs/Kconfig"
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 9449a33..d90fe9f 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -3369,7 +3369,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
>  	flush_scheduled_work();
>  	if (di->usb_chg.enabled)
>  		power_supply_unregister(&di->usb_chg.psy);
> -	if (di->ac_chg.enabled)
> +	if (di->ac_chg.enabled && !di->ac_chg.external)

But the patch descriptions says that the patch is removing some flags, but
it adds something unrelated.

If anything, this should be two separate patches.

>  		power_supply_unregister(&di->ac_chg.psy);
>  
>  	platform_set_drvdata(pdev, NULL);
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 45/57] power: ab8500: defer btemp filtering while init
  2012-09-25 16:12 ` [PATCH 45/57] power: ab8500: defer btemp filtering while init mathieu.poirier
@ 2012-09-28  1:08   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  1:08 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:42AM -0600, mathieu.poirier@linaro.org wrote:
> From: Rupesh Kumar <rupesh.kumar@stericsson.com>
> 
> Due to btemp filtering enabled during init, temp values
> reported to charge algorithm driver started from 0.
> As a result,charge algorithm was going into wrong
> state and charging was stopped.
> This patch defers btemp filtering till init is done.
> 
> Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> ---
>  drivers/power/ab8500_btemp.c |    6 +++---
>  1 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index b24835f..1f33122 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -613,7 +613,6 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
>  		struct ab8500_btemp, btemp_periodic_work.work);
>  
>  	if (!di->initialized) {
> -		di->initialized = true;
>  		/* Identify the battery */
>  		if (ab8500_btemp_id(di) < 0)
>  			dev_warn(di->dev, "failed to identify the battery\n");
> @@ -626,8 +625,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
>  	 * same temperature. Else only allow 1 degree change from previous
>  	 * reported value in the direction of the new measurement.
>  	 */
> -	if (bat_temp == di->prev_bat_temp || !di->initialized) {
> -		if (di->bat_temp != di->prev_bat_temp || !di->initialized) {
> +	if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
> +		if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {

This change is stray, and adds completely unneeded parenthesis.

> +			di->initialized = true;
>  			di->bat_temp = bat_temp;
>  			power_supply_changed(&di->btemp_psy);
>  		}
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 47/57] power: Harmonising platform data declaration/handling
  2012-09-25 16:12 ` [PATCH 47/57] power: Harmonising platform data declaration/handling mathieu.poirier
@ 2012-09-28  1:11   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  1:11 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:44AM -0600, mathieu.poirier@linaro.org wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
> 
> Making platform data declaration and handling similar accross all
> ab8500_xyc.c battery management files.  Also adding gards against
> NULL platform data.
> 
> Signed-off-by: Philippe Langlais <philippe.langlais@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/power/ab8500_btemp.c    |   17 +++++++++--------
>  drivers/power/ab8500_charger.c  |   18 +++++++++---------
>  drivers/power/ab8500_fg.c       |   17 +++++++++--------
>  drivers/power/abx500_chargalg.c |   25 ++++++++++++++++++++-----
>  4 files changed, 47 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index 1f33122..cf4b653 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -1028,10 +1028,10 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
>  {
>  	int irq, i, ret = 0;
>  	u8 val;
> -	struct ab8500_platform_data *plat_data;
> -	struct ab8500_btemp *di;
> +	struct ab8500_platform_data *plat;
>  
> -	di = kzalloc(sizeof(*di), GFP_KERNEL);

While at it, you could remove the unneeded empty line.

> +	struct ab8500_btemp *di =
> +		kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);

And here you need the empty line.

>  	if (!di)
>  		return -ENOMEM;
>  

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 46/57] power: chargealg: Realign with upstream version
  2012-09-25 16:12 ` [PATCH 46/57] power: chargealg: Realign with upstream version mathieu.poirier
@ 2012-09-28  1:17   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  1:17 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:43AM -0600, mathieu.poirier@linaro.org wrote:
> From: Loic Pallardy <loic.pallardy@stericsson.com>
> 
> Upstream version of AB charge algo has been reverted
> during kernel 3.4 port.

Why it's been reverted in the first place?.. If you're just reverting what
has been reverted before, you're probably breaking things again, so the
patch would [temporary] introduce some breakage?

> This patch restore state by:
> - renaming ab8500_chargal.c in abx500_chargal.c
> - renaming function from ab8500 to abx500
> - moving generic structure in "include/mfd/abx500.h"
> 
> Goal is to ease next code reversion and realignment
> with mainline

I still hardly understand the rationale behind this change. But if it
compiles, bisectionable and works... it's fine by me. :-)

> 
> Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c       |  166 +++++++++++-----------------------
>  include/linux/mfd/abx500.h           |   10 ++-
>  include/linux/mfd/abx500/ab8500-bm.h |    5 +-
>  3 files changed, 65 insertions(+), 116 deletions(-)
> 
[...]
> @@ -2678,7 +2616,7 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
>  {
>  	struct ab8500_charger *di = container_of(work,
>  		struct ab8500_charger, vbus_drop_end_work.work);
> -	int ret;
> +	int ret, curr;

Should be one variable declaration per line.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 48/57] power: ab8500 : quick re-attach for ext charger
  2012-09-25 16:12 ` [PATCH 48/57] power: ab8500 : quick re-attach for ext charger mathieu.poirier
@ 2012-09-28  1:19   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  1:19 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:45AM -0600, mathieu.poirier@linaro.org wrote:
> From: Rupesh Kumar <rupesh.kumar@stericsson.com>
> 
> Quick re-attach charging behaviour is not required
> for external ac charger. Internal AC/USB Charger removal
> detection problem is due to a bug in AB8500 ASICs.
> 
> Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
> ---
>  drivers/power/abx500_chargalg.c |    5 +++--
>  1 files changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 180deab..ce58f20 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -330,12 +330,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
>  		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) {
> +	} else if ((di->chg_info.charger_type & AC_CHG) &&
> +					!(di->ac_chg->external)) {
>  		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;
> +	return 0;

I'm not sure about this logic, but I guess it should be

	} else {
		return 0;
	}
	return -ENXIO;

>  }
>  
>  /**
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 49/57] power: Cancelling status charging notification.
  2012-09-25 16:12 ` [PATCH 49/57] power: Cancelling status charging notification mathieu.poirier
@ 2012-09-28  2:16   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:16 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:46AM -0600, mathieu.poirier@linaro.org wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>

No reasoning behind the change?

> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/power/abx500_chargalg.c |    2 --
>  1 files changed, 0 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index ce58f20..4db0ef0 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -641,10 +641,8 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
>  	abx500_chargalg_usb_en(di, false, 0, 0);
>  	abx500_chargalg_stop_safety_timer(di);
>  	abx500_chargalg_stop_maintenance_timer(di);
> -	di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
>  	di->maintenance_chg = false;
>  	cancel_delayed_work(&di->chargalg_wd_work);
> -	power_supply_changed(&di->chargalg_psy);
>  }
>  
>  /**
> -- 
> 1.7.5.4
> 
> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp
  2012-09-25 16:12 ` [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp mathieu.poirier
@ 2012-09-28  2:21   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:21 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:47AM -0600, mathieu.poirier@linaro.org wrote:
> From: Hakan Berg <hakan.berg@stericsson.com>
> 
> When the charging safety timer is elapsed the battery health is shown as "Good".
> This is misleading and also hard to distingiush problems reported on "phone
> discharges although charger is attached".
> 
> When safety timer elapses that is an indication of a fault in the battery of
> some kind. Hence report as POWER_SUPPLY_HEALTH_UNSPEC_FAILURE.
> 
> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
> ---
>  drivers/power/abx500_chargalg.c |    4 ++++
>  1 files changed, 4 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 4db0ef0..1df238f 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -1711,6 +1711,10 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
>  				val->intval = POWER_SUPPLY_HEALTH_COLD;
>  			else
>  				val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
> +		} else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
> +			di->charge_state ==
> +			STATE_SAFETY_TIMER_EXPIRED_INIT) {

Wrong indentation, no need to wrap lines. (You could align to
di->change_state, and thus it'll look prettier and there still wouldn't
need for line wrapping.)

> +			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
>  		} else {
>  			val->intval = POWER_SUPPLY_HEALTH_GOOD;
>  		}
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 51/57] power: ab8500: Re-alignment with internal developement.
  2012-09-25 16:12 ` [PATCH 51/57] power: ab8500: Re-alignment with internal developement mathieu.poirier
@ 2012-09-28  2:35   ` Anton Vorontsov
  2012-09-28  2:45     ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:35 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:48AM -0600, mathieu.poirier@linaro.org wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
> 
> A lot of developement happened internally since the first
> mainlining of the battery managmenent driver.  Most of the
> new code can be historically accounted for but some of it
> can't.
> 
> This patch is a gathering of the code for which history was
> lost but still relevant to the well being of the driver.

Nope, sorry. The patch has to be logically separated and the description
should precisely describe the change(s), not 'let's gather some missing
bits.'

> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/power/ab8500_charger.c  |    2 +-
>  drivers/power/abx500_chargalg.c |   66 +++++++++++++++++++++++++++------------
>  2 files changed, 47 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 1290470..3a97012 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -720,7 +720,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>  		di->is_aca_rid = 0;
>  		break;
>  	case USB_STAT_ACA_RID_C_HS_CHIRP:
> -		case USB_STAT_ACA_RID_C_NM:
> +	case USB_STAT_ACA_RID_C_NM:
>  		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
>  		di->is_aca_rid = 1;
>  		break;
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 1df238f..636d970 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -27,7 +27,7 @@
>  #include <linux/notifier.h>
>  
>  /* Watchdog kick interval */
> -#define CHG_WD_INTERVAL			(6 * HZ)
> +#define CHG_WD_INTERVAL			(60 * HZ)
>  
>  /* End-of-charge criteria counter */
>  #define EOC_COND_CNT			10
> @@ -513,7 +513,7 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
>  static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>  	int vset, int iset)
>  {
> -	static int ab8500_chargalg_ex_ac_enable_toggle;
> +	static int abx500_chargalg_ex_ac_enable_toggle;
>  
>  	if (!di->ac_chg || !di->ac_chg->ops.enable)
>  		return -ENXIO;
> @@ -529,10 +529,10 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>  
>  	/*enable external charger*/
>  	if (enable && di->ac_chg->external &&
> -		!ab8500_chargalg_ex_ac_enable_toggle) {
> +		!abx500_chargalg_ex_ac_enable_toggle) {
>  		blocking_notifier_call_chain(&charger_notifier_list,
>  					0, di->dev);
> -		ab8500_chargalg_ex_ac_enable_toggle++;
> +		abx500_chargalg_ex_ac_enable_toggle++;
>  	}
>  
>  	return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
> @@ -899,6 +899,27 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
>  	}
>  }
>  
> +static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
> +{
> +	/*
> +	 * The safety timer will not be started until the capacity reported
> +	 * from the FG algorithm is 100%. Then we know that the amount of
> +	 * charge that's gone into the battery is enough for the battery
> +	 * to be full. If it has not reached end-of-charge before the safety
> +	 * timer has expired then we know that the battery is overcharged
> +	 * and charging will be stopped to protect the battery.
> +	 */
> +	if (di->batt_data.percent == 100 &&
> +		!timer_pending(&di->safety_timer)) {

Wrong indentation.

> +		abx500_chargalg_start_safety_timer(di);
> +		dev_dbg(di->dev, "start safety timer\n");
> +	} else if (di->batt_data.percent != 100 &&
> +		timer_pending(&di->safety_timer)) {

Ditto.

Plus, I think this can be simplified, you can just do.

abx500_chargalg_check_safety_timer()
{
	if (di->batt_data.percent < 100) {
		dev_dbg(di->dev, "stopping safety timer\n");
		abx500_chargalg_stop_safety_timer(di);
		return;
	}

	dev_dbg(di->dev, "starting safety timer\n");
	abx500_chargalg_start_safety_timer(di);
}

The thing is, restarting an already pending timer is no-op, unless you
program the timer before the previously programmed value.

And stopping unarmed timer should be alo no-op.

(btw, these dev_dbg() should probably be placed into the start/stop
functions, to catch all the users/invocations in the debug log).

> +		abx500_chargalg_stop_safety_timer(di);
> +		dev_dbg(di->dev, "stop safety timer\n");
> +	}
> +}
> +
>  static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
>  {
>  	struct power_supply *psy;
> @@ -1125,6 +1146,10 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
>  			switch (ext->type) {
>  			case POWER_SUPPLY_TYPE_BATTERY:
>  				di->batt_data.volt = ret.intval / 1000;
> +				if (di->batt_data.volt >= BATT_OVV_VALUE)
> +					di->events.batt_ovv = true;
> +				else
> +					di->events.batt_ovv = false;
>  				break;
>  			case POWER_SUPPLY_TYPE_MAINS:
>  				di->chg_info.ac_volt = ret.intval / 1000;
> @@ -1214,7 +1239,6 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
>  			}
>  			break;
>  		case POWER_SUPPLY_PROP_CAPACITY:
> -			di->batt_data.percent = ret.intval;
>  			if (!capacity_updated)
>  				di->batt_data.percent = ret.intval;
>  			break;
> @@ -1465,12 +1489,12 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
>  			di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
>  			di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
>  		abx500_chargalg_state_to(di, STATE_NORMAL);
> -		abx500_chargalg_start_safety_timer(di);
>  		abx500_chargalg_stop_maintenance_timer(di);
>  		init_maxim_chg_curr(di);
>  		di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
>  		di->eoc_cnt = 0;
>  		di->maintenance_chg = false;
> +		di->maint_state = MAINT_A;
>  		power_supply_changed(&di->chargalg_psy);
>  
>  		break;
> @@ -1478,17 +1502,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
>  	case STATE_NORMAL:
>  		handle_maxim_chg_curr(di);
>  		if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
> -			di->maintenance_chg) {
> -			if (di->bat->no_maintenance)
> -				abx500_chargalg_state_to(di,
> -					STATE_WAIT_FOR_RECHARGE_INIT);
> -			else
> -				abx500_chargalg_state_to(di,
> -					STATE_MAINTENANCE_A_INIT);
> -		}
> +				di->maintenance_chg)
> +			abx500_chargalg_state_to(di,
> +				STATE_WAIT_FOR_RECHARGE_INIT);
> +
> +		/* Check whether we should start the safety timer or not */
> +		abx500_chargalg_check_safety_timer(di);
>  		break;
>  
> -	/* This state will be used when the maintenance state is disabled */
>  	case STATE_WAIT_FOR_RECHARGE_INIT:
>  		abx500_chargalg_hold_charging(di);
>  		abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
> @@ -1531,13 +1552,15 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
>  			di->bat->bat_type[
>  				di->bat->batt_id].maint_a_cur_lvl);
>  		abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
> +		di->maint_state = MAINT_B;
>  		power_supply_changed(&di->chargalg_psy);
>  		/* Intentional fallthrough*/
>  
>  	case STATE_MAINTENANCE_A:
>  		if (di->events.maintenance_timer_expired) {
>  			abx500_chargalg_stop_maintenance_timer(di);
> -			abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
> +			abx500_chargalg_state_to(di,
> +					STATE_WAIT_FOR_RECHARGE_INIT);
>  		}
>  		break;
>  
> @@ -1603,7 +1626,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
>  	/* Start charging directly if the new state is a charge state */
>  	if (di->charge_state == STATE_NORMAL_INIT ||
>  			di->charge_state == STATE_MAINTENANCE_A_INIT ||
> -			di->charge_state == STATE_MAINTENANCE_B_INIT)
> +			di->charge_state == STATE_MAINTENANCE_B_INIT ||
> +			di->charge_state == STATE_WAIT_FOR_RECHARGE_INIT)
>  		queue_work(di->chargalg_wq, &di->chargalg_work);
>  }
>  
> @@ -1824,7 +1848,7 @@ static struct attribute *abx500_chargalg_chg[] = {
>  	NULL
>  };
>  
> -static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
> +const struct sysfs_ops abx500_chargalg_sysfs_ops = {

Why?..

>  	.show = abx500_chargalg_sysfs_show,
>  	.store = abx500_chargalg_sysfs_charger,
>  };
> @@ -2009,10 +2033,12 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
>  		goto free_psy;
>  	}
>  
> +	di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
> +	abx500_chargalg_state_to(di, STATE_HANDHELD);
> +
>  	/* Run the charging algorithm */
>  	queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
>  
> -	dev_info(di->dev, "probe success\n");
>  	return ret;
>  
>  free_psy:
> @@ -2052,4 +2078,4 @@ module_exit(abx500_chargalg_exit);
>  MODULE_LICENSE("GPL v2");
>  MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
>  MODULE_ALIAS("platform:abx500-chargalg");
> -MODULE_DESCRIPTION("abx500 battery charging algorithm");
> +MODULE_DESCRIPTION("abx500 battery temperatur driver");

typo?


Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 51/57] power: ab8500: Re-alignment with internal developement.
  2012-09-28  2:35   ` Anton Vorontsov
@ 2012-09-28  2:45     ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:45 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Thu, Sep 27, 2012 at 07:35:10PM -0700, Anton Vorontsov wrote:
[...]
> abx500_chargalg_check_safety_timer()
> {
> 	if (di->batt_data.percent < 100) {
> 		dev_dbg(di->dev, "stopping safety timer\n");
> 		abx500_chargalg_stop_safety_timer(di);
> 		return;
> 	}
> 
> 	dev_dbg(di->dev, "starting safety timer\n");
> 	abx500_chargalg_start_safety_timer(di);
> }
> 
> The thing is, restarting an already pending timer is no-op, unless you
> program the timer before the previously programmed value.

Oh, actually, no. It's no-op if old expires == new expires. :-/

So, yes, you need to check for pending before (re)starting. So, it'll
become

	if (pending)
		return;
	abx500_chargalg_start_safety_timer(di);

(Or better, start_safety_timer() should do that, and it seems that it
already does.)

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 52/57] power: abx500_chargalg: Use hrtimer
  2012-09-25 16:12 ` [PATCH 52/57] power: abx500_chargalg: Use hrtimer mathieu.poirier
@ 2012-09-28  2:47   ` Anton Vorontsov
  2012-09-28 18:33     ` Mathieu Poirier
  0 siblings, 1 reply; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:47 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:49AM -0600, mathieu.poirier@linaro.org wrote:
> From: Hakan Berg <hakan.berg@stericsson.com>
> 
> Timers used for charging safety and maintenance must work even when
> CPU is power collapsed. By using hrtimers with realtime clock, system
> is able to trigger an alarm that wakes the CPU up and make it possible
> to handle the event.
> 
> Allow a little slack of 5 minutes to the hrtimers to allow CPU to be
> waked up in a more optimal power saving way. A 5 minute delay to
> time out timers on hours does not impact on safety.
> 
> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
> ---
>  drivers/power/abx500_chargalg.c |   94 ++++++++++++++++++++++-----------------
>  1 files changed, 53 insertions(+), 41 deletions(-)
> 
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 636d970..c8849af 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -1,5 +1,6 @@
>  /*
>   * Copyright (C) ST-Ericsson SA 2012
> + * Copyright (c) 2012 Sony Mobile Communications AB
>   *
>   * Charging algorithm driver for abx500 variants
>   *
> @@ -8,11 +9,13 @@
>   *	Johan Palsson <johan.palsson@stericsson.com>
>   *	Karl Komierowski <karl.komierowski@stericsson.com>
>   *	Arun R Murthy <arun.murthy@stericsson.com>
> + *	Imre Sunyi <imre.sunyi@sonymobile.com>
>   */
>  
>  #include <linux/init.h>
>  #include <linux/module.h>
>  #include <linux/device.h>
> +#include <linux/hrtimer.h>
>  #include <linux/interrupt.h>
>  #include <linux/delay.h>
>  #include <linux/slab.h>
> @@ -32,6 +35,12 @@
>  /* End-of-charge criteria counter */
>  #define EOC_COND_CNT			10
>  
> +/* One hour expressed in seconds */
> +#define ONE_HOUR_IN_SECONDS		3600
> +
> +/* Five minutes expressed in seconds */
> +#define FIVE_MINUTES_IN_SECONDS		300
> +
>  #define to_abx500_chargalg_device_info(x) container_of((x), \
>  	struct abx500_chargalg, chargalg_psy);
>  
> @@ -245,8 +254,8 @@ struct abx500_chargalg {
>  	struct delayed_work chargalg_periodic_work;
>  	struct delayed_work chargalg_wd_work;
>  	struct work_struct chargalg_work;
> -	struct timer_list safety_timer;
> -	struct timer_list maintenance_timer;
> +	struct hrtimer safety_timer;
> +	struct hrtimer maintenance_timer;
>  	struct kobject chargalg_kobject;
>  };
>  
> @@ -261,38 +270,47 @@ BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
>  
>  /**
>   * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
> - * @data:	pointer to the abx500_chargalg structure
> + * @timer:	pointer to the hrtimer structure
>   *
>   * This function gets called when the safety timer for the charger
>   * expires
>   */
> -static void abx500_chargalg_safety_timer_expired(unsigned long data)
> +static enum hrtimer_restart
> +abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
>  {
> -	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
> +	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
> +							safety_timer);

Empty line here.

>  	dev_err(di->dev, "Safety timer expired\n");
>  	di->events.safety_timer_expired = true;
>  
>  	/* Trigger execution of the algorithm instantly */
>  	queue_work(di->chargalg_wq, &di->chargalg_work);
> +
> +	return HRTIMER_NORESTART;
>  }
>  
>  /**
>   * abx500_chargalg_maintenance_timer_expired() - Expiration of
>   * the maintenance timer
> - * @i:		pointer to the abx500_chargalg structure
> + * @timer:	pointer to the timer structure
>   *
>   * This function gets called when the maintenence timer
>   * expires
>   */
> -static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
> +static enum hrtimer_restart
> +abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
> +
>  {
>  
> -	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
> +	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
> +							maintenance_timer);
>  	dev_dbg(di->dev, "Maintenance timer expired\n");
>  	di->events.maintenance_timer_expired = true;
>  
>  	/* Trigger execution of the algorithm instantly */
>  	queue_work(di->chargalg_wq, &di->chargalg_work);
> +
> +	return HRTIMER_NORESTART;
>  }
>  
>  /**
> @@ -392,19 +410,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
>   */
>  static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>  {
> -	unsigned long timer_expiration = 0;
> +	/* Charger-dependent expiration time in hours*/
> +	int timer_expiration = 0;
>  
>  	switch (di->chg_info.charger_type) {
>  	case AC_CHG:
> -		timer_expiration =
> -		round_jiffies(jiffies +
> -			(di->bat->main_safety_tmr_h * 3600 * HZ));
> +		timer_expiration = di->bat->main_safety_tmr_h;
>  		break;
>  
>  	case USB_CHG:
> -		timer_expiration =
> -		round_jiffies(jiffies +
> -			(di->bat->usb_safety_tmr_h * 3600 * HZ));
> +		timer_expiration = di->bat->usb_safety_tmr_h;
>  		break;
>  
>  	default:
> @@ -413,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>  	}
>  
>  	di->events.safety_timer_expired = false;
> -	di->safety_timer.expires = timer_expiration;
> -	if (!timer_pending(&di->safety_timer))
> -		add_timer(&di->safety_timer);
> -	else
> -		mod_timer(&di->safety_timer, timer_expiration);
> +	hrtimer_set_expires_range(&di->safety_timer,
> +		ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
> +		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
> +	hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);

OK, now we're chaning from timers to hrtimers, and their behaviour
might be different. I guess in this case you should check whether
old value 'before' new value, and if so, do nothing.

>  }
>  
>  /**
> @@ -428,8 +442,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>   */
>  static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
>  {
> -	di->events.safety_timer_expired = false;
> -	del_timer(&di->safety_timer);
> +	if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
> +		di->events.safety_timer_expired = false;
>  }
>  
>  /**
> @@ -444,17 +458,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
>  static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
>  	int duration)
>  {
> -	unsigned long timer_expiration;
> -
> -	/* Convert from hours to jiffies */
> -	timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
> -
> +	hrtimer_set_expires_range(&di->maintenance_timer,
> +		ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
> +		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
>  	di->events.maintenance_timer_expired = false;
> -	di->maintenance_timer.expires = timer_expiration;
> -	if (!timer_pending(&di->maintenance_timer))
> -		add_timer(&di->maintenance_timer);
> -	else
> -		mod_timer(&di->maintenance_timer, timer_expiration);
> +	hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
>  }
>  
>  /**
> @@ -466,8 +474,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
>   */
>  static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
>  {
> -	di->events.maintenance_timer_expired = false;
> -	del_timer(&di->maintenance_timer);
> +	if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
> +		di->events.maintenance_timer_expired = false;
>  }
>  
>  /**
> @@ -910,11 +918,11 @@ static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
>  	 * and charging will be stopped to protect the battery.
>  	 */
>  	if (di->batt_data.percent == 100 &&
> -		!timer_pending(&di->safety_timer)) {
> +		!hrtimer_active(&di->safety_timer)) {
>  		abx500_chargalg_start_safety_timer(di);
>  		dev_dbg(di->dev, "start safety timer\n");
>  	} else if (di->batt_data.percent != 100 &&
> -		timer_pending(&di->safety_timer)) {
> +		hrtimer_active(&di->safety_timer)) {
>  		abx500_chargalg_stop_safety_timer(di);
>  		dev_dbg(di->dev, "stop safety timer\n");
>  	}
> @@ -1932,10 +1940,16 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
>  	/* sysfs interface to enable/disbale charging from user space */
>  	abx500_chargalg_sysfs_exit(di);
>  
> +	hrtimer_cancel(&di->safety_timer);
> +	hrtimer_cancel(&di->maintenance_timer);
> +
> +	cancel_delayed_work_sync(&di->chargalg_periodic_work);
> +	cancel_delayed_work_sync(&di->chargalg_wd_work);
> +	cancel_work_sync(&di->chargalg_work);
> +

These cancel_ calls seem to be separate bugfixes?

>  	/* Delete the work queue */
>  	destroy_workqueue(di->chargalg_wq);
>  
> -	flush_scheduled_work();
>  	power_supply_unregister(&di->chargalg_psy);
>  	platform_set_drvdata(pdev, NULL);
>  	kfree(di);
> @@ -1987,15 +2001,13 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
>  		abx500_chargalg_external_power_changed;
>  
>  	/* Initilialize safety timer */
> -	init_timer(&di->safety_timer);
> +	hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
>  	di->safety_timer.function = abx500_chargalg_safety_timer_expired;
> -	di->safety_timer.data = (unsigned long) di;
>  
>  	/* Initilialize maintenance timer */
> -	init_timer(&di->maintenance_timer);
> +	hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
>  	di->maintenance_timer.function =
>  		abx500_chargalg_maintenance_timer_expired;
> -	di->maintenance_timer.data = (unsigned long) di;
>  
>  	/* Create a work queue for the chargalg */
>  	di->chargalg_wq =
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file
  2012-09-25 16:12 ` [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file mathieu.poirier
@ 2012-09-28  2:51   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:51 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:50AM -0600, mathieu.poirier@linaro.org wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
> 
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
[...]
> diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
> new file mode 100644
> index 0000000..5cfadc2
> --- /dev/null
> +++ b/drivers/power/ab8500_fg.h
> @@ -0,0 +1,201 @@
> +/*
> + * Copyright (C) ST-Ericsson AB 2012
> + *
> + * Main and Back-up battery management driver.
> + *
> + * Note: Backup battery management is required in case of Li-Ion battery and not
> + * for capacitive battery. HREF boards have capacitive battery and hence backup
> + * battery management is not used and the supported code is available in this
> + * driver.
> + *
> + * License Terms: GNU General Public License v2
> + * Author: Johan Palsson <johan.palsson@stericsson.com>
> + * Author: Karl Komierowski <karl.komierowski@stericsson.com>
> + */
> +

The change is dubious, since you don't seem to use the header anywhere
outside of _fg, so technically there's no need for it.

But if you want to logically separate structs and definitions, it's fine.

OK, but at least you have to check for multiple inclusions, I guess.
I.e. #ifndef __AB8500_FG_H...

> +#define MILLI_TO_MICRO			1000
> +#define FG_LSB_IN_MA			1627
> +#define QLSB_NANO_AMP_HOURS_X10		1129
> +#define INS_CURR_TIMEOUT		(3 * HZ)

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register
  2012-09-25 16:12 ` [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register mathieu.poirier
@ 2012-09-28  2:56   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:56 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:51AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> The newer AB's such as the AB8505, AB9540 etc include a
> USBLink1 Status register which detects a larger range of
> external devices. This should be used instead of the
> USBLine Status register.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Yang QU <yang.qu@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c |   22 ++++++++++++++++------
>  1 files changed, 16 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 3a97012..7f8f362 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -2258,8 +2258,13 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  	 * to start the charging process. but by jumping
>  	 * thru a few hoops it can be forced to start.
>  	 */
> -	ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
> -			AB8500_USB_LINE_STAT_REG, &val);
> +	if (is_ab8500(di->parent))
> +		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
> +					AB8500_USB_LINE_STAT_REG, &val);
> +	else
> +		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
> +					AB8500_USB_LINK1_STAT_REG, &val);

How about

	int reg = is_ab8500(di->parent) ? AB8500_USB_LINE_STAT_REG :
					  AB8500_USB_LINK1_STAT_REG;

	ret = abx500_get_register_interruptible(di->dev, AB8500_USB, reg, &val);

Shorter, clearer, and precisely fits into 80 columns -- must be good. :-)

> +
>  	if (ret >= 0)
>  		dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
>  	else
> @@ -2299,10 +2304,15 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  						AB8500_MCH_IPT_CURLVL_REG,
>  						0x01, 0x00);
>  			/*Check link status*/
> -			ret = abx500_get_register_interruptible(di->dev,
> -						AB8500_USB,
> -						AB8500_USB_LINE_STAT_REG,
> -						&val);
> +			if (is_ab8500(di->parent))
> +				ret = abx500_get_register_interruptible(di->dev,
> +					AB8500_USB, AB8500_USB_LINE_STAT_REG,
> +					&val);
> +			else
> +				ret = abx500_get_register_interruptible(di->dev,
> +					AB8500_USB, AB8500_USB_LINK1_STAT_REG,
> +					&val);
> +

Same here. Actually, isn't it exactly the same as above? If so, then just
factor it into its own function.

>  			dev_dbg(di->dev, "USB link status= 0x%02x\n",
>  				(val & link_status) >> USB_LINK_STATUS_SHIFT);
>  			di->invalid_charger_detect_state = 2;
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference
  2012-09-25 16:12 ` [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference mathieu.poirier
@ 2012-09-28  2:58   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  2:58 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:52AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> When the state of USB Charge detection is changed then the calls
> use a define for another register in other bank. This change
> creates a new define for the correct register and removes the
> magic numbers that are present.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> 
> Conflicts:
> 
> 	drivers/power/ab8500_charger.c

Stray comment.

> ---
>  drivers/power/ab8500_charger.c       |   11 +++++------
>  include/linux/mfd/abx500/ab8500-bm.h |    1 +
>  2 files changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index 7f8f362..afb4fda 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -51,6 +51,7 @@
>  #define VBUS_DET_DBNC1			0x01
>  #define OTP_ENABLE_WD			0x01
>  #define DROP_COUNT_RESET		0x01
> +#define USB_CH_DET			0x01
>  
>  #define MAIN_CH_INPUT_CURR_SHIFT	4
>  #define VBUS_IN_CURR_LIM_SHIFT		4
> @@ -2287,9 +2288,8 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  					USB_CH_ENA, USB_CH_ENA);
>  			/*Enable charger detection*/
>  			abx500_mask_and_set_register_interruptible(di->dev,
> -						AB8500_USB,
> -						AB8500_MCH_IPT_CURLVL_REG,
> -						0x01, 0x01);
> +					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
> +					USB_CH_DET, USB_CH_DET);
>  			di->invalid_charger_detect_state = 1;
>  			/*exit and wait for new link status interrupt.*/
>  			return;
> @@ -2300,9 +2300,8 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>  				"Invalid charger detected, state= 1\n");
>  			/*Stop charger detection*/
>  			abx500_mask_and_set_register_interruptible(di->dev,
> -						AB8500_USB,
> -						AB8500_MCH_IPT_CURLVL_REG,
> -						0x01, 0x00);
> +					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
> +					USB_CH_DET, 0x00);
>  			/*Check link status*/
>  			if (is_ab8500(di->parent))
>  				ret = abx500_get_register_interruptible(di->dev,
> diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
> index 721bd6d..6b69ad5 100644
> --- a/include/linux/mfd/abx500/ab8500-bm.h
> +++ b/include/linux/mfd/abx500/ab8500-bm.h
> @@ -23,6 +23,7 @@
>   * Bank : 0x5
>   */
>  #define AB8500_USB_LINE_STAT_REG	0x80
> +#define AB8500_USB_LINE_CTRL2_REG	0x82
>  #define AB8500_USB_LINK1_STAT_REG	0x94
>  
>  /*
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue.
  2012-09-25 16:12 ` [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue mathieu.poirier
@ 2012-09-28  3:00   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  3:00 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:12:53AM -0600, mathieu.poirier@linaro.org wrote:
> From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> 
> The patch for 426250 added a change to check for the quick

What is 426250? I guess it's some internal bug#... but since we don't have
access to that info, it's better to describe which upstream commit caused
this.

> re-attachment of the charger connection as an error in the
> AB8500 HW meant that a quick detach/attach wouldn't be
> detected.
> This patch isolates the original change so that newer AB's
> are not affected.
> 
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Martin SJOBLOM <martin.w.sjoblom@stericsson.com>
> Reviewed-by: Hakan BERG <hakan.berg@stericsson.com>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/abx500_chargalg.c |   11 ++++++-----
>  1 files changed, 6 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index c8849af..7a81e4e 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -1299,11 +1299,12 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *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__);
> -
> +	if (is_ab8500(di->parent)) {
> +		ret = abx500_chargalg_check_charger_enable(di);
> +		if (ret < 0)
> +			dev_err(di->dev, "Checking charger is enabled error");
> +			dev_err(di->dev, ": Returned Value %d\n", ret);

Ouch. Missing braces. No need for two dev_err().

> +	}
>  	/*
>  	 * First check if we have a charger connected.
>  	 * Also we don't allow charging of unknown batteries if configured
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 01/57] power: ab8500_bm: Charger current step-up/down
  2012-09-25 16:11 ` [PATCH 01/57] power: ab8500_bm: Charger current step-up/down mathieu.poirier
@ 2012-09-28  3:41   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  3:41 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:11:58AM -0600, mathieu.poirier@linaro.org wrote:
> From: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
> 
> There is no state machine in the AB to step up/down
> the charger current to avoid dips and spikes on VBUS
> and VBAT when charging is started.
> Instead this is implemented in SW

Some general comment: the commit messages use random line wrapping length.

It's usually a good idea to make lines no longer than 74 columns (since
'git log' adds some spaces before the message) , but too short or random
is also not pretty.

> Signed-off-by: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
> Signed-off-by: Mattias Wallin <mattias.wallin@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
> ---
>  drivers/power/ab8500_charger.c |  172 +++++++++++++++++++++++++++++++---------
>  1 files changed, 133 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index d4f0c98..3ceb788 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -77,6 +77,9 @@
>  /* Lowest charger voltage is 3.39V -> 0x4E */
>  #define LOW_VOLT_REG			0x4E
>  
> +/* Step up/down delay in us */
> +#define STEP_UDELAY			1000
> +
>  /* UsbLineStatus register - usb types */
>  enum ab8500_charger_link_status {
>  	USB_STAT_NOT_CONFIGURED,
> @@ -934,6 +937,88 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
>  }
>  
>  /**
> + * ab8500_charger_set_current() - set charger current
> + * @di:		pointer to the ab8500_charger structure
> + * @ich:	charger current, in mA
> + * @reg:	select what charger register to set
> + *
> + * Set charger current.
> + * There is no state machine in the AB to step up/down the charger
> + * current to avoid dips and spikes on MAIN, VBUS and VBAT when
> + * charging is started. Instead we need to implement
> + * this charger current step-up/down here.
> + * Returns error code in case of failure else 0(on success)

Random line wrapping...

Sophisticated editors (like vim :-), can format text for you. i.e. 'gqip'
command. I'm sure emacs can do this too.

> + */
> +static int ab8500_charger_set_current(struct ab8500_charger *di,
> +	int ich, int reg)
> +{
> +	int ret, i;
> +	int curr_index, prev_curr_index, shift_value;

One variable per line, please.

> +	u8 reg_value;
> +
> +	switch (reg) {
> +	case AB8500_MCH_IPT_CURLVL_REG:
> +		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
> +		curr_index = ab8500_current_to_regval(ich);
> +		break;
> +	case AB8500_USBCH_IPT_CRNTLVL_REG:
> +		shift_value = VBUS_IN_CURR_LIM_SHIFT;
> +		curr_index = ab8500_vbus_in_curr_to_regval(ich);
> +		break;
> +	case AB8500_CH_OPT_CRNTLVL_REG:
> +		shift_value = 0;
> +		curr_index = ab8500_current_to_regval(ich);
> +		break;
> +	default:
> +		dev_err(di->dev, "%s current register not valid\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (curr_index < 0) {
> +		dev_err(di->dev, "requested current limit out-of-range\n");
> +		return -ENXIO;
> +	}
> +
> +	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
> +		reg, &reg_value);
> +	if (ret < 0) {
> +		dev_err(di->dev, "%s read failed\n", __func__);
> +		return ret;
> +	}
> +	prev_curr_index = (reg_value >> shift_value);

No need for parenthesis.

> +	/* only update current if it's been changed */
> +	if (prev_curr_index == curr_index)
> +		return 0;
> +
> +	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
> +		__func__, ich, reg);
> +
> +	if (prev_curr_index > curr_index) {
> +		for (i = prev_curr_index - 1; i >= curr_index; i--) {
> +			ret = abx500_set_register_interruptible(di->dev,
> +				AB8500_CHARGER, reg, (u8) i << shift_value);
> +			if (ret) {
> +				dev_err(di->dev, "%s write failed\n", __func__);
> +				return ret;
> +			}
> +			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
> +		}
> +	} else {
> +		for (i = prev_curr_index + 1; i <= curr_index; i++) {
> +			ret = abx500_set_register_interruptible(di->dev,
> +				AB8500_CHARGER, reg, (u8) i << shift_value);
> +			if (ret) {
> +				dev_err(di->dev, "%s write failed\n", __func__);
> +				return ret;
> +			}
> +			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
> +		}
> +	}

Too much duplication.

Assuming that you need to preserve the order of the writes, i.e. if it
matters to the hw, I guess this (or something alike) will work:

write_current()
{
	uint start = prev_curr_index + 1;
	uint stop = curr_index + 1;
	int direction = 1;

	if (prev_curr_index > curr_index) {
		start = prev_curr_index - 1;
		stop = curr_index - 1;
		direction = -1;
	}

	for (i = start; i != stop; i += direction) {
		ret = abx500_set_register_interruptible(di->dev,
			AB8500_CHARGER, reg, (u8)i << shift_value);
		if (ret) {
			dev_err(di->dev, "%s write failed\n", __func__);
			return ret;
		}
		usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
	}
}

If the order doesn't matter, it could be even simpler.

> +	return ret;
> +}
> +
> +/**
>   * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
>   * @di:		pointer to the ab8500_charger structure
>   * @ich_in:	charger input current limit
> @@ -944,8 +1029,6 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
>  static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>  		int ich_in)
>  {
> -	int ret;
> -	int input_curr_index;
>  	int min_value;
>  
>  	/* We should always use to lowest current limit */
> @@ -964,19 +1047,38 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>  		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");
> -		return -ENXIO;
> -	}
> +	return ab8500_charger_set_current(di, min_value,
> +		AB8500_USBCH_IPT_CRNTLVL_REG);
> +}
>  
> -	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> -		AB8500_USBCH_IPT_CRNTLVL_REG,
> -		input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
> -	if (ret)
> -		dev_err(di->dev, "%s write failed\n", __func__);
> +/**
> + * ab8500_charger_set_main_in_curr() - set main charger input current
> + * @di:		pointer to the ab8500_charger structure
> + * @ich_in:	input charger current, in mA
> + *
> + * Set main charger input current.
> + * Returns error code in case of failure else 0(on success)
> + */
> +static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
> +	int ich_in)
> +{
> +	return ab8500_charger_set_current(di, ich_in,
> +		AB8500_MCH_IPT_CURLVL_REG);
> +}
>  
> -	return ret;
> +/**
> + * ab8500_charger_set_output_curr() - set charger output current
> + * @di:		pointer to the ab8500_charger structure
> + * @ich_out:	output charger current, in mA
> + *
> + * Set charger output current.
> + * Returns error code in case of failure else 0(on success)
> + */
> +static int ab8500_charger_set_output_curr(struct ab8500_charger *di,
> +	int ich_out)

Wrong indentation.

> +{
> +	return ab8500_charger_set_current(di, ich_out,
> +		AB8500_CH_OPT_CRNTLVL_REG);
>  }
>  
>  /**
> @@ -1088,18 +1190,18 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
>  			return ret;
>  		}
>  		/* MainChInputCurr: current that can be drawn from the charger*/
> -		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> -			AB8500_MCH_IPT_CURLVL_REG,
> -			input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
> +		ret = ab8500_charger_set_main_in_curr(di,
> +			di->bat->chg_params->ac_curr_max);
>  		if (ret) {
> -			dev_err(di->dev, "%s write failed\n", __func__);
> +			dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
> +				__func__);
>  			return ret;
>  		}
>  		/* ChOutputCurentLevel: protected output current */
> -		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> -			AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
> +		ret = ab8500_charger_set_output_curr(di, iset);
>  		if (ret) {
> -			dev_err(di->dev, "%s write failed\n", __func__);
> +			dev_err(di->dev,
> +			 "%s Failed to set ChOutputCurentLevel\n", __func__);
>  			return ret;
>  		}
>  
> @@ -1156,12 +1258,11 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
>  				return ret;
>  			}
>  
> -			ret = abx500_set_register_interruptible(di->dev,
> -				AB8500_CHARGER,
> -				AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
> +			ret = ab8500_charger_set_output_curr(di, 0);
>  			if (ret) {
>  				dev_err(di->dev,
> -					"%s write failed\n", __func__);
> +				 "%s Failed to set ChOutputCurentLevel\n",
> +				 __func__);

Weird, inconsistent indentation.

>  				return ret;
>  			}
>  		} else {
> @@ -1264,10 +1365,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>  			return ret;
>  		}
>  		/* ChOutputCurentLevel: protected output current */
> -		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> -			AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
> +		ret = ab8500_charger_set_output_curr(di, ich_out);
>  		if (ret) {
> -			dev_err(di->dev, "%s write failed\n", __func__);
> +			dev_err(di->dev,
> +			 "%s Failed to set ChOutputCurentLevel\n",
> +			 __func__);

Weird indentation.

>  			return ret;
>  		}
>  		/* Check if VBAT overshoot control should be enabled */
> @@ -1364,7 +1466,6 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
>  		int ich_out)
>  {
>  	int ret;
> -	int curr_index;
>  	struct ab8500_charger *di;
>  
>  	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
> @@ -1374,18 +1475,11 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
>  	else
>  		return -ENXIO;
>  
> -	curr_index = ab8500_current_to_regval(ich_out);
> -	if (curr_index < 0) {
> -		dev_err(di->dev,
> -			"Charger current too high, "
> -			"charging not started\n");
> -		return -ENXIO;
> -	}
> -
> -	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
> -		AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
> +	ret = ab8500_charger_set_output_curr(di, ich_out);
>  	if (ret) {
> -		dev_err(di->dev, "%s write failed\n", __func__);
> +		dev_err(di->dev,
> +		 "%s Failed to set ChOutputCurentLevel\n",
> +		 __func__);

Weird indentation. No need for the first line wrap.

>  		return ret;
>  	}
>  
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit
  2012-09-25 16:11 ` [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit mathieu.poirier
@ 2012-09-28  3:42   ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28  3:42 UTC (permalink / raw)
  To: mathieu.poirier; +Cc: linux-kernel, dwmw2

On Tue, Sep 25, 2012 at 10:11:59AM -0600, mathieu.poirier@linaro.org wrote:
> From: Kalle Komierowski <karl.komierowski@stericsson.com>
> 
> The CCMuxOffset bit is not kept set, this will force the columb counter
> of the AB8500 to use the measure offset calibration.
> This should increase the accuracy of the fuel gauge.
> 
> Signed-off-by: Kalle Komierowski <karl.komierowski@stericsson.com>
> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> ---
>  drivers/power/ab8500_fg.c |    8 ++++----
>  1 files changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index bf02225..af792a8 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -485,8 +485,9 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
>  		di->flags.fg_enabled = true;
>  	} else {
>  		/* Clear any pending read requests */
> -		ret = abx500_set_register_interruptible(di->dev,
> -			AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
> +		ret = abx500_mask_and_set_register_interruptible(di->dev,
> +			AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
> +			(RESET_ACCU | READ_REQ), 0);
>  		if (ret)
>  			goto cc_err;
>  
> @@ -1404,8 +1405,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
>  		sleep_time = di->bat->fg_params->init_timer;
>  
>  		/* Discard the first [x] seconds */
> -		if (di->init_cnt >
> -			di->bat->fg_params->init_discard_time) {
> +		if (di->init_cnt > di->bat->fg_params->init_discard_time) {

This change is OK, but it's cosmetic, and desires its own patch (you can
combine all cosmetic changes, which does not change the logic, into one
patch).

>  			ab8500_fg_calc_cap_discharge_voltage(di, true);
>  
>  			ab8500_fg_check_capacity_limits(di, true);
> -- 
> 1.7.5.4

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 33/57] power: u8500_charger: Delay for USB enumeration
  2012-09-27  7:42   ` Anton Vorontsov
@ 2012-09-28 16:56     ` Mathieu Poirier
  2012-09-28 17:09       ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 16:56 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 01:42 AM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:30AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
>>
>> If charging is started before USB enumeration of an
>> Accessory Charger Adapter has finished, the AB8500 will
>> generate a VBUS_ERROR. This in turn results in timeouts
>> and delays the enumeration with around 15 seconds.
>> This patch delays the charging and then ramps currents
>> slowly to avoid VBUS errors. The delay allows the enumeration
>> to have finished before charging is turned on.
>>
>> Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
>> ---
> [...]
>> @@ -264,17 +275,19 @@ struct ab8500_charger {
>>  	struct ab8500_charger_info usb;
>>  	struct regulator *regu;
>>  	struct workqueue_struct *charger_wq;
>> +	struct mutex usb_ipt_crnt_lock;
>>  	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;
>> +	struct delayed_work usb_state_changed_work;
>>  	struct delayed_work attach_work;
>>  	struct delayed_work ac_charger_attached_work;
>>  	struct delayed_work usb_charger_attached_work;
>> +	struct delayed_work vbus_drop_end_work;
>>  	struct work_struct ac_work;
>>  	struct work_struct detect_usb_type_work;
>>  	struct work_struct usb_link_status_work;
>> -	struct work_struct usb_state_changed_work;
> 
> This just moves line around. Doesn't belong to this patch.

This is moving 'usb_state_changed_work' from type 'struct work_struct'
to 'struct delayed_work'.  Is it that you want to see the change on the
same line rather than two different lines ?

> 
>>  	struct work_struct check_main_thermal_prot_work;
>>  	struct work_struct check_usb_thermal_prot_work;
>>  	struct usb_phy *usb_phy;
>> @@ -560,6 +573,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
>>  /**
>>   * ab8500_charger_detect_chargers() - Detect the connected chargers
>>   * @di:		pointer to the ab8500_charger structure
>> + * @probe:     if probe, don't delay and wait for HW
>>   *
>>   * Returns the type of charger connected.
>>   * For USB it will not mean we can actually charge from it
>> @@ -570,10 +584,10 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
>>   * Returns an integer value, that means,
>>   * NO_PW_CONN  no power supply is connected
>>   * AC_PW_CONN  if the AC power supply is connected
>> - * USB_PW_CONN  if the USB power supply is connected
>> + * USB_PW_CONN	if the USB power supply is connected
> 
> Cosmetic change... doesn't belong to this patch, I guess.
> 
>>   * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
>>   */
>> -static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
>> +static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
>>  {
>>  	int result = NO_PW_CONN;
>>  	int ret;
>> @@ -591,6 +605,15 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
>>  		result = AC_PW_CONN;
>>  
>>  	/* Check for USB charger */
>> +	if (!probe) {
>> +		/*
>> +		 * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
>> +		 * when disconnecting ACA even though no
>> +		 * charger was connected. Try waiting a little
>> +		 * longer than the 100 ms of VBUS_DET_DBNC100...
>> +		 */
>> +		msleep(110);
>> +	}
>>  	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>>  		AB8500_CH_USBCH_STAT1_REG, &val);
>>  	if (ret < 0) {
>> @@ -598,6 +621,9 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
>>  		return ret;
>>  	}
>>  
>> +	dev_dbg(di->dev,
>> +		"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__, val);
>> +
>>  	if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
>>  		result |= USB_PW_CONN;
>>  
>> @@ -620,33 +646,47 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>>  
>>  	di->usb_device_is_unrecognised = false;
>>  
>> +	/*
>> +	 * Platform only supports USB 2.0.
>> +	 * This means that charging current from USB source
>> +	 * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
>> +	 * should set USB_CH_IP_CUR_LVL_0P5.
>> +	 */
>> +
>>  	switch (link_status) {
>>  	case USB_STAT_STD_HOST_NC:
>>  	case USB_STAT_STD_HOST_C_NS:
>>  	case USB_STAT_STD_HOST_C_S:
>>  		dev_dbg(di->dev, "USB Type - Standard host is ");
>>  		dev_dbg(di->dev, "detected through USB driver\n");
>> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
>> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
>> +		di->is_usb_host = true;
>> +		di->is_aca_rid = 0;
>>  		break;
>>  	case USB_STAT_HOST_CHG_HS_CHIRP:
>>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
>> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>> -				di->max_usb_in_curr);
>> +		di->is_usb_host = true;
>> +		di->is_aca_rid = 0;
>>  		break;
>>  	case USB_STAT_HOST_CHG_HS:
>> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
>> +		di->is_usb_host = true;
>> +		di->is_aca_rid = 0;
>> +		break;
>>  	case USB_STAT_ACA_RID_C_HS:
>>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
>> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>> -				di->max_usb_in_curr);
>> +		di->is_usb_host = false;
>> +		di->is_aca_rid = 0;
>>  		break;
>>  	case USB_STAT_ACA_RID_A:
>>  		/*
>>  		 * Dedicated charger level minus maximum current accessory
>> -		 * can consume (300mA). Closest level is 1100mA
>> +		 * can consume (900mA). Closest level is 500mA
>>  		 */
>> -		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
>> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>> -				di->max_usb_in_curr);
>> +		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
>> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
>> +		di->is_usb_host = false;
>> +		di->is_aca_rid = 1;
>>  		break;
>>  	case USB_STAT_ACA_RID_B:
>>  		/*
>> @@ -656,14 +696,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
>>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
>>  		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>>  				di->max_usb_in_curr);
>> +		di->is_usb_host = false;
>> +		di->is_aca_rid = 1;
>>  		break;
>>  	case USB_STAT_HOST_CHG_NM:
>> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
>> +		di->is_usb_host = true;
>> +		di->is_aca_rid = 0;
>> +		break;
>>  	case USB_STAT_DEDICATED_CHG:
>> -	case USB_STAT_ACA_RID_C_NM:
>> +		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
>> +		di->is_usb_host = false;
>> +		di->is_aca_rid = 0;
>> +		break;
>>  	case USB_STAT_ACA_RID_C_HS_CHIRP:
>> +		case USB_STAT_ACA_RID_C_NM:
>>  		di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
>> -		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
>> -				di->max_usb_in_curr);
>> +		di->is_usb_host = false;
>> +		di->is_aca_rid = 1;
>>  		break;
>>  	case USB_STAT_NOT_CONFIGURED:
>>  		if (di->vbus_detected) {
>> @@ -780,6 +830,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
>>  		ret = abx500_get_register_interruptible(di->dev,
>>  			AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
>>  			&val);
>> +		dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
>> +							 __func__, val);
>>  		if (ret < 0) {
>>  			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>>  			return ret;
>> @@ -795,6 +847,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
>>  			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>>  			return ret;
>>  		}
>> +		dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n",
>> +							 __func__, val);
>>  		/*
>>  		 * Until the IT source register is read the UsbLineStatus
>>  		 * register is not updated, hence doing the same
>> @@ -1054,69 +1108,128 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
>>  static int ab8500_charger_set_current(struct ab8500_charger *di,
>>  	int ich, int reg)
>>  {
>> -	int ret, i;
>> -	int curr_index, prev_curr_index, shift_value;
>> +	int ret = 0;
> 
> = 0 initializer is not needed.
> 
>> +	int auto_curr_index, curr_index, prev_curr_index, shift_value, i;
> 
> Should be one variable definition per line.
> 
>>  	u8 reg_value;
>> +	u32 step_udelay;
>> +	bool no_stepping = false;
>> +
>> +	atomic_inc(&di->current_stepping_sessions);
>> +
>> +	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>> +		reg, &reg_value);
>> +	if (ret < 0) {
>> +		dev_err(di->dev, "%s read failed\n", __func__);
>> +		goto exit_set_current;
>> +	}
>>  
>>  	switch (reg) {
>>  	case AB8500_MCH_IPT_CURLVL_REG:
>>  		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
>> +		prev_curr_index = (reg_value >> shift_value);
>>  		curr_index = ab8500_current_to_regval(ich);
>> +		step_udelay = STEP_UDELAY;
>> +		if (!di->ac.charger_connected)
>> +			no_stepping = true;
>>  		break;
>>  	case AB8500_USBCH_IPT_CRNTLVL_REG:
>>  		shift_value = VBUS_IN_CURR_LIM_SHIFT;
>> +		prev_curr_index = (reg_value >> shift_value);
>>  		curr_index = ab8500_vbus_in_curr_to_regval(ich);
>> +		step_udelay = STEP_UDELAY * 100;
>> +
>> +		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>> +					AB8500_CH_USBCH_STAT2_REG, &reg_value);
>> +
>> +		if (ret < 0) {
>> +			dev_err(di->dev, "%s read failed\n", __func__);
>> +			goto exit_set_current;
>> +		}
>> +		auto_curr_index =
>> +			reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
> 
> This fits into one line ,no need for line wrap.
> 
>> +
>> +		dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
>> +			__func__,
> 
> __func__ can go into previous line.
> 
>> +			ab8500_charger_vbus_in_curr_map[auto_curr_index]);
>> +
>> +		prev_curr_index = min(prev_curr_index, auto_curr_index);
>> +
>> +		if (!di->usb.charger_connected)
>> +			no_stepping = true;
>>  		break;
>>  	case AB8500_CH_OPT_CRNTLVL_REG:
>>  		shift_value = 0;
>> +		prev_curr_index = (reg_value >> shift_value);
>>  		curr_index = ab8500_current_to_regval(ich);
>> +		if (curr_index == 0)
>> +			step_udelay = STEP_UDELAY;
>> +		else if ((curr_index - prev_curr_index) > 1)
>> +			step_udelay = STEP_UDELAY * 100;
>> +		else
>> +			step_udelay = STEP_UDELAY;
> 		
> Just
> 
> 		step_udelay = STEP_UDELAY;
> 		if (curr_index && (curr_index - prev_curr_index) > 1)
> 			step_udelay *= 100;
> 
>> +
>> +		if (!di->usb.charger_connected && !di->ac.charger_connected)
>> +			no_stepping = true;
>> +
>>  		break;
>>  	default:
>>  		dev_err(di->dev, "%s current register not valid\n", __func__);
>> -		return -ENXIO;
>> +		ret = -ENXIO;
>> +		goto exit_set_current;
>>  	}
>>  
>>  	if (curr_index < 0) {
>>  		dev_err(di->dev, "requested current limit out-of-range\n");
>> -		return -ENXIO;
>> -	}
>> -
>> -	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
>> -		reg, &reg_value);
>> -	if (ret < 0) {
>> -		dev_err(di->dev, "%s read failed\n", __func__);
>> -		return ret;
>> +		ret = -ENXIO;
>> +		goto exit_set_current;
>>  	}
>> -	prev_curr_index = (reg_value >> shift_value);
>>  
>>  	/* only update current if it's been changed */
>> -	if (prev_curr_index == curr_index)
>> -		return 0;
>> +	if (prev_curr_index == curr_index) {
>> +		dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
>> +			__func__, reg);
>> +		ret = 0;
>> +		goto exit_set_current;
>> +	}
>>  
>>  	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
>>  		__func__, ich, reg);
>>  
>> -	if (prev_curr_index > curr_index) {
>> +	if (no_stepping) {
>> +		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
>> +					reg, (u8) curr_index << shift_value);
> 
> No space before curr_index.
> 
>> +		if (ret)
>> +			dev_err(di->dev, "%s write failed\n", __func__);
>> +	} else if (prev_curr_index > curr_index) {
>>  		for (i = prev_curr_index - 1; i >= curr_index; i--) {
>> +			dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
>> +				(u8) i << shift_value, reg);
> 
> ditto.
> 
>>  			ret = abx500_set_register_interruptible(di->dev,
>>  				AB8500_CHARGER, reg, (u8) i << shift_value);
>>  			if (ret) {
>>  				dev_err(di->dev, "%s write failed\n", __func__);
>> -				return ret;
>> +				goto exit_set_current;
>>  			}
>> -			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
>> +			if (i != curr_index)
>> +				usleep_range(step_udelay, step_udelay * 2);
>>  		}
>>  	} else {
>>  		for (i = prev_curr_index + 1; i <= curr_index; i++) {
>> +			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
>> +				(u8) i << shift_value, reg);
> 
> ditto.
> 
>>  			ret = abx500_set_register_interruptible(di->dev,
>>  				AB8500_CHARGER, reg, (u8) i << shift_value);
>>  			if (ret) {
>>  				dev_err(di->dev, "%s write failed\n", __func__);
>> -				return ret;
>> +				goto exit_set_current;
>>  			}
>> -			usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
>> +			if (i != curr_index)
>> +				usleep_range(step_udelay, step_udelay * 2);
>>  		}
>>  	}
>> +
>> +exit_set_current:
>> +	atomic_dec(&di->current_stepping_sessions);
>>  	return ret;
>>  }
>>  
>> @@ -1132,6 +1245,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>>  		int ich_in)
>>  {
>>  	int min_value;
>> +	int ret;
>>  
>>  	/* We should always use to lowest current limit */
>>  	min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
>> @@ -1149,8 +1263,14 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
>>  		break;
>>  	}
>>  
>> -	return ab8500_charger_set_current(di, min_value,
>> +	dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
>> +
>> +	mutex_lock(&di->usb_ipt_crnt_lock);
>> +	ret = ab8500_charger_set_current(di, min_value,
>>  		AB8500_USBCH_IPT_CRNTLVL_REG);
>> +	mutex_unlock(&di->usb_ipt_crnt_lock);
>> +
>> +	return ret;
>>  }
>>  
>>  /**
>> @@ -1460,25 +1580,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>>  			dev_err(di->dev, "%s write failed\n", __func__);
>>  			return ret;
>>  		}
>> -		/* USBChInputCurr: current that can be drawn from the usb */
>> -		ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
>> -		if (ret) {
>> -			dev_err(di->dev, "setting USBChInputCurr failed\n");
>> -			return ret;
>> -		}
>> -		/* ChOutputCurentLevel: protected output current */
>> -		ret = ab8500_charger_set_output_curr(di, ich_out);
>> -		if (ret) {
>> -			dev_err(di->dev,
>> -			 "%s Failed to set ChOutputCurentLevel\n",
>> -			 __func__);
>> -			return ret;
>> -		}
>>  		/* Check if VBAT overshoot control should be enabled */
>>  		if (!di->bat->enable_overshoot)
>>  			overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
>>  
>>  		/* Enable USB Charger */
>> +		dev_dbg(di->dev,
>> +			"Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
>>  		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
>>  			AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
>>  		if (ret) {
>> @@ -1491,11 +1599,30 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>>  		if (ret < 0)
>>  			dev_err(di->dev, "failed to enable LED\n");
>>  
>> +		di->usb.charger_online = 1;
>> +
>> +		/* USBChInputCurr: current that can be drawn from the usb */
>> +		ret = ab8500_charger_set_vbus_in_curr(di,
>> +					di->max_usb_in_curr);
>> +		if (ret) {
>> +			dev_err(di->dev, "setting USBChInputCurr failed\n");
>> +			return ret;
>> +		}
>> +
>> +		/* ChOutputCurentLevel: protected output current */
>> +		ret = ab8500_charger_set_output_curr(di, ich_out);
>> +		if (ret) {
>> +			dev_err(di->dev,
>> +				"%s Failed to set ChOutputCurentLevel\n",
>> +				__func__);
>> +			return ret;
>> +		}
>> +
>>  		queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
>>  
>> -		di->usb.charger_online = 1;
>>  	} else {
>>  		/* Disable USB charging */
>> +		dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
>>  		ret = abx500_set_register_interruptible(di->dev,
>>  			AB8500_CHARGER,
>>  			AB8500_USBCH_CTRL1_REG, 0);
>> @@ -1508,7 +1635,21 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>>  		ret = ab8500_charger_led_en(di, false);
>>  		if (ret < 0)
>>  			dev_err(di->dev, "failed to disable LED\n");
>> +		/* USBChInputCurr: current that can be drawn from the usb */
>> +		ret = ab8500_charger_set_vbus_in_curr(di, 0);
>> +		if (ret) {
>> +			dev_err(di->dev, "setting USBChInputCurr failed\n");
>> +			return ret;
>> +		}
>>  
>> +		/* ChOutputCurentLevel: protected output current */
>> +		ret = ab8500_charger_set_output_curr(di, 0);
>> +		if (ret) {
>> +			dev_err(di->dev,
>> +				"%s Failed to reset ChOutputCurentLevel\n",
>> +				__func__);
>> +			return ret;
>> +		}
>>  		di->usb.charger_online = 0;
>>  		di->usb.wd_expired = false;
>>  
>> @@ -1791,7 +1932,7 @@ static void ab8500_charger_ac_work(struct work_struct *work)
>>  	 * synchronously, we have the check if the main charger is
>>  	 * connected by reading the status register
>>  	 */
>> -	ret = ab8500_charger_detect_chargers(di);
>> +	ret = ab8500_charger_detect_chargers(di, false);
>>  	if (ret < 0)
>>  		return;
>>  
>> @@ -1899,16 +2040,18 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>>  	 * synchronously, we have the check if is
>>  	 * connected by reading the status register
>>  	 */
>> -	ret = ab8500_charger_detect_chargers(di);
>> +	ret = ab8500_charger_detect_chargers(di, false);
>>  	if (ret < 0)
>>  		return;
>>  
>>  	if (!(ret & USB_PW_CONN)) {
>> -		di->vbus_detected = 0;
>> +		dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
>> +		di->vbus_detected = false;
>>  		ab8500_charger_set_usb_connected(di, false);
>>  		ab8500_power_supply_changed(di, &di->usb_chg.psy);
>>  	} else {
>> -		di->vbus_detected = 1;
>> +		dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
>> +		di->vbus_detected = true;
>>  
>>  		if (is_ab8500_1p1_or_earlier(di->parent)) {
>>  			ret = ab8500_charger_detect_usb_type(di);
>> @@ -1918,7 +2061,8 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>>  							    &di->usb_chg.psy);
>>  			}
>>  		} else {
>> -			/* For ABB cut2.0 and onwards we have an IRQ,
>> +			/*
>> +			 * For ABB cut2.0 and onwards we have an IRQ,
> 
> This change is correct, but it doesn't belong to this patch.
> 
>>  			 * USB_LINK_STATUS that will be triggered when the USB
>>  			 * link status changes. The exception is USB connected
>>  			 * during startup. Then we don't get a
>> @@ -1939,7 +2083,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
>>  }
>>  
>>  /**
>> - * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
>> + * ab8500_charger_usb_link_attach_work() - work to detect USB type
>>   * @work:	pointer to the work_struct structure
>>   *
>>   * Detect the type of USB plugged
>> @@ -1979,10 +2123,10 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>>  
>>  	/*
>>  	 * Since we can't be sure that the events are received
>> -	 * synchronously, we have the check if  is
>> +	 * synchronously, we have the check if	is
> 
> cosmetic change, it's OK, but in separate patch.
> 
>>  	 * connected by reading the status register
>>  	 */
>> -	detected_chargers = ab8500_charger_detect_chargers(di);
>> +	detected_chargers = ab8500_charger_detect_chargers(di, false);
>>  	if (detected_chargers < 0)
>>  		return;
>>  
>> @@ -2009,7 +2153,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>>  			abx500_mask_and_set_register_interruptible(di->dev,
>>  						AB8500_CHARGER,
>>  						AB8500_USBCH_CTRL1_REG,
>> -						0x01, 0x01)
>> +						0x01, 0x01);
> 
> Ouch. Was it compilable before this change? It's not bisectable.

I just went cross-eyed on this one - I'll look at it.

> 
>>  			/*Enable charger detection*/
>>  			abx500_mask_and_set_register_interruptible(di->dev,
>>  						AB8500_USB,
>> @@ -2042,32 +2186,46 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
>>  	}
>>  
>>  	if (!(detected_chargers & USB_PW_CONN)) {
>> -		di->vbus_detected = 0;
>> +		di->vbus_detected = false;
> 
> Nope. Firstly, 0 is OK for bool. Secondly, even if you want to use
> false instead of 0, this is cosmetic change, and should go separately.
> 
>>  		ab8500_charger_set_usb_connected(di, false);
>>  		ab8500_power_supply_changed(di, &di->usb_chg.psy);
>> -	} else {
>> -		di->vbus_detected = 1;
>> -		ret = ab8500_charger_read_usb_type(di);
>> -		if (!ret) {
>> -			if (di->usb_device_is_unrecognised) {
>> -				dev_dbg(di->dev,
>> -					"Potential Legacy Charger device. "
>> -					"Delay work for %d msec for USB enum "
>> -					"to finish",
>> -					WAIT_FOR_USB_ENUMERATION);
>> -				queue_delayed_work(di->charger_wq,
>> -					&di->attach_work,
>> -					msecs_to_jiffies
>> -						(WAIT_FOR_USB_ENUMERATION));
>> -			} else {
>> -				queue_delayed_work(di->charger_wq,
>> -							&di->attach_work, 0);
>> -			}
>> -		} else if (ret == -ENXIO) {
>> +		return;
>> +	}
>> +
>> +	dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
>> +	di->vbus_detected = true;
>> +	ret = ab8500_charger_read_usb_type(di);
>> +	if (ret) {
>> +		if (ret == -ENXIO) {
>>  			/* No valid charger type detected */
>>  			ab8500_charger_set_usb_connected(di, false);
>>  			ab8500_power_supply_changed(di, &di->usb_chg.psy);
>>  		}
>> +		return;
>> +	}
>> +
>> +	if (di->usb_device_is_unrecognised) {
>> +		dev_dbg(di->dev,
>> +			"Potential Legacy Charger device. "
>> +			"Delay work for %d msec for USB enum "
>> +			"to finish",
>> +			WAIT_ACA_RID_ENUMERATION);
>> +		queue_delayed_work(di->charger_wq,
>> +			&di->attach_work,
>> +			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
>> +	} else if (di->is_aca_rid == 1) {
>> +		/* Only wait once */
>> +		di->is_aca_rid++;
>> +		dev_dbg(di->dev,
>> +			"%s Wait %d msec for USB enum to finish",
> 
> This can go into previous line.
> 
>> +			__func__, WAIT_ACA_RID_ENUMERATION);
>> +		queue_delayed_work(di->charger_wq,
>> +			&di->attach_work,
> 
> These two lines can be merged.
> 
>> +			msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
>> +	} else {
>> +		queue_delayed_work(di->charger_wq,
>> +			&di->attach_work,
>> +			0);
> 
> This fits into one line.
> 
>>  	}
>>  }
>>  
>> @@ -2077,24 +2235,20 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
>>  	unsigned long flags;
>>  
>>  	struct ab8500_charger *di = container_of(work,
>> -		struct ab8500_charger, usb_state_changed_work);
>> +		struct ab8500_charger, usb_state_changed_work.work);
>>  
>> -	if (!di->vbus_detected)
>> +	if (!di->vbus_detected) {
>> +		dev_dbg(di->dev,
>> +			"%s !di->vbus_detected\n",
>> +			__func__);
> 
> No wrapping necessary.
> 
>>  		return;
>> +	}
>>  
>>  	spin_lock_irqsave(&di->usb_state.usb_lock, flags);
>> -	di->usb_state.usb_changed = false;
>> +	di->usb_state.state = di->usb_state.state_tmp;
>> +	di->usb_state.usb_current = di->usb_state.usb_current_tmp;
>>  	spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
>>  
>> -	/*
>> -	 * wait for some time until you get updates from the usb stack
>> -	 * and negotiations are completed
>> -	 */
>> -	msleep(250);
>> -
>> -	if (di->usb_state.usb_changed)
>> -		return;
>> -
>>  	dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
>>  		__func__, di->usb_state.state, di->usb_state.usb_current);
>>  
>> @@ -2332,6 +2486,21 @@ static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
>>  	return IRQ_HANDLED;
>>  }
>>  
>> +static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
>> +{
>> +	struct ab8500_charger *di = container_of(work,
>> +		struct ab8500_charger, vbus_drop_end_work.work);
>> +
>> +	di->flags.vbus_drop_end = false;
>> +
>> +	/* Reset the drop counter */
>> +	abx500_set_register_interruptible(di->dev,
>> +				AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
>> +
>> +	if (di->usb.charger_connected)
>> +		ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
>> +}
>> +
>>  /**
>>   * ab8500_charger_vbusdetf_handler() - VBUS falling detected
>>   * @irq:       interrupt number
>> @@ -2343,6 +2512,7 @@ static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
>>  {
>>  	struct ab8500_charger *di = _di;
>>  
>> +	di->vbus_detected = false;
>>  	dev_dbg(di->dev, "VBUS falling detected\n");
>>  	queue_work(di->charger_wq, &di->detect_usb_type_work);
>>  
>> @@ -2470,6 +2640,25 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
>>  }
>>  
>>  /**
>> + * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
>> + * @irq:       interrupt number
>> + * @_di:       pointer to the ab8500_charger structure
>> + *
>> + * Returns IRQ status(IRQ_HANDLED)
>> + */
>> +static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
>> +{
>> +	struct ab8500_charger *di = _di;
>> +
>> +	dev_dbg(di->dev, "VBUS charger drop ended\n");
>> +	di->flags.vbus_drop_end = true;
>> +	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
>> +						round_jiffies(30 * HZ));
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>>   * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
>>   * @irq:       interrupt number
>>   * @_di:       pointer to the ab8500_charger structure
>> @@ -2559,9 +2748,9 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy,
>>  
>>  /**
>>   * ab8500_charger_usb_get_property() - get the usb properties
>> - * @psy:        pointer to the power_supply structure
>> - * @psp:        pointer to the power_supply_property structure
>> - * @val:        pointer to the power_supply_propval union
>> + * @psy:	pointer to the power_supply structure
>> + * @psp:	pointer to the power_supply_property structure
>> + * @val:	pointer to the power_supply_propval union
> 
> Stray cosmetic changes, should go via a separate patch.
> 
>>   * This function gets called when an application tries to get the usb
>>   * properties by reading the sysfs files.
>> @@ -2739,6 +2928,12 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
>>  		goto out;
>>  	}
>>  
>> +	ret = ab8500_charger_led_en(di, false);
>> +	if (ret < 0) {
>> +		dev_err(di->dev, "failed to disable LED\n");
>> +		goto out;
>> +	}
>> +
>>  	/* Backup battery voltage and current */
>>  	ret = abx500_set_register_interruptible(di->dev,
>>  		AB8500_RTC,
>> @@ -2778,6 +2973,7 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
>>  	{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
>>  	{"VBUS_OVV", ab8500_charger_vbusovv_handler},
>>  	{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
>> +	{"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
>>  };
>>  
>>  static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
>> @@ -2814,13 +3010,15 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
>>  		__func__, bm_usb_state, mA);
>>  
>>  	spin_lock(&di->usb_state.usb_lock);
>> -	di->usb_state.usb_changed = true;
>> +	di->usb_state.state_tmp = bm_usb_state;
>> +	di->usb_state.usb_current_tmp = mA;
>>  	spin_unlock(&di->usb_state.usb_lock);
>>  
>> -	di->usb_state.state = bm_usb_state;
>> -	di->usb_state.usb_current = mA;
>> -
>> -	queue_work(di->charger_wq, &di->usb_state_changed_work);
>> +	/*
>> +	 * wait for some time until you get updates from the usb stack
>> +	 * and negotiations are completed
>> +	 */
>> +	queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
>>  
>>  	return NOTIFY_OK;
>>  }
>> @@ -2860,6 +3058,9 @@ static int ab8500_charger_resume(struct platform_device *pdev)
>>  			&di->check_hw_failure_work, 0);
>>  	}
>>  
>> +	if (di->flags.vbus_drop_end)
>> +		queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
>> +
>>  	return 0;
>>  }
>>  
>> @@ -2872,6 +3073,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>>  	if (delayed_work_pending(&di->check_hw_failure_work))
>>  		cancel_delayed_work(&di->check_hw_failure_work);
>>  
>> +	if (delayed_work_pending(&di->vbus_drop_end_work))
>> +		cancel_delayed_work(&di->vbus_drop_end_work);
>> +
>>  	flush_delayed_work_sync(&di->attach_work);
>>  	flush_delayed_work_sync(&di->usb_charger_attached_work);
>>  	flush_delayed_work_sync(&di->ac_charger_attached_work);
>> @@ -2883,11 +3087,14 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>>  	flush_work_sync(&di->ac_work);
>>  	flush_work_sync(&di->detect_usb_type_work);
>>  
>> +	if (atomic_read(&di->current_stepping_sessions))
>> +		return -EAGAIN;
>> +
>>  	return 0;
>>  }
>>  #else
>> -#define ab8500_charger_suspend      NULL
>> -#define ab8500_charger_resume       NULL
>> +#define ab8500_charger_suspend	    NULL
>> +#define ab8500_charger_resume	    NULL
> 
> Cosmetic, doesn't belong to this patch.
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 33/57] power: u8500_charger: Delay for USB enumeration
  2012-09-28 16:56     ` Mathieu Poirier
@ 2012-09-28 17:09       ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28 17:09 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Fri, Sep 28, 2012 at 10:56:50AM -0600, Mathieu Poirier wrote:
[...]
> >> +	struct delayed_work usb_state_changed_work;
> >>  	struct delayed_work attach_work;
> >>  	struct delayed_work ac_charger_attached_work;
> >>  	struct delayed_work usb_charger_attached_work;
> >> +	struct delayed_work vbus_drop_end_work;
> >>  	struct work_struct ac_work;
> >>  	struct work_struct detect_usb_type_work;
> >>  	struct work_struct usb_link_status_work;
> >> -	struct work_struct usb_state_changed_work;
> > 
> > This just moves line around. Doesn't belong to this patch.
> 
> This is moving 'usb_state_changed_work' from type 'struct work_struct'
> to 'struct delayed_work'.  Is it that you want to see the change on the
> same line rather than two different lines ?

Oh, I see. No, then it's fine as is.

Thanks!

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV
  2012-09-27  3:36   ` Anton Vorontsov
@ 2012-09-28 18:24     ` Mathieu Poirier
  2012-09-28 18:26       ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:24 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-26 09:36 PM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:14AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Hakan Berg <hakan.berg@stericsson.com>
>>
>> Add support for the battery over-voltage situation
>>
>> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
>> ---
>>  drivers/power/ab8500_fg.c |   32 ++++++++++++++++----------------
>>  1 files changed, 16 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
>> index c4d9307..8507254 100644
>> --- a/drivers/power/ab8500_fg.c
>> +++ b/drivers/power/ab8500_fg.c
>> @@ -1842,24 +1842,26 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
>>  	 * If we have had a battery over-voltage situation,
>>  	 * check ovv-bit to see if it should be reset.
>>  	 */
>> -	if (di->flags.bat_ovv) {
>> -		ret = abx500_get_register_interruptible(di->dev,
>> -			AB8500_CHARGER, AB8500_CH_STAT_REG,
>> -			&reg_value);
>> -		if (ret < 0) {
>> -			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>> -			return;
>> -		}
>> -		if ((reg_value & BATT_OVV) != BATT_OVV) {
>> -			dev_dbg(di->dev, "Battery recovered from OVV\n");
>> -			di->flags.bat_ovv = false;
>> +	ret = abx500_get_register_interruptible(di->dev,
>> +		AB8500_CHARGER, AB8500_CH_STAT_REG,
>> +		&reg_value);
>> +	if (ret < 0) {
>> +		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
>> +		return;
>> +	}
>> +	if ((reg_value & BATT_OVV) == BATT_OVV) {
>> +		if (!di->flags.bat_ovv) {
>> +			dev_dbg(di->dev, "Battery OVV\n");
>> +			di->flags.bat_ovv = true;
>>  			power_supply_changed(&di->fg_psy);
>> -			return;
>>  		}
>> -
>>  		/* Not yet recovered from ovv, reschedule this test */
>>  		queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
>> -				   round_jiffies(HZ));
>> +			HZ);
> 
> Why this change? I.e. round_jiffies(HZ) -> HZ?
> 
> Yes, it seems like round_jiffies(HZ) is not needed since HZ itself is a
> full second.. But the change itself does not belong to this patch.

I agree with your point of view.  How do we fix it now ?  Do you think
it's worth crafting a one-line patch ?

> 
>> +		} else {
>> +			dev_dbg(di->dev, "Battery recovered from OVV\n");
>> +			di->flags.bat_ovv = false;
>> +			power_supply_changed(&di->fg_psy);
>>  	}
>>  }
>>  
>> @@ -2039,8 +2041,6 @@ static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
>>  	struct ab8500_fg *di = _di;
>>  
>>  	dev_dbg(di->dev, "Battery OVV\n");
>> -	di->flags.bat_ovv = true;
>> -	power_supply_changed(&di->fg_psy);
>>  
>>  	/* Schedule a new HW failure check */
>>  	queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
>> -- 
>> 1.7.5.4


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 18/57] power: Add sysfs interfaces for capacity
  2012-09-27  7:08   ` Anton Vorontsov
@ 2012-09-28 18:26     ` Mathieu Poirier
  2012-09-28 19:11       ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:26 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 01:08 AM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:15AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Daniel WILLERUD <daniel.willerud@stericsson.com>
>>
>> Switchable depending on whether capacity scaling is enabled
>>
>> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
>> Signed-off-by: Daniel WILLERUD <daniel.willerud@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
>> ---
>>  drivers/power/ab8500_fg.c |   57 ++++++++++++++++++++++++++++++++++++++++++++-
>>  1 files changed, 56 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
>> index 8507254..46010ec 100644
>> --- a/drivers/power/ab8500_fg.c
>> +++ b/drivers/power/ab8500_fg.c
>> @@ -266,7 +266,6 @@ static enum power_supply_property ab8500_fg_props[] = {
>>  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
>>  	POWER_SUPPLY_PROP_CHARGE_FULL,
>>  	POWER_SUPPLY_PROP_CHARGE_NOW,
>> -	POWER_SUPPLY_PROP_CAPACITY,
> [...]
>> +static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
>> +	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
>> +};
> 
> I don't understand the rationale behind this patch. Why remove normal
> power supply property, and make your own with the same name? Something
> isn't right...

The similarity in the naming convention it a coincidence here.  In one
case it's a enum and the other sysfs attribute.  Could you expand on
your suspicions ?

> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV
  2012-09-28 18:24     ` Mathieu Poirier
@ 2012-09-28 18:26       ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28 18:26 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Fri, Sep 28, 2012 at 12:24:53PM -0600, Mathieu Poirier wrote:
[...]
> >>  		/* Not yet recovered from ovv, reschedule this test */
> >>  		queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
> >> -				   round_jiffies(HZ));
> >> +			HZ);
> > 
> > Why this change? I.e. round_jiffies(HZ) -> HZ?
> > 
> > Yes, it seems like round_jiffies(HZ) is not needed since HZ itself is a
> > full second.. But the change itself does not belong to this patch.
> 
> I agree with your point of view.  How do we fix it now ?  Do you think
> it's worth crafting a one-line patch ?

Yup. Or you can leave the code as is: I guess round_jiffies() is not a big
deal since it's not in some kind of hot path, so generally this doesn't
affect behaviour. It's up to you, I'm fine either way.

Thanks!

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 27/57] power: sysfs interface update
  2012-09-27  7:20   ` Anton Vorontsov
@ 2012-09-28 18:26     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:26 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 01:20 AM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:24AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Michel JAOUEN <michel.jaouen@stericsson.com>
>>
>> Add new sysfs interface to get current charge status
>>
>> Signed-off-by: Michel JAOUEN <michel.jaouen@stericsson.com>
>> Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
>> Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
>> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
>> ---
>>  drivers/power/ab8500_charger.c  |    3 +++
>>  drivers/power/abx500_chargalg.c |   24 +++++++++++++++++++++++-
>>  2 files changed, 26 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
>> index 4129599..0a781a0 100644
>> --- a/drivers/power/ab8500_charger.c
>> +++ b/drivers/power/ab8500_charger.c
>> @@ -2759,6 +2759,9 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
>>  	enum ab8500_usb_state bm_usb_state;
>>  	unsigned mA = *((unsigned *)power);
>>  
>> +	if (di == NULL)
>> +		return NOTIFY_DONE;
>> +
> 
> I'd write !di.

Not sure I agree with you here.  If di is NULL there is nothing to work
with and as such, exit.

> 
> [...]
>> +static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
>> +	struct attribute *attr, char *buf)
>> +{
>> +	struct abx500_chargalg *di = container_of(kobj,
>> +		struct abx500_chargalg, chargalg_kobject);
>> +
>> +	if ((di->susp_status.ac_suspended == true) &&
>> +		(di->susp_status.usb_suspended == true))
>> +		return sprintf(buf, "0\n");
>> +	else
>> +		return sprintf(buf, "1\n");
> 
> just
> 
> return sprintf(buf, "%d\n", di->susp_status.ac_suspended &&
> 			    di->susp_status.usb_suspended);

Much cleaner yes.

> 
>> +}
>> +
>>  /* Exposure to the sysfs interface */
>>  
>>  /**
>> @@ -1749,7 +1770,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
>>  static struct attribute abx500_chargalg_en_charger = \
>>  {
>>  	.name = "chargalg",
>> -	.mode = S_IWUGO,
>> +	.mode = S_IRUGO | S_IWUSR,
>>  };
>>  
>>  static struct attribute *abx500_chargalg_chg[] = {
>> @@ -1758,6 +1779,7 @@ static struct attribute *abx500_chargalg_chg[] = {
>>  };
>>  
>>  static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
>> +	.show = abx500_chargalg_sysfs_show,
>>  	.store = abx500_chargalg_sysfs_charger,
>>  };
>>  
>> -- 
>> 1.7.5.4


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 30/57] power: ab8500: Flush & sync all works
  2012-09-27  7:23   ` Anton Vorontsov
@ 2012-09-28 18:28     ` Mathieu Poirier
  2012-09-28 19:54       ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:28 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 01:23 AM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:27AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Jonas Aaberg <jonas.aberg@stericsson.com>
>>
>> Flush and sync all workqueues at suspend to avoid
>> that we suspend in the middle of a work.
>>
>> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
>> ---
>>  drivers/power/ab8500_charger.c |   11 +++++++++++
>>  drivers/power/ab8500_fg.c      |    5 +++++
>>  2 files changed, 16 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
>> index ee5ad7b..071c7c2 100644
>> --- a/drivers/power/ab8500_charger.c
>> +++ b/drivers/power/ab8500_charger.c
>> @@ -2862,6 +2862,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>>  	if (delayed_work_pending(&di->check_hw_failure_work))
>>  		cancel_delayed_work(&di->check_hw_failure_work);
>>  
>> +	flush_delayed_work_sync(&di->attach_work);
>> +	flush_delayed_work_sync(&di->usb_charger_attached_work);
>> +	flush_delayed_work_sync(&di->ac_charger_attached_work);
>> +	flush_delayed_work_sync(&di->check_usbchgnotok_work);
>> +	flush_delayed_work_sync(&di->check_vbat_work);
>> +	flush_delayed_work_sync(&di->kick_wd_work);
>> +
>> +	flush_work_sync(&di->usb_link_status_work);
>> +	flush_work_sync(&di->ac_work);
>> +	flush_work_sync(&di->detect_usb_type_work);
> 
> I belive each of these have to be added by the patches that add the
> appropriate work structs. But really, it's better to avoid these many
> delayed work.

Agreed - on the flip side they were added over multiple patches spanning
many centuries.  Tracking all this work down would be impossible as
history was re-written many time over.  What would you like to see
happening here ?

> 
>>  	return 0;
>>  }
>>  #else
>> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
>> index e7a0e1f..0e71e7e 100644
>> --- a/drivers/power/ab8500_fg.c
>> +++ b/drivers/power/ab8500_fg.c
>> @@ -2626,6 +2626,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
>>  	struct ab8500_fg *di = platform_get_drvdata(pdev);
>>  
>>  	flush_delayed_work_sync(&di->fg_periodic_work);
>> +	flush_work_sync(&di->fg_work);
>> +	flush_work_sync(&di->fg_acc_cur_work);
>> +	flush_delayed_work_sync(&di->fg_reinit_work);
>> +	flush_delayed_work_sync(&di->fg_low_bat_work);
>> +	flush_delayed_work_sync(&di->fg_check_hw_failure_work);
>>  
>>  	/*
>>  	 * If the FG is enabled we will disable it before going to suspend
>> -- 
>> 1.7.5.4
>>
>>


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 38/57] power: l9540: Charge only mode fixes
  2012-09-28  0:27   ` Anton Vorontsov
@ 2012-09-28 18:32     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:32 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 06:27 PM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:35AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Rupesh Kumar <rupesh.kumar@stericsson.com>
>>
>> Fix for: charging not getting enabled in
>> charge only mode by external charger.
> 
> Subject says l9540.. what is this?
> 
>> Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
>> Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
>> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
>> Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
>> ---
>>  drivers/power/ab8500_charger.c            |   42 +++++++++++++++++++++++++++++
>>  drivers/power/abx500_chargalg.c           |   14 +++++++++
>>  include/linux/mfd/abx500/ux500_chargalg.h |    2 +
>>  3 files changed, 58 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
>> index 70e7c5e..ebeb068 100644
>> --- a/drivers/power/ab8500_charger.c
>> +++ b/drivers/power/ab8500_charger.c
>> @@ -15,6 +15,7 @@
>>  #include <linux/device.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/delay.h>
>> +#include <linux/notifier.h>
>>  #include <linux/slab.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/power_supply.h>
>> @@ -94,6 +95,10 @@
>>  #define AB8500_SW_CONTROL_FALLBACK	0x03
>>  /* Wait for enumeration before charging in us */
>>  #define WAIT_ACA_RID_ENUMERATION	(5 * 1000)
>> +/*External charger control*/
>> +#define AB8500_SYS_CHARGER_CONTROL_REG		0x52
>> +#define EXTERNAL_CHARGER_DISABLE_REG_VAL	0x03
>> +#define EXTERNAL_CHARGER_ENABLE_REG_VAL		0x07
>>  
>>  /* UsbLineStatus register - usb types */
>>  enum ab8500_charger_link_status {
>> @@ -1672,6 +1677,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
>>  	return ret;
>>  }
>>  
>> +static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
>> +				unsigned long event, void *data)
>> +{
>> +	int ret;
>> +	struct device *dev = data;
> 
> Need an empty line here.
> 
>> +	/*Toggle External charger control pin*/
> 
> Spaces after /* and before */.
> 
>> +	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
>> +				  AB8500_SYS_CHARGER_CONTROL_REG,
>> +				  EXTERNAL_CHARGER_DISABLE_REG_VAL);
>> +	if (ret < 0) {
>> +		dev_err(dev, "write reg failed %d\n", ret);
>> +		goto out;
> 
> No need for goto.
> 
>> +	}
>> +	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
>> +				  AB8500_SYS_CHARGER_CONTROL_REG,
>> +				  EXTERNAL_CHARGER_ENABLE_REG_VAL);
>> +	if (ret < 0)
>> +		dev_err(dev, "Write reg failed %d\n", ret);
>> +
>> +out:
>> +	return ret;
>> +}
>> +
>>  /**
>>   * ab8500_charger_usb_check_enable() - enable usb charging
>>   * @charger:	pointer to the ux500_charger structure
>> @@ -3201,6 +3229,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
>>  #define ab8500_charger_resume	    NULL
>>  #endif
>>  
>> +static struct notifier_block charger_nb = {
>> +	.notifier_call = ab8500_external_charger_prepare,
>> +};
>> +
>>  static int __devexit ab8500_charger_remove(struct platform_device *pdev)
>>  {
>>  	struct ab8500_charger *di = platform_get_drvdata(pdev);
>> @@ -3233,6 +3265,11 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
>>  	/* Delete the work queue */
>>  	destroy_workqueue(di->charger_wq);
>>  
>> +	/*Unregister external charger enable notifier*/
> 
> Spaces.
> 
>> +	if (!di->ac_chg.enabled)
>> +		blocking_notifier_chain_unregister(
>> +			&charger_notifier_list, &charger_nb);
>> +
>>  	flush_scheduled_work();
>>  	if (di->usb_chg.enabled)
>>  		power_supply_unregister(&di->usb_chg.psy);
>> @@ -3307,6 +3344,11 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
>>  	di->ac_chg.enabled = di->pdata->ac_enabled;
>>  	di->ac_chg.external = false;
>>  
>> +	/*notifier for external charger enabling*/
> 
> Spaces.
> 
>> +	if (!di->ac_chg.enabled)
>> +		blocking_notifier_chain_register(
>> +			&charger_notifier_list, &charger_nb);
>> +
>>  	/* USB supply */
>>  	/* power_supply base class */
>>  	di->usb_chg.psy.name = "ab8500_usb";
>> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
>> index 9568f63..3ca00dd 100644
>> --- a/drivers/power/abx500_chargalg.c
>> +++ b/drivers/power/abx500_chargalg.c
>> @@ -24,6 +24,7 @@
>>  #include <linux/mfd/abx500.h>
>>  #include <linux/mfd/abx500/ux500_chargalg.h>
>>  #include <linux/mfd/abx500/ab8500-bm.h>
>> +#include <linux/notifier.h>
>>  
>>  /* Watchdog kick interval */
>>  #define CHG_WD_INTERVAL			(6 * HZ)
>> @@ -255,6 +256,9 @@ static enum power_supply_property abx500_chargalg_props[] = {
>>  	POWER_SUPPLY_PROP_HEALTH,
>>  };
>>  
>> +/*External charger prepare notifier*/
>> +BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
> 
> Can you place the notifier list in di->? Oh, you can't, di is
> something different for registrat
> 
> struct abx500_chargalg *di;
> struct ab8500_charger *di;
> 
> But having the global list is not friendly to multi-device situations.
> 
> If there's a subtle reason why you do it this way, it have to be described
> in the patch description (which is very scarce, btw).

Though I did  not write this code, it looks to me like any other global
variable.  Perhaps you can suggest another way to write this ?

> 
> Also, the charger_notifier_list name is in global name space, it's too
> generic. Has to be ab8500_chg_notifier_list or something.

Ok, will fix.

> 
>>  /**
>>   * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
>>   * @data:	pointer to the abx500_chargalg structure
>> @@ -508,6 +512,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
>>  static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>>  	int vset, int iset)
>>  {
>> +	static int ab8500_chargalg_ex_ac_enable_toggle;
> 
> Static? No, this is not multi-device friendly, seems like a hack.
> 
>> +
>>  	if (!di->ac_chg || !di->ac_chg->ops.enable)
>>  		return -ENXIO;
>>  
>> @@ -520,6 +526,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
>>  	di->chg_info.ac_iset = iset;
>>  	di->chg_info.ac_vset = vset;
>>  
>> +	/*enable external charger*/
> 
> spaces.
> 
>> +	if (enable && di->ac_chg->external &&
>> +		!ab8500_chargalg_ex_ac_enable_toggle) {
> 
> Wrong indentation.
> 
>> +		blocking_notifier_call_chain(&charger_notifier_list,
>> +					0, di->dev);
>> +		ab8500_chargalg_ex_ac_enable_toggle++;
>> +	}
>> +
>>  	return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
>>  }
>>  
>> diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
>> index 110d12f..fa831f1 100644
>> --- a/include/linux/mfd/abx500/ux500_chargalg.h
>> +++ b/include/linux/mfd/abx500/ux500_chargalg.h
>> @@ -41,4 +41,6 @@ struct ux500_charger {
>>  	bool external;
>>  };
>>  
>> +extern struct blocking_notifier_head charger_notifier_list;
>> +
>>  #endif
>> -- 
>> 1.7.5.4


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 52/57] power: abx500_chargalg: Use hrtimer
  2012-09-28  2:47   ` Anton Vorontsov
@ 2012-09-28 18:33     ` Mathieu Poirier
  2012-09-28 19:41       ` Anton Vorontsov
  0 siblings, 1 reply; 108+ messages in thread
From: Mathieu Poirier @ 2012-09-28 18:33 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linux-kernel, dwmw2

On 12-09-27 08:47 PM, Anton Vorontsov wrote:
> On Tue, Sep 25, 2012 at 10:12:49AM -0600, mathieu.poirier@linaro.org wrote:
>> From: Hakan Berg <hakan.berg@stericsson.com>
>>
>> Timers used for charging safety and maintenance must work even when
>> CPU is power collapsed. By using hrtimers with realtime clock, system
>> is able to trigger an alarm that wakes the CPU up and make it possible
>> to handle the event.
>>
>> Allow a little slack of 5 minutes to the hrtimers to allow CPU to be
>> waked up in a more optimal power saving way. A 5 minute delay to
>> time out timers on hours does not impact on safety.
>>
>> Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> Reviewed-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
>> ---
>>  drivers/power/abx500_chargalg.c |   94 ++++++++++++++++++++++-----------------
>>  1 files changed, 53 insertions(+), 41 deletions(-)
>>
>> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
>> index 636d970..c8849af 100644
>> --- a/drivers/power/abx500_chargalg.c
>> +++ b/drivers/power/abx500_chargalg.c
>> @@ -1,5 +1,6 @@
>>  /*
>>   * Copyright (C) ST-Ericsson SA 2012
>> + * Copyright (c) 2012 Sony Mobile Communications AB
>>   *
>>   * Charging algorithm driver for abx500 variants
>>   *
>> @@ -8,11 +9,13 @@
>>   *	Johan Palsson <johan.palsson@stericsson.com>
>>   *	Karl Komierowski <karl.komierowski@stericsson.com>
>>   *	Arun R Murthy <arun.murthy@stericsson.com>
>> + *	Imre Sunyi <imre.sunyi@sonymobile.com>
>>   */
>>  
>>  #include <linux/init.h>
>>  #include <linux/module.h>
>>  #include <linux/device.h>
>> +#include <linux/hrtimer.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/delay.h>
>>  #include <linux/slab.h>
>> @@ -32,6 +35,12 @@
>>  /* End-of-charge criteria counter */
>>  #define EOC_COND_CNT			10
>>  
>> +/* One hour expressed in seconds */
>> +#define ONE_HOUR_IN_SECONDS		3600
>> +
>> +/* Five minutes expressed in seconds */
>> +#define FIVE_MINUTES_IN_SECONDS		300
>> +
>>  #define to_abx500_chargalg_device_info(x) container_of((x), \
>>  	struct abx500_chargalg, chargalg_psy);
>>  
>> @@ -245,8 +254,8 @@ struct abx500_chargalg {
>>  	struct delayed_work chargalg_periodic_work;
>>  	struct delayed_work chargalg_wd_work;
>>  	struct work_struct chargalg_work;
>> -	struct timer_list safety_timer;
>> -	struct timer_list maintenance_timer;
>> +	struct hrtimer safety_timer;
>> +	struct hrtimer maintenance_timer;
>>  	struct kobject chargalg_kobject;
>>  };
>>  
>> @@ -261,38 +270,47 @@ BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
>>  
>>  /**
>>   * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
>> - * @data:	pointer to the abx500_chargalg structure
>> + * @timer:	pointer to the hrtimer structure
>>   *
>>   * This function gets called when the safety timer for the charger
>>   * expires
>>   */
>> -static void abx500_chargalg_safety_timer_expired(unsigned long data)
>> +static enum hrtimer_restart
>> +abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
>>  {
>> -	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
>> +	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
>> +							safety_timer);
> 
> Empty line here.
> 
>>  	dev_err(di->dev, "Safety timer expired\n");
>>  	di->events.safety_timer_expired = true;
>>  
>>  	/* Trigger execution of the algorithm instantly */
>>  	queue_work(di->chargalg_wq, &di->chargalg_work);
>> +
>> +	return HRTIMER_NORESTART;
>>  }
>>  
>>  /**
>>   * abx500_chargalg_maintenance_timer_expired() - Expiration of
>>   * the maintenance timer
>> - * @i:		pointer to the abx500_chargalg structure
>> + * @timer:	pointer to the timer structure
>>   *
>>   * This function gets called when the maintenence timer
>>   * expires
>>   */
>> -static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
>> +static enum hrtimer_restart
>> +abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
>> +
>>  {
>>  
>> -	struct abx500_chargalg *di = (struct abx500_chargalg *) data;
>> +	struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
>> +							maintenance_timer);
>>  	dev_dbg(di->dev, "Maintenance timer expired\n");
>>  	di->events.maintenance_timer_expired = true;
>>  
>>  	/* Trigger execution of the algorithm instantly */
>>  	queue_work(di->chargalg_wq, &di->chargalg_work);
>> +
>> +	return HRTIMER_NORESTART;
>>  }
>>  
>>  /**
>> @@ -392,19 +410,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
>>   */
>>  static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>>  {
>> -	unsigned long timer_expiration = 0;
>> +	/* Charger-dependent expiration time in hours*/
>> +	int timer_expiration = 0;
>>  
>>  	switch (di->chg_info.charger_type) {
>>  	case AC_CHG:
>> -		timer_expiration =
>> -		round_jiffies(jiffies +
>> -			(di->bat->main_safety_tmr_h * 3600 * HZ));
>> +		timer_expiration = di->bat->main_safety_tmr_h;
>>  		break;
>>  
>>  	case USB_CHG:
>> -		timer_expiration =
>> -		round_jiffies(jiffies +
>> -			(di->bat->usb_safety_tmr_h * 3600 * HZ));
>> +		timer_expiration = di->bat->usb_safety_tmr_h;
>>  		break;
>>  
>>  	default:
>> @@ -413,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>>  	}
>>  
>>  	di->events.safety_timer_expired = false;
>> -	di->safety_timer.expires = timer_expiration;
>> -	if (!timer_pending(&di->safety_timer))
>> -		add_timer(&di->safety_timer);
>> -	else
>> -		mod_timer(&di->safety_timer, timer_expiration);
>> +	hrtimer_set_expires_range(&di->safety_timer,
>> +		ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
>> +		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
>> +	hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
> 
> OK, now we're chaning from timers to hrtimers, and their behaviour
> might be different. I guess in this case you should check whether
> old value 'before' new value, and if so, do nothing.
> 

Would you mind being more descriptive - I'm affraid that I don't
understand your suggestion.

>>  }
>>  
>>  /**
>> @@ -428,8 +442,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
>>   */
>>  static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
>>  {
>> -	di->events.safety_timer_expired = false;
>> -	del_timer(&di->safety_timer);
>> +	if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
>> +		di->events.safety_timer_expired = false;
>>  }
>>  
>>  /**
>> @@ -444,17 +458,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
>>  static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
>>  	int duration)
>>  {
>> -	unsigned long timer_expiration;
>> -
>> -	/* Convert from hours to jiffies */
>> -	timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
>> -
>> +	hrtimer_set_expires_range(&di->maintenance_timer,
>> +		ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
>> +		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
>>  	di->events.maintenance_timer_expired = false;
>> -	di->maintenance_timer.expires = timer_expiration;
>> -	if (!timer_pending(&di->maintenance_timer))
>> -		add_timer(&di->maintenance_timer);
>> -	else
>> -		mod_timer(&di->maintenance_timer, timer_expiration);
>> +	hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
>>  }
>>  
>>  /**
>> @@ -466,8 +474,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
>>   */
>>  static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
>>  {
>> -	di->events.maintenance_timer_expired = false;
>> -	del_timer(&di->maintenance_timer);
>> +	if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
>> +		di->events.maintenance_timer_expired = false;
>>  }
>>  
>>  /**
>> @@ -910,11 +918,11 @@ static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
>>  	 * and charging will be stopped to protect the battery.
>>  	 */
>>  	if (di->batt_data.percent == 100 &&
>> -		!timer_pending(&di->safety_timer)) {
>> +		!hrtimer_active(&di->safety_timer)) {
>>  		abx500_chargalg_start_safety_timer(di);
>>  		dev_dbg(di->dev, "start safety timer\n");
>>  	} else if (di->batt_data.percent != 100 &&
>> -		timer_pending(&di->safety_timer)) {
>> +		hrtimer_active(&di->safety_timer)) {
>>  		abx500_chargalg_stop_safety_timer(di);
>>  		dev_dbg(di->dev, "stop safety timer\n");
>>  	}
>> @@ -1932,10 +1940,16 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
>>  	/* sysfs interface to enable/disbale charging from user space */
>>  	abx500_chargalg_sysfs_exit(di);
>>  
>> +	hrtimer_cancel(&di->safety_timer);
>> +	hrtimer_cancel(&di->maintenance_timer);
>> +
>> +	cancel_delayed_work_sync(&di->chargalg_periodic_work);
>> +	cancel_delayed_work_sync(&di->chargalg_wd_work);
>> +	cancel_work_sync(&di->chargalg_work);
>> +
> 
> These cancel_ calls seem to be separate bugfixes?
> 
>>  	/* Delete the work queue */
>>  	destroy_workqueue(di->chargalg_wq);
>>  
>> -	flush_scheduled_work();
>>  	power_supply_unregister(&di->chargalg_psy);
>>  	platform_set_drvdata(pdev, NULL);
>>  	kfree(di);
>> @@ -1987,15 +2001,13 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
>>  		abx500_chargalg_external_power_changed;
>>  
>>  	/* Initilialize safety timer */
>> -	init_timer(&di->safety_timer);
>> +	hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
>>  	di->safety_timer.function = abx500_chargalg_safety_timer_expired;
>> -	di->safety_timer.data = (unsigned long) di;
>>  
>>  	/* Initilialize maintenance timer */
>> -	init_timer(&di->maintenance_timer);
>> +	hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
>>  	di->maintenance_timer.function =
>>  		abx500_chargalg_maintenance_timer_expired;
>> -	di->maintenance_timer.data = (unsigned long) di;
>>  
>>  	/* Create a work queue for the chargalg */
>>  	di->chargalg_wq =
>> -- 
>> 1.7.5.4


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 18/57] power: Add sysfs interfaces for capacity
  2012-09-28 18:26     ` Mathieu Poirier
@ 2012-09-28 19:11       ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28 19:11 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Fri, Sep 28, 2012 at 12:26:08PM -0600, Mathieu Poirier wrote:
> On 12-09-27 01:08 AM, Anton Vorontsov wrote:
> > On Tue, Sep 25, 2012 at 10:12:15AM -0600, mathieu.poirier@linaro.org wrote:
> >> From: Daniel WILLERUD <daniel.willerud@stericsson.com>
> >>
> >> Switchable depending on whether capacity scaling is enabled
> >>
> >> Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
> >> Signed-off-by: Daniel WILLERUD <daniel.willerud@stericsson.com>
> >> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> >> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
> >> ---
> >>  drivers/power/ab8500_fg.c |   57 ++++++++++++++++++++++++++++++++++++++++++++-
> >>  1 files changed, 56 insertions(+), 1 deletions(-)
> >>
> >> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> >> index 8507254..46010ec 100644
> >> --- a/drivers/power/ab8500_fg.c
> >> +++ b/drivers/power/ab8500_fg.c
> >> @@ -266,7 +266,6 @@ static enum power_supply_property ab8500_fg_props[] = {
> >>  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
> >>  	POWER_SUPPLY_PROP_CHARGE_FULL,
> >>  	POWER_SUPPLY_PROP_CHARGE_NOW,
> >> -	POWER_SUPPLY_PROP_CAPACITY,
> > [...]
> >> +static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
> >> +	__ATTR(capacity, S_IRUGO, ab8500_show_capacity, NULL),
> >> +};
> > 
> > I don't understand the rationale behind this patch. Why remove normal
> > power supply property, and make your own with the same name? Something
> > isn't right...
> 
> The similarity in the naming convention it a coincidence here.  In one
> case it's a enum and the other sysfs attribute.  Could you expand on
> your suspicions ?

>From the userland point of view, how would the new attribute look like?

Originally the driver reported capacity via power supply class, i.e.
PROP_CAPACITY, now you change to your own attribute.. so the question is
why you do this.

If you need optional attributes, it is still possible to make this w/o
introducing your own attributes mechanisms.

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 52/57] power: abx500_chargalg: Use hrtimer
  2012-09-28 18:33     ` Mathieu Poirier
@ 2012-09-28 19:41       ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28 19:41 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Fri, Sep 28, 2012 at 12:33:51PM -0600, Mathieu Poirier wrote:
[...]
> >> @@ -413,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
> >>  	}
> >>  
> >>  	di->events.safety_timer_expired = false;
> >> -	di->safety_timer.expires = timer_expiration;
> >> -	if (!timer_pending(&di->safety_timer))
> >> -		add_timer(&di->safety_timer);
> >> -	else
> >> -		mod_timer(&di->safety_timer, timer_expiration);
> >> +	hrtimer_set_expires_range(&di->safety_timer,
> >> +		ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
> >> +		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
> >> +	hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
> > 
> > OK, now we're chaning from timers to hrtimers, and their behaviour
> > might be different. I guess in this case you should check whether
> > old value 'before' new value, and if so, do nothing.
> 
> Would you mind being more descriptive - I'm affraid that I don't
> understand your suggestion.

I was partly referencing to my answer here
http://lkml.org/lkml/2012/9/27/678

There I was suggesting how to make abx500_chargalg_check_safety_timer()
simpler, but since in this patch you switching code to hrtimers, the logic
have to change too.

So, to put it even simpler, in this patch you don't even need to check for
time values, in abx500_chargalg_start_safety_timer(), you can just write

	if (hrtimer_active(&di->safety_timer))
		return;

	dev_dbg(di->dev, "starting safety timer\n");

	hrtimer_set_expires_range(&di->safety_timer,
		ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
	hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);

And you don't have to check for hrtimer_active() anywhere else...

[...]
> >> @@ -910,11 +918,11 @@ static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di)
> >>  	 * and charging will be stopped to protect the battery.
> >>  	 */
> >>  	if (di->batt_data.percent == 100 &&
> >> -		!timer_pending(&di->safety_timer)) {
> >> +		!hrtimer_active(&di->safety_timer)) {
> >>  		abx500_chargalg_start_safety_timer(di);
> >>  		dev_dbg(di->dev, "start safety timer\n");
> >>  	} else if (di->batt_data.percent != 100 &&
> >> -		timer_pending(&di->safety_timer)) {
> >> +		hrtimer_active(&di->safety_timer)) {
> >>  		abx500_chargalg_stop_safety_timer(di);
> >>  		dev_dbg(di->dev, "stop safety timer\n");
> >>  	}

..so with the above suggestion, this code will turn into just

	if (di->batt_data.percent < 100) {
		abx500_chargalg_stop_safety_timer(di);
		return;
	}
	abx500_chargalg_start_safety_timer(di);

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 30/57] power: ab8500: Flush & sync all works
  2012-09-28 18:28     ` Mathieu Poirier
@ 2012-09-28 19:54       ` Anton Vorontsov
  0 siblings, 0 replies; 108+ messages in thread
From: Anton Vorontsov @ 2012-09-28 19:54 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-kernel, dwmw2

On Fri, Sep 28, 2012 at 12:28:03PM -0600, Mathieu Poirier wrote:
[...]
> >> +	flush_delayed_work_sync(&di->attach_work);
> >> +	flush_delayed_work_sync(&di->usb_charger_attached_work);
> >> +	flush_delayed_work_sync(&di->ac_charger_attached_work);
> >> +	flush_delayed_work_sync(&di->check_usbchgnotok_work);
> >> +	flush_delayed_work_sync(&di->check_vbat_work);
> >> +	flush_delayed_work_sync(&di->kick_wd_work);
> >> +
> >> +	flush_work_sync(&di->usb_link_status_work);
> >> +	flush_work_sync(&di->ac_work);
> >> +	flush_work_sync(&di->detect_usb_type_work);
> > 
> > I belive each of these have to be added by the patches that add the
> > appropriate work structs. But really, it's better to avoid these many
> > delayed work.
> 
> Agreed - on the flip side they were added over multiple patches spanning
> many centuries.  Tracking all this work down would be impossible as
> history was re-written many time over.  What would you like to see
> happening here ?

Um.. why can't we just fold

+	flush_delayed_work_sync(&di->attach_work);

Into "[PATCH 20/57] power: Adds support for legacy USB chargers"?

Then 

+	flush_delayed_work_sync(&di->usb_charger_attached_work);
+	flush_delayed_work_sync(&di->ac_charger_attached_work);

Into "[PATCH 07/57] power: ab8500_bm: Detect removed charger"

etc...

That is, at the same time you introduce the work structs, you have to
flush them appropriately.

For work structs that were already in mainline, you'll have this patch to
fixup things. But adding new code with known bugs, and then fixing them in
one go is usually not how things should be done (unless there's a good
technical reason behind this).

Thanks,
Anton.

^ permalink raw reply	[flat|nested] 108+ messages in thread

end of thread, other threads:[~2012-09-28 19:57 UTC | newest]

Thread overview: 108+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-25 16:11 [PATCH 00/57] power: Upgrade to ux500 battery management driver mathieu.poirier
2012-09-25 16:11 ` [PATCH 01/57] power: ab8500_bm: Charger current step-up/down mathieu.poirier
2012-09-28  3:41   ` Anton Vorontsov
2012-09-25 16:11 ` [PATCH 02/57] power: ab8500_bm: Don't clear the CCMuxOffset bit mathieu.poirier
2012-09-28  3:42   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 03/57] power: ab8500_btemp: Detect battery type in workqueue mathieu.poirier
2012-09-25 16:12 ` [PATCH 04/57] power: ab8500: bm: movimg back to ab8500 platform data managment mathieu.poirier
2012-09-27  2:42   ` Anton Vorontsov
2012-09-27  2:53   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 05/57] power: ab8500_bm: Rename the power_loss function mathieu.poirier
2012-09-25 16:12 ` [PATCH 06/57] power: ab8500_bm: Skip first CCEOC irq for instant current mathieu.poirier
2012-09-25 16:12 ` [PATCH 07/57] power: ab8500_bm: Detect removed charger mathieu.poirier
2012-09-27  3:09   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 08/57] power: ab8500_fg: flush sync on suspend mathieu.poirier
2012-09-27  3:11   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 09/57] power: ab8500_fg: usleep_range instead of short msleep mathieu.poirier
2012-09-27  2:36   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 10/57] power: ab8500_charger: Handle gpadc errors mathieu.poirier
2012-09-25 16:12 ` [PATCH 11/57] power: Recharge condition not optimal for battery mathieu.poirier
2012-09-27  3:29   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 12/57] power: ab8500_fg: balance IRQ enable mathieu.poirier
2012-09-25 16:12 ` [PATCH 13/57] power: ab8500_bm: Ignore false btemp low interrupt mathieu.poirier
2012-09-27  3:32   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 14/57] power: Adds support for Car/Travel Adapters mathieu.poirier
2012-09-25 16:12 ` [PATCH 15/57] power: ab8500_fg: Round capacity output mathieu.poirier
2012-09-25 16:12 ` [PATCH 16/57] power: bm remove superfluous BTEMP thermal comp mathieu.poirier
2012-09-25 16:12 ` [PATCH 17/57] power: ab8500_bm: Added support for BATT_OVV mathieu.poirier
2012-09-27  3:36   ` Anton Vorontsov
2012-09-28 18:24     ` Mathieu Poirier
2012-09-28 18:26       ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 18/57] power: Add sysfs interfaces for capacity mathieu.poirier
2012-09-27  7:08   ` Anton Vorontsov
2012-09-28 18:26     ` Mathieu Poirier
2012-09-28 19:11       ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 19/57] power: remove unused defines mathieu.poirier
2012-09-25 16:12 ` [PATCH 20/57] power: Adds support for legacy USB chargers mathieu.poirier
2012-09-27  7:15   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 21/57] power: Overflow in current calculation mathieu.poirier
2012-09-25 16:12 ` [PATCH 22/57] power: AB workaround for invalid charger mathieu.poirier
2012-09-25 16:12 ` [PATCH 23/57] power: Add plaform data charger configurables mathieu.poirier
2012-09-25 16:12 ` [PATCH 24/57] power: ab8500_fg: Adjust for RF bursts voltage drops mathieu.poirier
2012-09-25 16:12 ` [PATCH 25/57] power: ab8500: adaptation to ab version mathieu.poirier
2012-09-25 16:12 ` [PATCH 26/57] power: charge: update watchdog for pm2xxx support mathieu.poirier
2012-09-25 16:12 ` [PATCH 27/57] power: sysfs interface update mathieu.poirier
2012-09-27  7:20   ` Anton Vorontsov
2012-09-28 18:26     ` Mathieu Poirier
2012-09-25 16:12 ` [PATCH 28/57] power: ab8500 - Accessing Autopower register fails mathieu.poirier
2012-09-25 16:12 ` [PATCH 29/57] power: ab8500_fg: Goto INIT_RECOVERY when charger removed mathieu.poirier
2012-09-25 16:12 ` [PATCH 30/57] power: ab8500: Flush & sync all works mathieu.poirier
2012-09-27  7:23   ` Anton Vorontsov
2012-09-28 18:28     ` Mathieu Poirier
2012-09-28 19:54       ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 31/57] power: ab8500_fg: fix to use correct battery charge full design mathieu.poirier
2012-09-27  7:27   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 32/57] power: ab8500_charger: Do not touch VBUSOVV bits mathieu.poirier
2012-09-25 16:12 ` [PATCH 33/57] power: u8500_charger: Delay for USB enumeration mathieu.poirier
2012-09-27  7:42   ` Anton Vorontsov
2012-09-28 16:56     ` Mathieu Poirier
2012-09-28 17:09       ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 34/57] power: ab8500_fg: add power cut feature for ab8505 mathieu.poirier
2012-09-28  0:01   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 35/57] power: ab8500_fg: Report unscaled capacity mathieu.poirier
2012-09-25 16:12 ` [PATCH 36/57] power: add backup battery charge voltages mathieu.poirier
2012-09-25 16:12 ` [PATCH 37/57] power: ab8500_bm: Quick re-attach charging behaviour mathieu.poirier
2012-09-28  0:13   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 38/57] power: l9540: Charge only mode fixes mathieu.poirier
2012-09-28  0:27   ` Anton Vorontsov
2012-09-28 18:32     ` Mathieu Poirier
2012-09-25 16:12 ` [PATCH 39/57] power: ab8500_charger: Prevent auto drop of VBUS mathieu.poirier
2012-09-28  0:52   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 40/57] power: ab8500: ADC for battery thermistor mathieu.poirier
2012-09-28  0:57   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 41/57] power: ab8500_btemp: Filter btemp readings mathieu.poirier
2012-09-25 16:12 ` [PATCH 42/57] power: charging: Allow capacity to raise from 1% mathieu.poirier
2012-09-25 16:12 ` [PATCH 43/57] power: charging: Add AB8505_USB_LINK_STATUS mathieu.poirier
2012-09-25 16:12 ` [PATCH 44/57] power: ab8500: remove unecesary define flag mathieu.poirier
2012-09-28  1:05   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 45/57] power: ab8500: defer btemp filtering while init mathieu.poirier
2012-09-28  1:08   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 46/57] power: chargealg: Realign with upstream version mathieu.poirier
2012-09-28  1:17   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 47/57] power: Harmonising platform data declaration/handling mathieu.poirier
2012-09-28  1:11   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 48/57] power: ab8500 : quick re-attach for ext charger mathieu.poirier
2012-09-28  1:19   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 49/57] power: Cancelling status charging notification mathieu.poirier
2012-09-28  2:16   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 50/57] power: ab8500-chargalg: update battery health on safety timer exp mathieu.poirier
2012-09-28  2:21   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 51/57] power: ab8500: Re-alignment with internal developement mathieu.poirier
2012-09-28  2:35   ` Anton Vorontsov
2012-09-28  2:45     ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 52/57] power: abx500_chargalg: Use hrtimer mathieu.poirier
2012-09-28  2:47   ` Anton Vorontsov
2012-09-28 18:33     ` Mathieu Poirier
2012-09-28 19:41       ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 53/57] power: ab8500_fg: Moving structure definitions to header file mathieu.poirier
2012-09-28  2:51   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 54/57] power: ab8500_charger: Use USBLink1Status Register mathieu.poirier
2012-09-28  2:56   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 55/57] power: ab8500_charger: Add UsbLineCtrl2 reference mathieu.poirier
2012-09-28  2:58   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 56/57] power: abx500_chargalg: Fix quick re-attach charger issue mathieu.poirier
2012-09-28  3:00   ` Anton Vorontsov
2012-09-25 16:12 ` [PATCH 57/57] power: ab8500_charger: Limit USB charger current mathieu.poirier
2012-09-27  3:38 ` [PATCH 00/57] power: Upgrade to ux500 battery management driver Anton Vorontsov
2012-09-27 22:08   ` Mathieu Poirier
2012-09-28  0:36     ` Anton Vorontsov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).