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

Commit 813dedfb 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: Write to profile integrity bit before restarting FG"

parents 4fee7f40 ff83c462
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@
/* Battery missing irq votable reasons */
#define BATT_MISS_IRQ_VOTER	"fg_batt_miss_irq"

#define ESR_FCC_VOTER		"fg_esr_fcc"

#define DEBUG_PRINT_BUFFER_SIZE		64
/* 3 byte address + 1 space character */
#define ADDR_LEN			4
@@ -403,6 +405,7 @@ struct fg_chip {
	struct votable		*awake_votable;
	struct votable		*delta_bsoc_irq_en_votable;
	struct votable		*batt_miss_irq_en_votable;
	struct votable		*pl_disable_votable;
	struct fg_sram_param	*sp;
	struct fg_dma_address	*addr_map;
	struct fg_alg_flag	*alg_flags;
@@ -455,11 +458,11 @@ struct fg_chip {
	bool			qnovo_enable;
	struct completion	soc_update;
	struct completion	soc_ready;
	struct completion	mem_grant;
	struct delayed_work	profile_load_work;
	struct work_struct	status_change_work;
	struct delayed_work	ttf_work;
	struct delayed_work	sram_dump_work;
	struct delayed_work	pl_enable_work;
};

/* Debugfs data structures are below */
+38 −39
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
@@ -746,15 +746,12 @@ int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset,
	return rc;
}

#define MEM_GRANT_WAIT_MS	200
#define MEM_GNT_WAIT_TIME_US	10000
#define MEM_GNT_RETRIES		50
static int fg_direct_mem_request(struct fg_chip *chip, bool request)
{
	int rc, ret;
	int rc, ret, i = 0;
	u8 val, mask;
	bool tried_again = false;

	if (request)
		reinit_completion(&chip->mem_grant);

	mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
	val = request ? MEM_ACCESS_REQ_BIT : 0;
@@ -769,7 +766,7 @@ static int fg_direct_mem_request(struct fg_chip *chip, bool request)
	rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask, val);
	if (rc < 0) {
		pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n", rc);
		return rc;
		goto release;
	}

	if (request)
@@ -780,43 +777,45 @@ static int fg_direct_mem_request(struct fg_chip *chip, bool request)
	if (!request)
		return 0;

wait:
	ret = wait_for_completion_interruptible_timeout(
		&chip->mem_grant, msecs_to_jiffies(MEM_GRANT_WAIT_MS));
	/* If we were interrupted wait again one more time. */
	if (ret <= 0) {
		if ((ret == -ERESTARTSYS || ret == 0) && !tried_again) {
			pr_debug("trying again, ret=%d\n", ret);
			tried_again = true;
			goto wait;
		} else {
			pr_err("wait for mem_grant timed out ret=%d\n",
				ret);
			fg_dump_regs(chip);
	/*
	 * HW takes 5 cycles (200 KHz clock) to grant access after requesting
	 * for DMA. Wait for 40 us before polling for MEM_GNT first time.
	 */
	usleep_range(40, 41);

	while (i < MEM_GNT_RETRIES) {
		rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &val, 1);
		if (rc < 0) {
			pr_err("Error in reading MEM_IF_INT_RT_STS, rc=%d\n",
				rc);
			goto release;
		}

		if (val & MEM_GNT_BIT)
			return 0;

		usleep_range(MEM_GNT_WAIT_TIME_US, MEM_GNT_WAIT_TIME_US + 1);
		i++;
	}

	if (ret <= 0) {
	rc = -ETIMEDOUT;
	pr_err("wait for mem_grant timed out, val=0x%x\n", val);
	fg_dump_regs(chip);

release:
	val = 0;
	mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
		rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask,
					val);
		if (rc < 0) {
			pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n",
				rc);
			return rc;
	ret = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask, val);
	if (ret < 0) {
		pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n", rc);
		return ret;
	}

	mask = MEM_ARB_LO_LATENCY_EN_BIT | MEM_ARB_REQ_BIT;
		rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask,
					val);
		if (rc < 0) {
			pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n",
				rc);
			return rc;
		}

		return -ETIMEDOUT;
	ret = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask, val);
	if (ret < 0) {
		pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n", rc);
		return ret;
	}

	return rc;
