Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80d63754 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ARM: dts: msm: Enable SW based ESR for SDW3100"

parents a81b5cf5 ea3631a9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -385,6 +385,12 @@ First Level Node - FG Gen3 device
		    property "qcom,slope-limit-temp-threshold" to make dynamic
		    slope limit adjustment functional.

- qcom,fg-use-sw-esr
	Usage:      optional
	Value type: <empty>
	Definition: A boolean property when defined uses software based
		    ESR during charging.

==========================================================
Second Level Nodes - Peripherals managed by FG Gen3 driver
==========================================================
+4 −0
Original line number Diff line number Diff line
@@ -314,4 +314,8 @@
	qcom,fg-rsense-sel = <1>;	/* External rsense */
	qcom,fg-cutoff-voltage = <3400>;
	qcom,fg-recharge-voltage = <4100>;

	qcom,fg-use-sw-esr;
	qcom,fg-esr-pulse-thresh-ma = <40>;
	qcom,fg-esr-meas-curr-ma = <60>;
};
+18 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -24,8 +24,10 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/spmi.h>
#include <linux/alarmtimer.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -50,6 +52,7 @@
#define SRAM_WRITE		"fg_sram_write"
#define PROFILE_LOAD		"fg_profile_load"
#define DELTA_SOC		"fg_delta_soc"
#define FG_ESR_VOTER		"fg_esr_voter"

/* Delta BSOC irq votable reasons */
#define DELTA_BSOC_IRQ_VOTER	"fg_delta_bsoc_irq"
@@ -94,6 +97,11 @@ enum fg_debug_flag {
	FG_TTF			= BIT(8), /* Show time to full */
};

enum awake_reasons {
	FG_SW_ESR_WAKE = BIT(0),
	FG_STATUS_NOTIFY_WAKE = BIT(1),
};

/* SRAM access */
enum sram_access_flags {
	FG_IMA_DEFAULT	= 0,
@@ -233,6 +241,7 @@ struct fg_dt_props {
	bool	force_load_profile;
	bool	hold_soc_while_full;
	bool	auto_recharge_soc;
	bool	use_esr_sw;
	int	cutoff_volt_mv;
	int	empty_volt_mv;
	int	vbatt_low_thr_mv;
@@ -375,15 +384,19 @@ struct fg_chip {
	struct fg_cyc_ctr_data	cyc_ctr;
	struct notifier_block	nb;
	struct fg_cap_learning  cl;
	struct alarm            esr_sw_timer;
	struct mutex		bus_lock;
	struct mutex		sram_rw_lock;
	struct mutex		batt_avg_lock;
	struct mutex		charge_full_lock;
	spinlock_t		awake_lock;
	u32			batt_soc_base;
	u32			batt_info_base;
	u32			mem_if_base;
	u32			rradc_base;
	u32			wa_flags;
	u32			esr_wakeup_ms;
	u32			awake_status;
	int			batt_id_ohms;
	int			ki_coeff_full_soc;
	int			charge_status;
@@ -410,11 +423,13 @@ struct fg_chip {
	bool			esr_flt_cold_temp_en;
	bool			slope_limit_en;
	bool			use_ima_single_mode;
	bool			usb_present;
	struct completion	soc_update;
	struct completion	soc_ready;
	struct delayed_work	profile_load_work;
	struct work_struct	status_change_work;
	struct work_struct	cycle_count_work;
	struct work_struct	esr_sw_work;
	struct delayed_work	batt_avg_work;
	struct delayed_work	sram_dump_work;
	struct fg_circ_buf	ibatt_circ_buf;
@@ -477,4 +492,6 @@ extern void fg_circ_buf_add(struct fg_circ_buf *, int);
extern void fg_circ_buf_clr(struct fg_circ_buf *);
extern int fg_circ_buf_avg(struct fg_circ_buf *, int *);
extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *);
void fg_stay_awake(struct fg_chip *chip, int awake_reason);
void fg_relax(struct fg_chip *chip, int awake_reason);
#endif
+25 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -935,3 +935,27 @@ err_remove_fs:
	debugfs_remove_recursive(chip->dfs_root);
	return -ENOMEM;
}

void fg_stay_awake(struct fg_chip *chip, int awake_reason)
{
	spin_lock(&chip->awake_lock);

	if (!chip->awake_status)
		pm_stay_awake(chip->dev);

	chip->awake_status |= awake_reason;

	spin_unlock(&chip->awake_lock);
}

void fg_relax(struct fg_chip *chip, int awake_reason)
{
	spin_lock(&chip->awake_lock);

	chip->awake_status &= ~awake_reason;

	if (!chip->awake_status)
		pm_relax(chip->dev);

	spin_unlock(&chip->awake_lock);
}
+193 −4
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,8 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spmi.h>
#include <linux/spinlock.h>
#include <linux/alarmtimer.h>
#include <linux/of_batterydata.h>
#include <linux/iio/consumer.h>
#include <linux/qpnp/qpnp-revid.h>
@@ -74,6 +76,8 @@
#define ESR_TIMER_CHG_MAX_OFFSET	0
#define ESR_TIMER_CHG_INIT_WORD		18
#define ESR_TIMER_CHG_INIT_OFFSET	2
#define ESR_EXTRACTION_ENABLE_WORD	19
#define ESR_EXTRACTION_ENABLE_OFFSET	0
#define PROFILE_LOAD_WORD		24
#define PROFILE_LOAD_OFFSET		0
#define ESR_RSLOW_DISCHG_WORD		34
@@ -1175,6 +1179,18 @@ static bool batt_psy_initialized(struct fg_chip *chip)
	return true;
}

static bool usb_psy_initialized(struct fg_chip *chip)
{
	if (chip->usb_psy)
		return true;

	chip->usb_psy = power_supply_get_by_name("usb");
	if (!chip->usb_psy)
		return false;

	return true;
}

static bool is_parallel_charger_available(struct fg_chip *chip)
{
	if (!chip->parallel_psy)
@@ -2149,6 +2165,142 @@ static void fg_batt_avg_update(struct fg_chip *chip)
							msecs_to_jiffies(2000));
}

