Merge "power: qpnp-qg: Add a workaround to force recharge"
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index f87f7db..efa67f5 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -41,6 +41,16 @@
when this interrupt fires. If not specified, the
default value is 3200 mV.
+- qcom,vbatt-empty-cold-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ vbatt-empty interrupt fires. This threshold is only
+ applied at cold temperature specified by
+ 'qcom,cold-temp-threshold'. The SOC is forced to 0
+ when this interrupt fires. If not specified, the
+ default value is 3000 mV.
+
- qcom,vbatt-cutoff-mv
Usage: optional
Value type: <u32>
@@ -56,6 +66,16 @@
the action when this interrupt fires. If not specified
the default value is 3500 mV.
+- qcom,vbatt-low-cold-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ the VBAT_LOW interrupt fires. The threshold is only
+ applied at cold temperature specified by
+ 'qcom,cold-temp-threshold'. Software can take necessary
+ the action when this interrupt fires. If not specified
+ the default value is 3800 mV.
+
- qcom,qg-iterm-ma
Usage: optional
Value type: <u32>
@@ -164,6 +184,13 @@
improve the user experience. This is applicable only if
"qcom,hold-soc-while-full" is specified.
+- qcom,cold-temp-threshold
+ Usage: optional
+ Value type: <u32>
+ Definition: Temperature threshold in decidegree at which the low
+ temperature specific configuration as applied. If not
+ specified, the default value is 0 degree centigrade.
+
==========================================================
Second Level Nodes - Peripherals managed by QGAUGE driver
==========================================================
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 8bb9dbe..b8f8b68 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -29,7 +29,9 @@
pmi632_pon: qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
+
qcom,secondary-pon-reset;
+ qcom,s3-src = "kpdpwr";
};
pmi632_vadc: vadc@3100 {
@@ -464,9 +466,6 @@
#address-cells = <1>;
#size-cells = <1>;
- qcom,vbatt-empty-mv = <3300>;
- qcom,vbatt-low-mv = <3500>;
- qcom,vbatt-cutoff-mv = <3400>;
qcom,qg-iterm-ma = <100>;
qcom,hold-soc-while-full;
qcom,linearize-soc;
@@ -476,11 +475,12 @@
qcom,qgauge@4800 {
status = "okay";
reg = <0x4800 0x100>;
- interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>;
+ interrupts =
+ <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x48 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x48 0x4 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "qg-batt-missing",
"qg-vbat-low",
"qg-vbat-empty",
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 55dba4e..6e44f33 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -29,7 +29,9 @@
struct qg_dt {
int vbatt_empty_mv;
+ int vbatt_empty_cold_mv;
int vbatt_low_mv;
+ int vbatt_low_cold_mv;
int vbatt_cutoff_mv;
int iterm_ma;
int s2_fifo_length;
@@ -44,6 +46,7 @@
int delta_soc;
int rbat_conn_mohm;
int ignore_shutdown_soc_secs;
+ int cold_temp_threshold;
bool hold_soc_while_full;
bool linearize_soc;
};
@@ -147,6 +150,7 @@
enum qg_wa_flags {
QG_VBAT_LOW_WA = BIT(0),
+ QG_RECHARGE_SOC_WA = BIT(1),
};
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index 660f6f1..c4d5043 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -24,11 +24,15 @@
#define DEFAULT_UPDATE_TIME_MS 64000
#define SOC_SCALE_HYST_MS 2000
-#define SOC_SCALE_LOW_TEMP_THRESHOLD 100
static int qg_delta_soc_interval_ms = 20000;
module_param_named(
- delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+ soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+);
+
+static int qg_delta_soc_cold_interval_ms = 4000;
+module_param_named(
+ soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600
);
static void get_next_update_time(struct qpnp_qg *chip)
@@ -52,9 +56,11 @@
rc = qg_get_battery_temp(chip, &batt_temp);
if (rc < 0)
pr_err("Failed to read battery temperature rc=%d\n", rc);
+ else if (batt_temp < chip->dt.cold_temp_threshold)
+ min_delta_soc_interval_ms = qg_delta_soc_cold_interval_ms;
- if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD)
- min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2;
+ if (!min_delta_soc_interval_ms)
+ min_delta_soc_interval_ms = 1000; /* 1 second */
chip->next_wakeup_ms = (full_time_ms / (soc_points + 1))
- SOC_SCALE_HYST_MS;
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index effb093..f7a6b7a 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -114,9 +114,11 @@
return rc;
}
+#define DEFAULT_S3_FIFO_LENGTH 3
static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length)
{
int rc;
+ u8 s3_entry_fifo_length = 0;
if (!length || length > 8) {
pr_err("Invalid FIFO length %d\n", length);
@@ -128,6 +130,21 @@
if (rc < 0)
pr_err("Failed to write S2 FIFO length, rc=%d\n", rc);
+ /* update the S3 FIFO length, when S2 length is updated */
+ if (length > 3)
+ s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ?
+ chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH;
+ else /* Use S3 length as 1 for any S2 length <= 3 */
+ s3_entry_fifo_length = 1;
+
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+ SLEEP_IBAT_QUALIFIED_LENGTH_MASK,
+ s3_entry_fifo_length - 1);
+ if (rc < 0)
+ pr_err("Failed to write S3-entry fifo-length, rc=%d\n",
+ rc);
+
return rc;
}
@@ -412,8 +429,19 @@
#define VBAT_LOW_HYST_UV 50000 /* 50mV */
static int qg_vbat_low_wa(struct qpnp_qg *chip)
{
- int rc, i;
- u32 vbat_low_uv = chip->dt.vbatt_low_mv * 1000 + VBAT_LOW_HYST_UV;
+ int rc, i, temp = 0;
+ u32 vbat_low_uv = 0;
+
+ rc = qg_get_battery_temp(chip, &temp);
+ if (rc < 0) {
+ pr_err("Failed to read batt_temp rc=%d\n", rc);
+ temp = 250;
+ }
+
+ vbat_low_uv = 1000 * ((temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_low_cold_mv :
+ chip->dt.vbatt_low_mv);
+ vbat_low_uv += VBAT_LOW_HYST_UV;
if (!(chip->wa_flags & QG_VBAT_LOW_WA) || !chip->vbat_low)
return 0;
@@ -459,6 +487,73 @@
return rc;
}
+static int qg_vbat_thresholds_config(struct qpnp_qg *chip)
+{
+ int rc, temp = 0, vbat_mv;
+ u8 reg;
+
+ rc = qg_get_battery_temp(chip, &temp);
+ if (rc < 0) {
+ pr_err("Failed to read batt_temp rc=%d\n", rc);
+ return rc;
+ }
+
+ vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_empty_cold_mv :
+ chip->dt.vbatt_empty_mv;
+
+ rc = qg_read(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read vbat-empty, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (vbat_mv == (reg * 50)) /* No change */
+ goto config_vbat_low;
+
+ reg = vbat_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "VBAT EMPTY threshold updated to %dmV temp=%d\n",
+ vbat_mv, temp);
+
+config_vbat_low:
+ vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_low_cold_mv :
+ chip->dt.vbatt_low_mv;
+
+ rc = qg_read(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read vbat-low, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (vbat_mv == (reg * 50)) /* No change */
+ return 0;
+
+ reg = vbat_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-low, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "VBAT LOW threshold updated to %dmV temp=%d\n",
+ vbat_mv, temp);
+
+ return rc;
+}
+
#define MIN_FIFO_FULL_TIME_MS 12000
static int process_rt_fifo_data(struct qpnp_qg *chip,
bool vbat_low, bool update_smb)
@@ -609,6 +704,10 @@
goto done;
}
+ rc = qg_vbat_thresholds_config(chip);
+ if (rc < 0)
+ pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc);
+
rc = qg_vbat_low_wa(chip);
if (rc < 0) {
pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc);
@@ -1081,12 +1180,26 @@
chip->charge_full = true;
qg_dbg(chip, QG_DEBUG_STATUS, "Setting charge_full (0->1) @ msoc=%d\n",
chip->msoc);
- } else {
+ } else if (health != POWER_SUPPLY_HEALTH_GOOD) {
+ /* terminated in JEITA */
qg_dbg(chip, QG_DEBUG_STATUS, "Terminated charging @ msoc=%d\n",
chip->msoc);
}
} else if ((!chip->charge_done || chip->msoc < recharge_soc)
&& chip->charge_full) {
+
+ if (chip->wa_flags & QG_RECHARGE_SOC_WA) {
+ /* Force recharge */
+ prop.intval = 0;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_RECHARGE_SOC, &prop);
+ if (rc < 0)
+ pr_err("Failed to force recharge rc=%d\n", rc);
+ else
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "Forced recharge\n");
+ }
+
/*
* If recharge or discharge has started and
* if linearize soc dtsi property defined
@@ -1668,6 +1781,7 @@
{
switch (chip->pmic_rev_id->pmic_subtype) {
case PMI632_SUBTYPE:
+ chip->wa_flags |= QG_RECHARGE_SOC_WA;
if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4)
chip->wa_flags |= QG_VBAT_LOW_WA;
break;
@@ -1840,31 +1954,25 @@
}
}
- /* vbat low */
+ /* vbat based configs */
if (chip->dt.vbatt_low_mv < 0)
chip->dt.vbatt_low_mv = 0;
else if (chip->dt.vbatt_low_mv > 12750)
chip->dt.vbatt_low_mv = 12750;
- reg = chip->dt.vbatt_low_mv / 50;
- rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
- ®, 1);
- if (rc < 0) {
- pr_err("Failed to write vbat-low, rc=%d\n", rc);
- return rc;
- }
-
- /* vbat empty */
if (chip->dt.vbatt_empty_mv < 0)
chip->dt.vbatt_empty_mv = 0;
else if (chip->dt.vbatt_empty_mv > 12750)
chip->dt.vbatt_empty_mv = 12750;
- reg = chip->dt.vbatt_empty_mv / 50;
- rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
- ®, 1);
+ if (chip->dt.vbatt_empty_cold_mv < 0)
+ chip->dt.vbatt_empty_cold_mv = 0;
+ else if (chip->dt.vbatt_empty_cold_mv > 12750)
+ chip->dt.vbatt_empty_cold_mv = 12750;
+
+ rc = qg_vbat_thresholds_config(chip);
if (rc < 0) {
- pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+ pr_err("Failed to configure VBAT empty/low rc=%d\n", rc);
return rc;
}
@@ -1962,8 +2070,10 @@
}
#define DEFAULT_VBATT_EMPTY_MV 3200
+#define DEFAULT_VBATT_EMPTY_COLD_MV 3000
#define DEFAULT_VBATT_CUTOFF_MV 3400
#define DEFAULT_VBATT_LOW_MV 3500
+#define DEFAULT_VBATT_LOW_COLD_MV 3800
#define DEFAULT_ITERM_MA 100
#define DEFAULT_S2_FIFO_LENGTH 5
#define DEFAULT_S2_VBAT_LOW_LENGTH 2
@@ -1971,6 +2081,7 @@
#define DEFAULT_S2_ACC_INTVL_MS 100
#define DEFAULT_DELTA_SOC 1
#define DEFAULT_SHUTDOWN_SOC_SECS 360
+#define DEFAULT_COLD_TEMP_THRESHOLD 0
static int qg_parse_dt(struct qpnp_qg *chip)
{
int rc = 0;
@@ -2103,12 +2214,30 @@
else
chip->dt.vbatt_empty_mv = temp;
+ rc = of_property_read_u32(node, "qcom,vbatt-empty-cold-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_empty_cold_mv = DEFAULT_VBATT_EMPTY_COLD_MV;
+ else
+ chip->dt.vbatt_empty_cold_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,cold-temp-threshold", &temp);
+ if (rc < 0)
+ chip->dt.cold_temp_threshold = DEFAULT_COLD_TEMP_THRESHOLD;
+ else
+ chip->dt.cold_temp_threshold = temp;
+
rc = of_property_read_u32(node, "qcom,vbatt-low-mv", &temp);
if (rc < 0)
chip->dt.vbatt_low_mv = DEFAULT_VBATT_LOW_MV;
else
chip->dt.vbatt_low_mv = temp;
+ rc = of_property_read_u32(node, "qcom,vbatt-low-cold-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_low_cold_mv = DEFAULT_VBATT_LOW_COLD_MV;
+ else
+ chip->dt.vbatt_low_cold_mv = temp;
+
rc = of_property_read_u32(node, "qcom,vbatt-cutoff-mv", &temp);
if (rc < 0)
chip->dt.vbatt_cutoff_mv = DEFAULT_VBATT_CUTOFF_MV;
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 20dd78e..7a3a4dc 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -949,6 +949,7 @@
*************************/
static enum power_supply_property smb5_batt_props[] = {
POWER_SUPPLY_PROP_INPUT_SUSPEND,
+ POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -995,6 +996,10 @@
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_get_prop_input_suspend(chg, val);
break;
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ val->intval = !get_client_vote(chg->chg_disable_votable,
+ USER_VOTER);
+ break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
rc = smblib_get_prop_batt_charge_type(chg, val);
break;
@@ -1093,6 +1098,9 @@
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_set_prop_input_suspend(chg, val);
break;
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ vote(chg->chg_disable_votable, USER_VOTER, !val->intval, 0);
+ break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
rc = smblib_set_prop_system_temp_level(chg, val);
break;
@@ -1147,6 +1155,17 @@
chg->die_health = val->intval;
power_supply_changed(chg->batt_psy);
break;
+ case POWER_SUPPLY_PROP_RECHARGE_SOC:
+ if (chg->smb_version == PMI632_SUBTYPE) {
+ /* toggle charging to force recharge */
+ vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+ true, 0);
+ /* charge disable delay */
+ msleep(50);
+ vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+ false, 0);
+ }
+ break;
default:
rc = -EINVAL;
}
@@ -1169,6 +1188,7 @@
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
case POWER_SUPPLY_PROP_DIE_HEALTH:
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
return 1;
default:
break;
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 7ee4a3a..668ce5f 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -67,6 +67,7 @@
#define HW_LIMIT_VOTER "HW_LIMIT_VOTER"
#define DYNAMIC_RP_VOTER "DYNAMIC_RP_VOTER"
#define DEFAULT_100MA_VOTER "DEFAULT_100MA_VOTER"
+#define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER"
#define BOOST_BACK_STORM_COUNT 3
#define WEAK_CHG_STORM_COUNT 8