+113 −57
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
@@ -881,7 +881,7 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val)
		return 0;
	}

	if (chip->battery_missing) {
	if (chip->battery_missing || !chip->soc_reporting_ready) {
		*val = BATT_MISS_SOC;
		return 0;
	}
@@ -2093,8 +2093,12 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip)
				return 0;
			}
		} else {
			/* Charging, do nothing */
			if (!chip->recharge_soc_adjusted)
				return 0;

			/* Restore the default value */
			new_recharge_soc = recharge_soc;
			chip->recharge_soc_adjusted = false;
		}
	} else {
		/* Restore the default value */
@@ -2567,6 +2571,11 @@ static void status_change_work(struct work_struct *work)
		goto out;
	}

	if (!chip->soc_reporting_ready) {
		fg_dbg(chip, FG_STATUS, "Profile load is not complete yet\n");
		goto out;
	}

	rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
			&prop);
	if (rc < 0) {
@@ -2630,7 +2639,7 @@ static void status_change_work(struct work_struct *work)
	fg_ttf_update(chip);
	chip->prev_charge_status = chip->charge_status;
out:
	fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
	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);
}
@@ -2733,6 +2742,49 @@ static bool is_profile_load_required(struct fg_chip *chip)
	return true;
}

static void fg_update_batt_profile(struct fg_chip *chip)
{
	int rc, offset;
	u8 val;

	rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
			SW_CONFIG_OFFSET, &val, 1, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error in reading SW_CONFIG_OFFSET, rc=%d\n", rc);
		return;
	}

	/*
	 * If the RCONN had not been updated, no need to update battery
	 * profile. Else, update the battery profile so that the profile
	 * modified by bootloader or HLOS matches with the profile read
	 * from device tree.
	 */

	if (!(val & RCONN_CONFIG_BIT))
		return;

	rc = fg_sram_read(chip, ESR_RSLOW_CHG_WORD,
			ESR_RSLOW_CHG_OFFSET, &val, 1, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error in reading ESR_RSLOW_CHG_OFFSET, rc=%d\n", rc);
		return;
	}
	offset = (ESR_RSLOW_CHG_WORD - PROFILE_LOAD_WORD) * 4
			+ ESR_RSLOW_CHG_OFFSET;
	chip->batt_profile[offset] = val;

	rc = fg_sram_read(chip, ESR_RSLOW_DISCHG_WORD,
			ESR_RSLOW_DISCHG_OFFSET, &val, 1, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error in reading ESR_RSLOW_DISCHG_OFFSET, rc=%d\n", rc);
		return;
	}
	offset = (ESR_RSLOW_DISCHG_WORD - PROFILE_LOAD_WORD) * 4
			+ ESR_RSLOW_DISCHG_OFFSET;
	chip->batt_profile[offset] = val;
}

static void clear_battery_profile(struct fg_chip *chip)
{
	u8 val = 0;
@@ -2790,6 +2842,16 @@ static int __fg_restart(struct fg_chip *chip)
	return rc;
}

static void pl_enable_work(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work,
				struct fg_chip,
				pl_enable_work.work);

	vote(chip->pl_disable_votable, ESR_FCC_VOTER, false, 0);
	vote(chip->awake_votable, ESR_FCC_VOTER, false, 0);
}

static void profile_load_work(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work,
@@ -2816,6 +2878,8 @@ static void profile_load_work(struct work_struct *work)
	if (!chip->profile_available)
		goto out;

	fg_update_batt_profile(chip);

	if (!is_profile_load_required(chip))
		goto done;

@@ -2841,14 +2905,6 @@ static void profile_load_work(struct work_struct *work)
		goto out;
	}

	rc = __fg_restart(chip);
	if (rc < 0) {
		pr_err("Error in restarting FG, rc=%d\n", rc);
		goto out;
	}

	fg_dbg(chip, FG_STATUS, "SOC is ready\n");

	/* Set the profile integrity bit */
	val = HLOS_RESTART_BIT | PROFILE_LOAD_BIT;
	rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD,
@@ -2858,6 +2914,13 @@ static void profile_load_work(struct work_struct *work)
		goto out;
	}

	rc = __fg_restart(chip);
	if (rc < 0) {
		pr_err("Error in restarting FG, rc=%d\n", rc);
		goto out;
	}

	fg_dbg(chip, FG_STATUS, "SOC is ready\n");
done:
	rc = fg_bp_params_config(chip);
	if (rc < 0)