#define ESR_SW_FCC_UA				100000	/* 100mA */
#define ESR_EXTRACTION_ENABLE_MASK		BIT(0)
static void fg_esr_sw_work(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work,
			struct fg_chip, esr_sw_work);
	union power_supply_propval pval = {0, };
	int rc, esr_uohms = 0;

	vote(chip->awake_votable, FG_ESR_VOTER, true, 0);
	/*
	 * Enable ESR extraction just before we reduce the FCC
	 * to make sure that FG extracts the ESR. Disable ESR
	 * extraction after FCC reduction is complete to prevent
	 * any further HW pulses.
	 */
	rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
			ESR_EXTRACTION_ENABLE_OFFSET,
			ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Failed to enable ESR extraction rc=%d\n", rc);
		goto done;
	}

	/* delay for 1 FG cycle to complete */
	msleep(1500);

	/* for FCC to 100mA */
	pval.intval = ESR_SW_FCC_UA;
	rc = power_supply_set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
			&pval);
	if (rc < 0) {
		pr_err("Failed to set FCC to 100mA rc=%d\n", rc);
		goto done;
	}

	/* delay for ESR readings */
	msleep(3000);

	/* FCC to 0 (removes vote) */
	pval.intval = 0;
	rc = power_supply_set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
			&pval);
	if (rc < 0) {
		pr_err("Failed to remove FCC vote rc=%d\n", rc);
		goto done;
	}

	fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
	fg_dbg(chip, FG_STATUS, "SW ESR done ESR=%d\n", esr_uohms);

	/* restart the alarm timer */
	alarm_start_relative(&chip->esr_sw_timer,
		ms_to_ktime(chip->esr_wakeup_ms));
done:
	rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
			ESR_EXTRACTION_ENABLE_OFFSET,
			ESR_EXTRACTION_ENABLE_MASK, 0x0, FG_IMA_DEFAULT);
	if (rc < 0)
		pr_err("Failed to disable ESR extraction rc=%d\n", rc);


	vote(chip->awake_votable, FG_ESR_VOTER, false, 0);
	fg_relax(chip, FG_SW_ESR_WAKE);
}

static enum alarmtimer_restart
	fg_esr_sw_timer(struct alarm *alarm, ktime_t now)
{
	struct fg_chip *chip = container_of(alarm,
			struct fg_chip, esr_sw_timer);

	if (!chip->usb_present)
		return ALARMTIMER_NORESTART;

	fg_stay_awake(chip, FG_SW_ESR_WAKE);
	schedule_work(&chip->esr_sw_work);

	return ALARMTIMER_NORESTART;
}

