summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt19
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi2
-rw-r--r--drivers/power/qcom-charger/fg-core.h12
-rw-r--r--drivers/power/qcom-charger/fg-util.c12
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c135
5 files changed, 174 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
index 9bf6d5b2bf8e..bd236df6c056 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
@@ -119,6 +119,25 @@ First Level Node - FG Gen3 device
If these parameters are not specified, then the default
values used will be 0, 5, 45, 50.
+- qcom,fg-esr-timer-charging
+ Usage: optional
+ Value type: <u32>
+ Definition: Number of cycles between ESR pulses while the battery is
+ charging.
+
+- qcom,fg-esr-timer-awake
+ Usage: optional
+ Value type: <u32>
+ Definition: Number of cycles between ESR pulses while the system is
+ awake and the battery is discharging.
+
+- qcom,fg-esr-timer-asleep
+ Usage: optional
+ Value type: <u32>
+ Definition: Number of cycles between ESR pulses while the system is
+ asleep and the battery is discharging. This option requires
+ qcom,fg-esr-timer-awake to be defined.
+
==========================================================
Second Level Nodes - Peripherals managed by FG Gen3 driver
==========================================================
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index ac68f19e01ab..d99749a01f6c 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -309,6 +309,8 @@
qcom,pmic-revid = <&pmicobalt_revid>;
io-channels = <&pmicobalt_rradc 0>;
io-channel-names = "rradc_batt_id";
+ qcom,fg-esr-timer-awake = <64>;
+ qcom,fg-esr-timer-asleep = <256>;
status = "okay";
qcom,fg-batt-soc@4000 {
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
index ef4ea8172035..d176bbb7d872 100644
--- a/drivers/power/qcom-charger/fg-core.h
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -68,8 +68,9 @@ enum fg_debug_flag {
/* SRAM access */
enum sram_access_flags {
- FG_IMA_DEFAULT = 0,
- FG_IMA_ATOMIC,
+ FG_IMA_DEFAULT = 0,
+ FG_IMA_ATOMIC = BIT(0),
+ FG_IMA_NO_WLOCK = BIT(1),
};
/* JEITA */
@@ -117,6 +118,10 @@ enum fg_sram_param_id {
FG_SRAM_CUTOFF_VOLT,
FG_SRAM_EMPTY_VOLT,
FG_SRAM_VBATT_LOW,
+ FG_SRAM_ESR_TIMER_DISCHG_MAX,
+ FG_SRAM_ESR_TIMER_DISCHG_INIT,
+ FG_SRAM_ESR_TIMER_CHG_MAX,
+ FG_SRAM_ESR_TIMER_CHG_INIT,
FG_SRAM_SYS_TERM_CURR,
FG_SRAM_CHG_TERM_CURR,
FG_SRAM_DELTA_SOC_THR,
@@ -148,6 +153,9 @@ struct fg_dt_props {
int recharge_soc_thr;
int rsense_sel;
int jeita_thresholds[NUM_JEITA_LEVELS];
+ int esr_timer_charging;
+ int esr_timer_awake;
+ int esr_timer_asleep;
};
/* parameters from battery profile */
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index 9f2d9973e04b..5f133d6f39c1 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -66,7 +66,8 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
if (!fg_sram_address_valid(address, len))
return -EFAULT;
- vote(chip->awake_votable, SRAM_WRITE, true, 0);
+ if (!(flags & FG_IMA_NO_WLOCK))
+ vote(chip->awake_votable, SRAM_WRITE, true, 0);
mutex_lock(&chip->sram_rw_lock);
if ((flags & FG_IMA_ATOMIC) && chip->irqs[SOC_UPDATE_IRQ].irq) {
@@ -113,7 +114,8 @@ out:
disable_irq_nosync(chip->irqs[SOC_UPDATE_IRQ].irq);
mutex_unlock(&chip->sram_rw_lock);
- vote(chip->awake_votable, SRAM_WRITE, false, 0);
+ if (!(flags & FG_IMA_NO_WLOCK))
+ vote(chip->awake_votable, SRAM_WRITE, false, 0);
return rc;
}
@@ -128,7 +130,8 @@ int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset,
if (!fg_sram_address_valid(address, len))
return -EFAULT;
- vote(chip->awake_votable, SRAM_READ, true, 0);
+ if (!(flags & FG_IMA_NO_WLOCK))
+ vote(chip->awake_votable, SRAM_READ, true, 0);
mutex_lock(&chip->sram_rw_lock);
rc = fg_interleaved_mem_read(chip, address, offset, val, len);
@@ -137,7 +140,8 @@ int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset,
address, offset, rc);
mutex_unlock(&chip->sram_rw_lock);
- vote(chip->awake_votable, SRAM_READ, false, 0);
+ if (!(flags & FG_IMA_NO_WLOCK))
+ vote(chip->awake_votable, SRAM_READ, false, 0);
return rc;
}
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index b350e53237b7..fcd4545471cc 100644
--- a/drivers/power/qcom-charger/qpnp-fg-gen3.c
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -45,6 +45,14 @@
#define EMPTY_VOLT_OFFSET 0
#define VBATT_LOW_WORD 15
#define VBATT_LOW_OFFSET 1
+#define ESR_TIMER_DISCHG_MAX_WORD 17
+#define ESR_TIMER_DISCHG_MAX_OFFSET 0
+#define ESR_TIMER_DISCHG_INIT_WORD 17
+#define ESR_TIMER_DISCHG_INIT_OFFSET 2
+#define ESR_TIMER_CHG_MAX_WORD 18
+#define ESR_TIMER_CHG_MAX_OFFSET 0
+#define ESR_TIMER_CHG_INIT_WORD 18
+#define ESR_TIMER_CHG_INIT_OFFSET 2
#define PROFILE_LOAD_WORD 24
#define PROFILE_LOAD_OFFSET 0
#define NOM_CAP_WORD 58
@@ -114,6 +122,15 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = {
100, fg_encode_default, NULL),
PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET,
1, 256, 100, fg_encode_default, NULL),
+ PARAM(ESR_TIMER_DISCHG_MAX, ESR_TIMER_DISCHG_MAX_WORD,
+ ESR_TIMER_DISCHG_MAX_OFFSET, 2, 1, 1, fg_encode_default, NULL),
+ PARAM(ESR_TIMER_DISCHG_INIT, ESR_TIMER_DISCHG_INIT_WORD,
+ ESR_TIMER_DISCHG_INIT_OFFSET, 2, 1, 1, fg_encode_default,
+ NULL),
+ PARAM(ESR_TIMER_CHG_MAX, ESR_TIMER_CHG_MAX_WORD,
+ ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, fg_encode_default, NULL),
+ PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD,
+ ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, fg_encode_default, NULL),
};
static int fg_gen3_debug_mask;
@@ -707,6 +724,45 @@ static inline void get_temp_setpoint(int threshold, u8 *val)
*val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5);
}
+static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
+ int flags)
+{
+ u8 buf[2];
+ int rc, timer_max, timer_init;
+
+ if (charging) {
+ timer_max = FG_SRAM_ESR_TIMER_CHG_MAX;
+ timer_init = FG_SRAM_ESR_TIMER_CHG_INIT;
+ } else {
+ timer_max = FG_SRAM_ESR_TIMER_DISCHG_MAX;
+ timer_init = FG_SRAM_ESR_TIMER_DISCHG_INIT;
+ }
+
+ fg_encode(chip->sp, timer_max, cycles, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[timer_max].address,
+ chip->sp[timer_max].offset, buf,
+ chip->sp[timer_max].len, flags);
+ if (rc < 0) {
+ pr_err("Error in writing esr_timer_dischg_max, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, timer_init, cycles, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[timer_init].address,
+ chip->sp[timer_init].offset, buf,
+ chip->sp[timer_init].len, flags);
+ if (rc < 0) {
+ pr_err("Error in writing esr_timer_dischg_init, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -943,6 +999,24 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
+ if (chip->dt.esr_timer_charging > 0) {
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_charging, true,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.esr_timer_awake > 0) {
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
return 0;
}
@@ -1327,6 +1401,25 @@ static int fg_parse_dt(struct fg_chip *chip)
}
}
+ rc = of_property_read_u32(node, "qcom,fg-esr-timer-charging", &temp);
+ if (rc < 0)
+ chip->dt.esr_timer_charging = -EINVAL;
+ else
+ chip->dt.esr_timer_charging = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-esr-timer-awake", &temp);
+ if (rc < 0)
+ chip->dt.esr_timer_awake = -EINVAL;
+ else
+ chip->dt.esr_timer_awake = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-esr-timer-asleep", &temp);
+ if (rc < 0)
+ chip->dt.esr_timer_asleep = -EINVAL;
+ else
+ chip->dt.esr_timer_asleep = temp;
+
+
return 0;
}
@@ -1450,6 +1543,47 @@ exit:
return rc;
}
+static int fg_gen3_suspend(struct device *dev)
+{
+ struct fg_chip *chip = dev_get_drvdata(dev);
+ int rc;
+
+ if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) {
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_asleep, false,
+ FG_IMA_NO_WLOCK);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer during suspend, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int fg_gen3_resume(struct device *dev)
+{
+ struct fg_chip *chip = dev_get_drvdata(dev);
+ int rc;
+
+ if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) {
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer during resume, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops fg_gen3_pm_ops = {
+ .suspend = fg_gen3_suspend,
+ .resume = fg_gen3_resume,
+};
+
static int fg_gen3_remove(struct platform_device *pdev)
{
struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
@@ -1468,6 +1602,7 @@ static struct platform_driver fg_gen3_driver = {
.name = FG_GEN3_DEV_NAME,
.owner = THIS_MODULE,
.of_match_table = fg_gen3_match_table,
+ .pm = &fg_gen3_pm_ops,
},
.probe = fg_gen3_probe,
.remove = fg_gen3_remove,