@@ -2877,13 +2940,23 @@ static void profile_load_work(struct work_struct *work)
				rc);
	}

	rc = fg_rconn_config(chip);
	if (rc < 0)
		pr_err("Error in configuring Rconn, rc=%d\n", rc);

	batt_psy_initialized(chip);
	fg_notify_charger(chip);
	chip->profile_loaded = true;
	fg_dbg(chip, FG_STATUS, "profile loaded successfully");
out:
	chip->soc_reporting_ready = true;
	vote(chip->awake_votable, ESR_FCC_VOTER, true, 0);
	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);
		schedule_work(&chip->status_change_work);
	}
}

static void sram_dump_work(struct work_struct *work)
@@ -4071,12 +4144,6 @@ static int fg_hw_init(struct fg_chip *chip)
		return rc;
	}

	rc = fg_rconn_config(chip);
	if (rc < 0) {
		pr_err("Error in configuring Rconn, rc=%d\n", rc);
		return rc;
	}

	fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER,
		chip->dt.esr_tight_flt_upct, buf);
	rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR_TIGHT_FILTER].addr_word,
@@ -4172,25 +4239,6 @@ static int fg_adjust_timebase(struct fg_chip *chip)

/* INTERRUPT HANDLERS STAY HERE */

static irqreturn_t fg_dma_grant_irq_handler(int irq, void *data)
{
	struct fg_chip *chip = data;
	u8 status;
	int rc;

	rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &status, 1);
	if (rc < 0) {
		pr_err("failed to read addr=0x%04x, rc=%d\n",
			MEM_IF_INT_RT_STS(chip), rc);
		return IRQ_HANDLED;
	}

	fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
	complete_all(&chip->mem_grant);

	return IRQ_HANDLED;
}

static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
{
	struct fg_chip *chip = data;
@@ -4250,6 +4298,8 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data)
		chip->profile_loaded = false;
		chip->soc_reporting_ready = false;
		chip->batt_id_ohms = -EINVAL;
		cancel_delayed_work_sync(&chip->pl_enable_work);
		vote(chip->pl_disable_votable, ESR_FCC_VOTER, true, 0);
		return IRQ_HANDLED;
	}

@@ -4476,7 +4526,7 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = {
	/* MEM_IF irqs */
	[DMA_GRANT_IRQ] = {
		.name		= "dma-grant",
		.handler	= fg_dma_grant_irq_handler,
		.handler	= fg_dummy_irq_handler,
		.wakeable	= true,
	},
	[MEM_XCP_IRQ] = {
@@ -5105,6 +5155,12 @@ static int fg_gen3_probe(struct platform_device *pdev)
		}
	}

	chip->pl_disable_votable = find_votable("PL_DISABLE");
	if (chip->pl_disable_votable == NULL) {
		rc = -EPROBE_DEFER;
		goto exit;
	}

	chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb,
					chip);
	if (IS_ERR(chip->awake_votable)) {
@@ -5147,8 +5203,8 @@ static int fg_gen3_probe(struct platform_device *pdev)
	mutex_init(&chip->qnovo_esr_ctrl_lock);
	init_completion(&chip->soc_update);
	init_completion(&chip->soc_ready);
	init_completion(&chip->mem_grant);
	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_DELAYED_WORK(&chip->ttf_work, ttf_work);
	INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
@@ -5162,23 +5218,6 @@ static int fg_gen3_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, chip);

	rc = fg_register_interrupts(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
			rc);
		goto exit;
	}

	/* Keep SOC_UPDATE irq disabled until we require it */
	if (fg_irqs[SOC_UPDATE_IRQ].irq)
		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);

	/* Keep BSOC_DELTA_IRQ disabled until we require it */
	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);

	/* Keep BATT_MISSING_IRQ disabled until we require it */
	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);

	rc = fg_hw_init(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n",
@@ -5206,6 +5245,23 @@ static int fg_gen3_probe(struct platform_device *pdev)
		goto exit;
	}

	rc = fg_register_interrupts(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
			rc);
		goto exit;
	}

	/* Keep SOC_UPDATE irq disabled until we require it */
	if (fg_irqs[SOC_UPDATE_IRQ].irq)
		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);

	/* Keep BSOC_DELTA_IRQ disabled until we require it */
	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);

	/* Keep BATT_MISSING_IRQ disabled until we require it */
	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);

	rc = fg_debugfs_create(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",