static int fg_config_esr_sw(struct fg_chip *chip)
{
	int rc;
	union power_supply_propval prop = {0, };

	if (!chip->dt.use_esr_sw)
		return 0;

	if (!usb_psy_initialized(chip))
		return 0;

	rc = power_supply_get_property(chip->usb_psy,
			POWER_SUPPLY_PROP_PRESENT, &prop);
	if (rc < 0) {
		pr_err("Error in reading usb-status rc = %d\n", rc);
		return rc;
	}

	if (chip->usb_present != prop.intval) {
		chip->usb_present = prop.intval;
		fg_dbg(chip, FG_STATUS, "USB status changed=%d\n",
						chip->usb_present);
		/* cancel any pending work */
		alarm_cancel(&chip->esr_sw_timer);
		cancel_work_sync(&chip->esr_sw_work);

		if (chip->usb_present) {
			/* disable ESR extraction across the charging cycle */
			rc = fg_sram_masked_write(chip,
					ESR_EXTRACTION_ENABLE_WORD,
					ESR_EXTRACTION_ENABLE_OFFSET,
					ESR_EXTRACTION_ENABLE_MASK,
					0x0, FG_IMA_DEFAULT);
			if (rc < 0)
				return rc;
			/* wake up early for the first ESR on insertion */
			alarm_start_relative(&chip->esr_sw_timer,
				ms_to_ktime(chip->esr_wakeup_ms / 2));
		} else {
			/* enable ESR extraction on removal */
			rc = fg_sram_masked_write(chip,
					ESR_EXTRACTION_ENABLE_WORD,
					ESR_EXTRACTION_ENABLE_OFFSET,
					ESR_EXTRACTION_ENABLE_MASK,
					0x1, FG_IMA_DEFAULT);
			if (rc < 0)
				return rc;
		}
	}

	return 0;
}

static void status_change_work(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work,
@@ -2166,6 +2318,10 @@ static void status_change_work(struct work_struct *work)
		goto out;
	}

	rc = fg_config_esr_sw(chip);
	if (rc < 0)
		pr_err("Failed to config SW ESR rc=%d\n", rc);

	rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
			&prop);
	if (rc < 0) {
@@ -2234,7 +2390,7 @@ static void status_change_work(struct work_struct *work)
out:
	fg_dbg(chip, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n",
		chip->charge_status, chip->charge_type, chip->charge_done);
	pm_relax(chip->dev);
	fg_relax(chip, FG_STATUS_NOTIFY_WAKE);
}

static void restore_cycle_counter(struct fg_chip *chip)
@@ -2629,7 +2785,7 @@ out:
	chip->soc_reporting_ready = true;
	vote(chip->awake_votable, PROFILE_LOAD, false, 0);
	if (!work_pending(&chip->status_change_work)) {
		pm_stay_awake(chip->dev);
		fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE);
		schedule_work(&chip->status_change_work);
	}
}
@@ -3197,7 +3353,7 @@ static int fg_notifier_cb(struct notifier_block *nb,
		 * We cannot vote for awake votable here as that takes
		 * a mutex lock and this is executed in an atomic context.
		 */
		pm_stay_awake(chip->dev);
		fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE);
		schedule_work(&chip->status_change_work);
	}

@@ -3500,6 +3656,18 @@ static int fg_hw_init(struct fg_chip *chip)
		}
	}

	if (chip->dt.use_esr_sw) {
		/* Enable ESR extraction explicitly */
		rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
				ESR_EXTRACTION_ENABLE_OFFSET,
				ESR_EXTRACTION_ENABLE_MASK,
				0x1, FG_IMA_DEFAULT);
		if (rc < 0) {
			pr_err("Error in enabling ESR extraction rc=%d\n", rc);
			return rc;
		}
	}

	return 0;
}

@@ -4374,6 +4542,8 @@ static int fg_parse_dt(struct fg_chip *chip)
			chip->dt.esr_meas_curr_ma = temp;
	}

	chip->dt.use_esr_sw = of_property_read_bool(node, "qcom,fg-use-sw-esr");

	return 0;
}

@@ -4482,11 +4652,13 @@ static int fg_gen3_probe(struct spmi_device *spmi)
	mutex_init(&chip->cl.lock);
	mutex_init(&chip->batt_avg_lock);
	mutex_init(&chip->charge_full_lock);
	spin_lock_init(&chip->awake_lock);
	init_completion(&chip->soc_update);
	init_completion(&chip->soc_ready);
	INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_WORK(&chip->cycle_count_work, cycle_count_work);
	INIT_WORK(&chip->esr_sw_work, fg_esr_sw_work);
	INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
	INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
	dev_set_drvdata(&spmi->dev, chip);
@@ -4505,6 +4677,23 @@ static int fg_gen3_probe(struct spmi_device *spmi)
		goto exit;
	}

	if (chip->dt.use_esr_sw) {
		if (alarmtimer_get_rtcdev()) {
			alarm_init(&chip->esr_sw_timer, ALARM_BOOTTIME,
				fg_esr_sw_timer);
		} else {
			pr_err("Failed to get esw_sw alarm-timer\n");
			/* RTC always registers, hence defer until it passes */
			rc = -EPROBE_DEFER;
			goto exit;
		}
		if (chip->dt.esr_timer_charging[TIMER_MAX] != -EINVAL)
			chip->esr_wakeup_ms =
				chip->dt.esr_timer_charging[TIMER_MAX] * 1460;
		else
			chip->esr_wakeup_ms = 140000;	/* 140 seconds */
	}

	chip->fg_psy.name = "bms";
	chip->fg_psy.type = POWER_SUPPLY_TYPE_BMS;
	chip->fg_psy.properties = fg_psy_props;
Loading