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

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

Merge "power: qpnp-fg-gen3: Add support for software based ESR during charging"

parents f9f5a6e7 63e9cea7
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -458,6 +458,12 @@ First Level Node - FG Gen3 device
	Value type: <u32>
	Definition: The delay in ms for FG to enable BMD after reading RID.

- 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
==========================================================
+17 −0
Original line number Diff line number Diff line
@@ -24,9 +24,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/alarmtimer.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -51,6 +53,7 @@
#define SRAM_WRITE		"fg_sram_write"
#define PROFILE_LOAD		"fg_profile_load"
#define TTF_PRIMING		"fg_ttf_priming"
#define FG_ESR_VOTER		"fg_esr_voter"

/* Delta BSOC irq votable reasons */
#define DELTA_BSOC_IRQ_VOTER	"fg_delta_bsoc_irq"
@@ -107,6 +110,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,
@@ -273,6 +281,7 @@ struct fg_dt_props {
	bool	hold_soc_while_full;
	bool	linearize_soc;
	bool	auto_recharge_soc;
	bool	use_esr_sw;
	int	cutoff_volt_mv;
	int	empty_volt_mv;
	int	vbatt_low_thr_mv;
@@ -440,17 +449,21 @@ 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 ttf		ttf;
	struct mutex		bus_lock;
	struct mutex		sram_rw_lock;
	struct mutex		charge_full_lock;
	struct mutex		qnovo_esr_ctrl_lock;
	spinlock_t		awake_lock;
	spinlock_t		suspend_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;
@@ -482,6 +495,7 @@ struct fg_chip {
	bool			esr_flt_cold_temp_en;
	bool			slope_limit_en;
	bool			use_ima_single_mode;
	bool			usb_present;
	bool			use_dma;
	bool			qnovo_enable;
	bool			suspended;
@@ -489,6 +503,7 @@ struct fg_chip {
	struct completion	soc_ready;
	struct delayed_work	profile_load_work;
	struct work_struct	status_change_work;
	struct work_struct	esr_sw_work;
	struct delayed_work	ttf_work;
	struct delayed_work	sram_dump_work;
	struct delayed_work	pl_enable_work;
@@ -561,4 +576,6 @@ extern int fg_circ_buf_avg(struct fg_circ_buf *buf, int *avg);
extern int fg_circ_buf_median(struct fg_circ_buf *buf, int *median);
extern int fg_lerp(const struct fg_pt *pts, size_t tablesize, s32 input,
			s32 *output);
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
@@ -999,3 +999,27 @@ int fg_debugfs_create(struct fg_chip *chip)
	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);
}
+178 −3
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/ktime.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/alarmtimer.h>
#include <linux/of_platform.h>
#include <linux/of_batterydata.h>
#include <linux/platform_device.h>
@@ -2762,6 +2764,142 @@ static const char *fg_get_cycle_count(struct fg_chip *chip)
	return buf;
}

#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,
@@ -2779,6 +2917,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) {
@@ -2844,7 +2986,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 int fg_bp_params_config(struct fg_chip *chip)
@@ -3165,7 +3307,7 @@ static void profile_load_work(struct work_struct *work)
	schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000));
	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);
	}
}
@@ -4083,7 +4225,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);
	}

@@ -4421,6 +4563,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;
}

@@ -5358,6 +5512,8 @@ static int fg_parse_dt(struct fg_chip *chip)
			chip->dt.bmd_en_delay_ms = temp;
	}

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

	return 0;
}

@@ -5506,12 +5662,14 @@ static int fg_gen3_probe(struct platform_device *pdev)
	mutex_init(&chip->ttf.lock);
	mutex_init(&chip->charge_full_lock);
	mutex_init(&chip->qnovo_esr_ctrl_lock);
	spin_lock_init(&chip->awake_lock);
	spin_lock_init(&chip->suspend_lock);
	init_completion(&chip->soc_update);
	init_completion(&chip->soc_ready);
	INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
	INIT_DELAYED_WORK(&chip->pl_enable_work, pl_enable_work);
	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_WORK(&chip->esr_sw_work, fg_esr_sw_work);
	INIT_DELAYED_WORK(&chip->ttf_work, ttf_work);
	INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
	INIT_WORK(&chip->esr_filter_work, esr_filter_work);
@@ -5534,6 +5692,23 @@ static int fg_gen3_probe(struct platform_device *pdev)
		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 */
	}

	/* Register the power supply */
	fg_psy_cfg.drv_data = chip;
	fg_psy_cfg.of_node = NULL;