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

Commit aab25926 authored by Xiaozhe Shi's avatar Xiaozhe Shi
Browse files

power: qpnp-fg: support battery hotswapping



Currently the fuel gauge driver is ignorant to battery hotswaps (switching
the battery while the device is powered from a different source). This
can cause issues because the battery profile is reloaded from OTP upon
battery insertions. If a custom profile was loaded, it will be
overwritten by the one loaded in OTP.

Fix this by rerunning the battery profile loading function whenever the
battery is inserted.

CRs-Fixed: 720242
Change-Id: I1ae877d39a2a81c97243386e8683e63bd01a45e4
Signed-off-by: default avatarXiaozhe Shi <xiaozhes@codeaurora.org>
parent aa1d3459
Loading
Loading
Loading
Loading
+89 −5
Original line number Diff line number Diff line
@@ -230,6 +230,7 @@ struct fg_chip {
	spinlock_t		sec_access_lock;
	bool			profile_loaded;
	bool			use_otp_profile;
	bool			battery_missing;
	bool			power_supply_registered;
	struct delayed_work	update_jeita_setting;
	struct delayed_work	update_sram_data;
@@ -250,6 +251,7 @@ static const char *DFS_ROOT_NAME = "fg_memif";
static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
static const char *default_batt_type	= "Unknown Battery";
static const char *loading_batt_type	= "Loading Battery Data";
static const char *missing_batt_type	= "Disconnected Battery";

/* Log buffer */
struct fg_log_buffer {
@@ -842,11 +844,14 @@ close_time:
}

#define DEFAULT_CAPACITY 50
#define MISSING_CAPACITY 100
static int get_prop_capacity(struct fg_chip *chip)
{
	u8 cap[2];
	int rc, capacity = 0, tries = 0;

	if (chip->battery_missing)
		return MISSING_CAPACITY;
	if (!chip->profile_loaded && !chip->use_otp_profile)
		return DEFAULT_CAPACITY;

@@ -1031,12 +1036,13 @@ static int fg_is_batt_id_valid(struct fg_chip *chip)
#define TEMP_LSB_16B	625
#define DECIKELVIN	2730
#define SRAM_PERIOD_UPDATE_MS		30000
#define SRAM_PERIOD_NO_ID_UPDATE_MS	100
static void update_sram_data(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work,
				struct fg_chip,
				update_sram_data.work);
	int i, rc = 0;
	int i, resched_ms, rc = 0;
	u8 reg[2];
	s16 temp;
	int battid_valid = fg_is_batt_id_valid(chip);
@@ -1082,12 +1088,16 @@ static void update_sram_data(struct work_struct *work)
			pr_info("%d %d %d\n", i, temp, fg_data[i].value);
	}

	if (battid_valid)
	if (battid_valid) {
		complete_all(&chip->batt_id_avail);
		resched_ms = SRAM_PERIOD_UPDATE_MS;
	} else {
		resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
	}
	get_current_time(&chip->last_sram_update_time);
	schedule_delayed_work(
		&chip->update_sram_data,
		msecs_to_jiffies(SRAM_PERIOD_UPDATE_MS));
		msecs_to_jiffies(resched_ms));
}

static void update_jeita_setting(struct work_struct *work)
@@ -1232,6 +1242,53 @@ static void dump_sram(struct work_struct *work)
	devm_kfree(chip->dev, buffer);
}

#define BATT_MISSING_STS BIT(6)
static bool is_battery_missing(struct fg_chip *chip)
{
	int rc;
	u8 fg_batt_sts;

	rc = fg_read(chip, &fg_batt_sts,
				 INT_RT_STS(chip->batt_base), 1);
	if (rc) {
		pr_err("spmi read failed: addr=%03X, rc=%d\n",
				INT_RT_STS(chip->batt_base), rc);
		return false;
	}

	return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
}

static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
{
	struct fg_chip *chip = _chip;
	bool batt_missing = is_battery_missing(chip);

	if (batt_missing) {
		chip->battery_missing = true;
		chip->batt_type = missing_batt_type;
	} else {
		if (!chip->use_otp_profile) {
			INIT_COMPLETION(chip->batt_id_avail);
			schedule_work(&chip->batt_profile_init);
			cancel_delayed_work(&chip->update_sram_data);
			schedule_delayed_work(
				&chip->update_sram_data,
				msecs_to_jiffies(0));
		} else {
			chip->battery_missing = false;
		}
	}

	if (fg_debug_mask & FG_IRQS)
		pr_info("batt-missing triggered: %s\n",
				batt_missing ? "missing" : "present");

	if (chip->power_supply_registered)
		power_supply_changed(&chip->bms_psy);
	return IRQ_HANDLED;
}

static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
{
	struct fg_chip *chip = _chip;
@@ -1452,12 +1509,19 @@ wait:
	mutex_unlock(&chip->rw_lock);

	/* read once to get a fg cycle in */
	rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 1);
	rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
	if (rc) {
		pr_err("failed to read profile integrity rc=%d\n", rc);
		goto fail;
	}

	/*
	 * If this is not the first time a profile has been loaded, sleep for
	 * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory
	 */
	if (chip->profile_loaded)
		msleep(3000);

	mutex_lock(&chip->rw_lock);
	fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG,
			RIF_MEM_ACCESS_REQ, 0);
@@ -1544,6 +1608,7 @@ wait:
done:
	chip->batt_type = batt_type_str;
	chip->profile_loaded = true;
	chip->battery_missing = is_battery_missing(chip);
	if (chip->power_supply_registered)
		power_supply_changed(&chip->bms_psy);
	return rc;
@@ -1726,6 +1791,25 @@ static int fg_init_irqs(struct fg_chip *chip)
			}
			break;
		case FG_BATT:
			chip->batt_irq[BATT_MISSING].irq = spmi_get_irq_byname(
					chip->spmi, spmi_resource,
					"batt-missing");
			if (chip->batt_irq[BATT_MISSING].irq < 0) {
				pr_err("Unable to get batt-missing irq\n");
				rc = -EINVAL;
				return rc;
			}
			rc |= devm_request_irq(chip->dev,
					chip->batt_irq[BATT_MISSING].irq,
					fg_batt_missing_irq_handler,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING,
					"batt-missing", chip);
			if (rc < 0) {
				pr_err("Can't request %d batt-missing: %d\n",
					chip->batt_irq[BATT_MISSING].irq, rc);
				return rc;
			}
		case FG_ADC:
			break;
		default: