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

Commit a8bd6f06 authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Harry Yang
Browse files

power: qcom-charger: separate parallel code



Currently the parallel charging code is tightly coupled in the main charger
files.  Update the design to separate parallel charging code. This
parallel charging code will implement few votables and interacts with
the main and parallel charger.

A brief discussion on important changes

Usb main psy
The new design introduces a charger type called USB-Main which exposes all
the properties required to implement parallel charging for the main
charger. These properties are implemented/supported

- CONSTANT_CHARGE_CURRENT_MAX, sets and gets the FCC of the main charger
- ICL_REDUCTION, indicates by how much the main charger should reduce
  its ICL. The parallel charger would draw some portion of the ICL and
  this implementation ensures that the main charger reduce its ICL in
  order to not exceed the adapter limits.
- VOLTAGE_MAX, sets the float voltage of the battery, we continue to
    raise float voltage for the parallel charger so that it operates as
    current source.
- INPUT_CURRENT_SETTLED: indicates the AICL results.
- FCC_DELTA: indicates the value by which the main is correcting the
    FCC. The main reduces FCC by few mA when in Jeita or Step
    charging. This helps in determining the exact FCC which in turn
    helps distribute it correctly.

Slave percent
The new file implements the sysfs entry for slave percent. When
thermal balancer updates it, it ends up calling fcc distribution
and also the icl distribution. Note that the icl distribution too
is dependent on the slave's fcc percent - in that it gives 10
percent point more to the main charger.

ICL
ICL needs to be distributed only in USBIN_USBIN configuration.
As noted above the ICL distribution is based on fcc distribution
aka slave_percent. The main charger is given 10% more ICL since it
caters to system load in addition to charging the battery. This is
similar to our traditional USBIN USBIN implementation where FCC was
distributed 50/50 while ICL was distributed 60/40 percent - the main
got 10% more of the ICL value.
Note that the ICL distribution is invoked when AICL settled current
changes, not when the ICL itself changes. The new code tracks the
aicl settled value and invokes the icl distribution when it changes.
It first requests the main charger to reduce its icl and then updates
the parallel's icl.

FCC Votable
The FCC votable is moved to the new file and is similar to its previous
implementation - It distributes FCC when ever the FCC value changes, the
slave_pct changes or parallel charger is enabled or disabled. If the main
charger is further modifying the FCC from what its registers were
configured, this FCC callback takes that in to account via the
FCC_DELTA property prior to distribution.

FV Votable
Like the FCC votable the FV votable is moved to the new file.
The code is similar as it was implemented in the main file where
it distributes FV when ever the FV value changes.

TAPER entry
The code that stepwise reduces slave's ICL as it enters taper mode
is moved to this file. So that it can correctly do this, the
charge type is tracked in its internal structure.

PL_DISABLE Votable
The PL_DISABLE too is moved to a new file. There are few implementation
changes here.
- Earlier the code used to run the same sequence while enabling and
  disabling. This could create a situation where FCC/ICL is incorrectly
  configured for a brief period. That has been corrected, in that
  the enable configures in this order configures fv, distributes fcc,
  enables parallel, distributes aicl. The disable does that in reverse
  order of enable.
- Charger's presence would vote on enabling/disabling parallel charger.
  This is no more required. Instead a vote from CHG_STATE_VOTER suffices
  for when fast charging begins. The voters that enable/disable parallel
  when typec is present or is in sink mode or when uUSB cable is
  inserted are removed.

CHG_STATE_VOTER and TAPER_END_VOTER
A typical charge cycle enters fast charging, moves to taper charging and
finally goes to end of charging. Parallel charging needs to be
enabled while fast charging starts and needs to be kept enabled during
the portion of taper charging when parallel's FCC is above 500mA threshold.
Note that if charging starts from taper, we only want to run it so long
as the parallel's FCC stays higher than 500mA threshold.

CHG_STATE_VOTER votes to enable parallel charging when fast charging
or parallel charging is in progress. When charging stops (i.e. when its not
in fast or taper charging) CHG_STATE_VOTER votes to disable parallel
charging.

Also the TAPER_END_VOTER votes to disable parallel charging when
parallel's FCC share falls below 500mA. It continues to vote disable on
parallel charging until charging stops. At that point it votes to
enable parallel charging.

Between these two voters, parallel charger
- enables when fast charging starts and remains enabled as it
 transitions to taper charge.
- is disabled once parallel's FCC falls below 500mA threshold and
remains disabled in taper charge until charging stops.

This makes it important that the battery psy provide correct psy
changes when it enters/exit FAST or TAPER type. The PMI chip and its
battery_psy implementation already guarantees that.

PL_TAPER_EARLY_BAD_VOTER
During some runs we see that parallel operates in taper mode. Given that
we sets its FV higher than main's FV, the parallel should never operate
in taper mode. This voter disables parallel charging if this happens.
This voter enables parallel charging when the main charger stops
charging.

Change-Id: If1676cd126d7eeace7c44cf8d819038d03433d7e
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent ae899e25
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
obj-$(CONFIG_QPNP_FG_GEN3)     += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_SMB135X_CHARGER)   += smb135x-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o
obj-$(CONFIG_QPNP_SMB2)		+= qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_SMB138X_CHARGER)	+= smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_QPNP_QNOVO)	+= qpnp-qnovo.o
obj-$(CONFIG_QPNP_SMB2)		+= qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o battery.o
obj-$(CONFIG_SMB138X_CHARGER)	+= smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
obj-$(CONFIG_QPNP_QNOVO)	+= qpnp-qnovo.o battery.o
+730 −0
Original line number Diff line number Diff line
/* Copyright (c) 2017 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
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__

#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/power_supply.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/printk.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
#include "pmic-voter.h"

#define DRV_MAJOR_VERSION	1
#define DRV_MINOR_VERSION	0

#define CHG_STATE_VOTER			"CHG_STATE_VOTER"
#define TAPER_END_VOTER			"TAPER_END_VOTER"
#define PL_TAPER_EARLY_BAD_VOTER	"PL_TAPER_EARLY_BAD_VOTER"
#define PARALLEL_PSY_VOTER		"PARALLEL_PSY_VOTER"

struct pl_data {
	int			pl_mode;
	int			slave_pct;
	int			taper_pct;
	int			slave_fcc_ua;
	struct votable		*fcc_votable;
	struct votable		*fv_votable;
	struct votable		*pl_disable_votable;
	struct work_struct	status_change_work;
	struct delayed_work	pl_taper_work;
	struct power_supply	*main_psy;
	struct power_supply	*pl_psy;
	struct power_supply	*batt_psy;
	int			settled_ua;
	int			charge_type;
	struct class		qcom_batt_class;
	struct wakeup_source	*pl_ws;
	struct notifier_block	nb;
};

struct pl_data *the_chip;

enum print_reason {
	PR_PARALLEL	= BIT(0),
};

static int debug_mask;
module_param_named(debug_mask, debug_mask, int, 0600);

#define pl_dbg(chip, reason, fmt, ...)				\
	do {								\
		if (debug_mask & (reason))				\
			pr_info(fmt, ##__VA_ARGS__);	\
		else							\
			pr_debug(fmt, ##__VA_ARGS__);		\
	} while (0)

enum {
	VER = 0,
	SLAVE_PCT,
};

/*******
 * ICL *
 ********/
static void split_settled(struct pl_data *chip)
{
	int slave_icl_pct;
	int slave_ua;
	union power_supply_propval pval = {0, };
	int rc;

	/* TODO some parallel chargers do not have a fine ICL resolution. For
	 * them implement a psy interface which returns the closest lower ICL
	 * for desired split
	 */

	if (chip->pl_mode != POWER_SUPPLY_PARALLEL_USBIN_USBIN)
		return;

	if (chip->main_psy)
		return;

	slave_ua = 0;

	if (!get_effective_result_locked(chip->pl_disable_votable)) {
		/* read the aicl settled value */
		rc = power_supply_get_property(chip->main_psy,
			       POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval);
		if (rc < 0) {
			pr_err("Couldn't get aicl settled value rc=%d\n", rc);
			return;
		}
		chip->settled_ua = pval.intval;
		/* slave gets 10 percent points less for ICL */
		slave_icl_pct = max(0, chip->slave_pct - 10);
		slave_ua = (chip->settled_ua * slave_icl_pct) / 100;
	}

	/* ICL_REDUCTION on main could be 0mA when pl is disabled */
	pval.intval = slave_ua;
	rc = power_supply_set_property(chip->main_psy,
			POWER_SUPPLY_PROP_ICL_REDUCTION, &pval);
	if (rc < 0) {
		pr_err("Couldn't change slave suspend state rc=%d\n", rc);
		return;
	}

	/* set parallel's ICL  could be 0mA when pl is disabled */
	pval.intval = slave_ua;
	rc = power_supply_set_property(chip->pl_psy,
			POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
	if (rc < 0) {
		pr_err("Couldn't set parallel icl, rc=%d\n", rc);
		return;
	}
}

static ssize_t version_show(struct class *c, struct class_attribute *attr,
			char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%d.%d\n",
			DRV_MAJOR_VERSION, DRV_MINOR_VERSION);
}

/*************
 * SLAVE PCT *
 **************/
static ssize_t slave_pct_show(struct class *c, struct class_attribute *attr,
			char *ubuf)
{
	struct pl_data *chip = container_of(c, struct pl_data,
			qcom_batt_class);

	return snprintf(ubuf, PAGE_SIZE, "%d\n", chip->slave_pct);
}

static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr,
			const char *ubuf, size_t count)
{
	struct pl_data *chip = container_of(c, struct pl_data,
			qcom_batt_class);
	unsigned long val;

	if (kstrtoul(ubuf, 10, &val))
		return -EINVAL;

	chip->slave_pct = val;
	rerun_election(chip->fcc_votable);
	rerun_election(chip->fv_votable);
	split_settled(chip);

	return count;
}

static struct class_attribute pl_attributes[] = {
	[VER]			= __ATTR_RO(version),
	[SLAVE_PCT]		= __ATTR(parallel_pct, 0644,
					slave_pct_show, slave_pct_store),
	__ATTR_NULL,
};

/***********
 *  TAPER  *
 ************/
#define MINIMUM_PARALLEL_FCC_UA		500000
#define PL_TAPER_WORK_DELAY_MS		100
#define TAPER_RESIDUAL_PCT		75
static void pl_taper_work(struct work_struct *work)
{
	struct pl_data *chip = container_of(work, struct pl_data,
						pl_taper_work.work);
	union power_supply_propval pval = {0, };
	int rc;

	/* exit immediately if parallel is disabled */
	if (get_effective_result(chip->pl_disable_votable)) {
		pl_dbg(chip, PR_PARALLEL, "terminating parallel not in progress\n");
		goto done;
	}

	pl_dbg(chip, PR_PARALLEL, "entering parallel taper work slave_fcc = %d\n",
			chip->slave_fcc_ua);
	if (chip->slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) {
		pl_dbg(chip, PR_PARALLEL, "terminating parallel's share lower than 500mA\n");
		vote(chip->pl_disable_votable, TAPER_END_VOTER, true, 0);
		goto done;
	}

	rc = power_supply_get_property(chip->batt_psy,
			       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
	if (rc < 0) {
		pr_err("Couldn't get batt charge type rc=%d\n", rc);
		goto done;
	}

	chip->charge_type = pval.intval;
	if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
		pl_dbg(chip, PR_PARALLEL, "master is taper charging; reducing slave FCC\n");

		__pm_stay_awake(chip->pl_ws);
		/* Reduce the taper percent by 25 percent */
		chip->taper_pct = chip->taper_pct * TAPER_RESIDUAL_PCT / 100;
		rerun_election(chip->fcc_votable);
		pl_dbg(chip, PR_PARALLEL, "taper entry scheduling work after %d ms\n",
				PL_TAPER_WORK_DELAY_MS);
		schedule_delayed_work(&chip->pl_taper_work,
				msecs_to_jiffies(PL_TAPER_WORK_DELAY_MS));
		return;
	}

	/*
	 * Master back to Fast Charge, get out of this round of taper reduction
	 */
	pl_dbg(chip, PR_PARALLEL, "master is fast charging; waiting for next taper\n");

done:
	__pm_relax(chip->pl_ws);
}

/*********
 *  FCC  *
 **********/
#define EFFICIENCY_PCT	80
#define MICRO_5V	5000000
static void split_fcc(struct pl_data *chip, int total_ua,
			int *master_ua, int *slave_ua)
{
	int rc, effective_total_ua, slave_limited_ua, hw_cc_delta_ua = 0,
		    aicl_settled_ua, input_limited_fcc_ua;
	union power_supply_propval pval = {0, };

	rc = power_supply_get_property(chip->main_psy,
			       POWER_SUPPLY_PROP_FCC_DELTA, &pval);
	if (rc < 0)
		hw_cc_delta_ua = 0;
	else
		hw_cc_delta_ua = pval.intval;

	input_limited_fcc_ua = INT_MAX;
	if (chip->pl_mode == POWER_SUPPLY_PARALLEL_MID_MID) {
		rc = power_supply_get_property(chip->main_psy,
				       POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
				       &pval);
		if (rc < 0)
			aicl_settled_ua = 0;
		else
			aicl_settled_ua = pval.intval;

		input_limited_fcc_ua = div64_s64(
			(s64)aicl_settled_ua * MICRO_5V * EFFICIENCY_PCT,
			(s64)get_effective_result(chip->fv_votable)
			* 100);
	}

	effective_total_ua = max(0, total_ua + hw_cc_delta_ua);
	slave_limited_ua = min(effective_total_ua, input_limited_fcc_ua);
	*slave_ua = (slave_limited_ua * chip->slave_pct) / 100;
	*slave_ua = (*slave_ua * chip->taper_pct) / 100;
	*master_ua = max(0, total_ua - *slave_ua);
}

static int pl_fcc_vote_callback(struct votable *votable, void *data,
			int total_fcc_ua, const char *client)
{
	struct pl_data *chip = data;
	union power_supply_propval pval = {0, };
	int rc, master_fcc_ua = total_fcc_ua, slave_fcc_ua = 0;

	if (total_fcc_ua < 0)
		return 0;

	if (!chip->main_psy)
		return 0;

	if (chip->pl_mode == POWER_SUPPLY_PARALLEL_NONE
	    || get_effective_result_locked(chip->pl_disable_votable)) {
		pval.intval = total_fcc_ua;
		rc = power_supply_set_property(chip->main_psy,
				POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
				&pval);
		if (rc < 0)
			pr_err("Couldn't set main fcc, rc=%d\n", rc);
		return rc;
	}

	split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
	pval.intval = slave_fcc_ua;
	rc = power_supply_set_property(chip->pl_psy,
			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
	if (rc < 0) {
		pr_err("Couldn't set parallel fcc, rc=%d\n", rc);
		return rc;
	}

	chip->slave_fcc_ua = slave_fcc_ua;

	pval.intval = master_fcc_ua;
	rc = power_supply_set_property(chip->main_psy,
			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
	if (rc < 0) {
		pr_err("Could not set main fcc, rc=%d\n", rc);
		return rc;
	}

	pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
		   master_fcc_ua, slave_fcc_ua,
		   (master_fcc_ua * 100) / total_fcc_ua,
		   (slave_fcc_ua * 100) / total_fcc_ua);

	return 0;
}

#define PARALLEL_FLOAT_VOLTAGE_DELTA_UV 50000
static int pl_fv_vote_callback(struct votable *votable, void *data,
			int fv_uv, const char *client)
{
	struct pl_data *chip = data;
	union power_supply_propval pval = {0, };
	int rc = 0;

	if (fv_uv < 0)
		return 0;

	if (!chip->main_psy)
		return 0;

	pval.intval = fv_uv;
	rc = power_supply_set_property(chip->main_psy,
			POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
	if (rc < 0) {
		pr_err("Couldn't set main fv, rc=%d\n", rc);
		return rc;
	}

	if (chip->pl_mode != POWER_SUPPLY_PARALLEL_NONE) {
		pval.intval = fv_uv + PARALLEL_FLOAT_VOLTAGE_DELTA_UV;
		rc = power_supply_set_property(chip->pl_psy,
				POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
		if (rc < 0) {
			pr_err("Couldn't set float on parallel rc=%d\n", rc);
			return rc;
		}
	}

	return 0;
}

static int pl_disable_vote_callback(struct votable *votable,
		void *data, int pl_disable, const char *client)
{
	struct pl_data *chip = data;
	union power_supply_propval pval = {0, };
	int rc;

	chip->settled_ua = 0;
	chip->taper_pct = 100;

	if (!pl_disable) { /* enable */
		rerun_election(chip->fv_votable);
		rerun_election(chip->fcc_votable);

		if (chip->pl_psy) {
			pval.intval = 0;
			rc = power_supply_set_property(chip->pl_psy,
					POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
			if (rc < 0)
				pr_err("Couldn't change slave suspend state rc=%d\n",
					rc);
		}

		if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN)
			split_settled(chip);
		/*
		 * we could have been enabled while in taper mode,
		 *  start the taper work if so
		 */
		rc = power_supply_get_property(chip->batt_psy,
				       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
		if (rc < 0) {
			pr_err("Couldn't get batt charge type rc=%d\n", rc);
		} else {
			if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
				pl_dbg(chip, PR_PARALLEL,
					"pl enabled in Taper scheduing work\n");
				schedule_delayed_work(&chip->pl_taper_work, 0);
			}
		}
	} else {
		if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN)
			split_settled(chip);

		if (chip->pl_psy) {
			pval.intval = 1;
			rc = power_supply_set_property(chip->pl_psy,
					POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
			if (rc < 0)
				pr_err("Couldn't change slave suspend state rc=%d\n",
					rc);
		}
		rerun_election(chip->fcc_votable);
		rerun_election(chip->fv_votable);
	}

	pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n",
		   pl_disable ? "disabled" : "enabled");

	return 0;
}

static bool is_main_available(struct pl_data *chip)
{
	if (!chip->main_psy)
		chip->main_psy = power_supply_get_by_name("main");

	if (!chip->main_psy)
		return false;

	return true;
}

static bool is_batt_available(struct pl_data *chip)
{
	if (!chip->batt_psy)
		chip->batt_psy = power_supply_get_by_name("battery");

	if (!chip->batt_psy)
		return false;

	return true;
}

static bool is_parallel_available(struct pl_data *chip)
{
	union power_supply_propval pval = {0, };
	int rc;

	if (chip->pl_psy)
		return true;

	chip->pl_psy = power_supply_get_by_name("parallel");
	if (!chip->pl_psy)
		return false;

	rc = power_supply_get_property(chip->pl_psy,
			       POWER_SUPPLY_PROP_PARALLEL_MODE, &pval);
	if (rc < 0) {
		pr_err("Couldn't get parallel mode from parallel rc=%d\n",
				rc);
		return false;
	}
	/*
	 * Note that pl_mode only be udpated to anything other than a _NONE
	 * only after pl_psy is found. IOW pl_mode != _NONE implies that
	 * pl_psy is present and valid
	 */
	chip->pl_mode = pval.intval;
	vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0);

	return true;
}

static void handle_main_charge_type(struct pl_data *chip)
{
	union power_supply_propval pval = {0, };
	int rc;

	rc = power_supply_get_property(chip->batt_psy,
			       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
	if (rc < 0) {
		pr_err("Couldn't get batt charge type rc=%d\n", rc);
		return;
	}

	/* not fast/not taper state to disables parallel */
	if ((pval.intval != POWER_SUPPLY_CHARGE_TYPE_FAST)
		&& (pval.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER)) {
		vote(chip->pl_disable_votable, CHG_STATE_VOTER, true, 0);
		chip->taper_pct = 100;
		vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0);
		vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER,
				false, 0);
		chip->charge_type = pval.intval;
		return;
	}

	/* handle fast/taper charge entry */
	if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER
			|| pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST) {
		pl_dbg(chip, PR_PARALLEL, "chg_state enabling parallel\n");
		vote(chip->pl_disable_votable, CHG_STATE_VOTER, false, 0);
		chip->charge_type = pval.intval;
		return;
	}

	/* handle taper charge entry */
	if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_FAST
		&& (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER)) {
		chip->charge_type = pval.intval;
		pl_dbg(chip, PR_PARALLEL, "taper entry scheduling work\n");
		schedule_delayed_work(&chip->pl_taper_work, 0);
		return;
	}

	/* remember the new state only if it isn't any of the above */
	chip->charge_type = pval.intval;
}

static void handle_settled_aicl_split(struct pl_data *chip)
{
	union power_supply_propval pval = {0, };
	int rc;

	if (!get_effective_result(chip->pl_disable_votable)
		&& chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) {
		/*
		 * call aicl split only when USBIN_USBIN and enabled
		 * and if aicl changed
		 */
		rc = power_supply_get_property(chip->main_psy,
				       POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
				       &pval);
		if (rc < 0) {
			pr_err("Couldn't get aicl settled value rc=%d\n", rc);
			return;
		}
		if (chip->settled_ua != pval.intval) {
			chip->settled_ua = pval.intval;
			split_settled(chip);
		}
	}
}

static void handle_parallel_in_taper(struct pl_data *chip)
{
	union power_supply_propval pval = {0, };
	int rc;

	if (get_effective_result_locked(chip->pl_disable_votable))
		return;

	if (!chip->pl_psy)
		return;

	rc = power_supply_get_property(chip->pl_psy,
			       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
	if (rc < 0) {
		pr_err("Couldn't get pl charge type rc=%d\n", rc);
		return;
	}

	/*
	 * if parallel is seen in taper mode ever, that is an anomaly and
	 * we disable parallel charger
	 */
	if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
		vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER,
				true, 0);
		return;
	}
}

static void status_change_work(struct work_struct *work)
{
	struct pl_data *chip = container_of(work,
			struct pl_data, status_change_work);

	if (!is_main_available(chip))
		return;

	if (!is_batt_available(chip))
		return;

	is_parallel_available(chip);

	handle_main_charge_type(chip);
	handle_settled_aicl_split(chip);
	handle_parallel_in_taper(chip);
}

static int pl_notifier_call(struct notifier_block *nb,
		unsigned long ev, void *v)
{
	struct power_supply *psy = v;
	struct pl_data *chip = container_of(nb, struct pl_data, nb);

	if (ev != PSY_EVENT_PROP_CHANGED)
		return NOTIFY_OK;

	if ((strcmp(psy->desc->name, "parallel") == 0)
	    || (strcmp(psy->desc->name, "battery") == 0))
		schedule_work(&chip->status_change_work);

	return NOTIFY_OK;
}

static int pl_register_notifier(struct pl_data *chip)
{
	int rc;

	chip->nb.notifier_call = pl_notifier_call;
	rc = power_supply_reg_notifier(&chip->nb);
	if (rc < 0) {
		pr_err("Couldn't register psy notifier rc = %d\n", rc);
		return rc;
	}

	return 0;
}

static int pl_determine_initial_status(struct pl_data *chip)
{
	status_change_work(&chip->status_change_work);
	return 0;
}

static int pl_init(void)
{
	struct pl_data *chip;
	int rc = 0;

	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;
	chip->slave_pct = 50;

	chip->pl_ws = wakeup_source_register("qcom-battery");
	if (!chip->pl_ws)
		goto cleanup;

	chip->fcc_votable = create_votable("FCC", VOTE_MIN,
					pl_fcc_vote_callback,
					chip);
	if (IS_ERR(chip->fcc_votable)) {
		rc = PTR_ERR(chip->fcc_votable);
		goto release_wakeup_source;
	}

	chip->fv_votable = create_votable("FV", VOTE_MAX,
					pl_fv_vote_callback,
					chip);
	if (IS_ERR(chip->fv_votable)) {
		rc = PTR_ERR(chip->fv_votable);
		goto destroy_votable;
	}

	chip->pl_disable_votable = create_votable("PL_DISABLE", VOTE_SET_ANY,
					pl_disable_vote_callback,
					chip);
	if (IS_ERR(chip->pl_disable_votable)) {
		rc = PTR_ERR(chip->pl_disable_votable);
		goto destroy_votable;
	}
	vote(chip->pl_disable_votable, CHG_STATE_VOTER, true, 0);
	vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0);
	vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, true, 0);

	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);

	rc = pl_register_notifier(chip);
	if (rc < 0) {
		pr_err("Couldn't register psy notifier rc = %d\n", rc);
		goto unreg_notifier;
	}

	rc = pl_determine_initial_status(chip);
	if (rc < 0) {
		pr_err("Couldn't determine initial status rc=%d\n", rc);
		goto unreg_notifier;
	}

	chip->qcom_batt_class.name = "qcom-battery",
	chip->qcom_batt_class.owner = THIS_MODULE,
	chip->qcom_batt_class.class_attrs = pl_attributes;

	rc = class_register(&chip->qcom_batt_class);
	if (rc < 0) {
		pr_err("couldn't register pl_data sysfs class rc = %d\n", rc);
		goto unreg_notifier;
	}

	return rc;

unreg_notifier:
	power_supply_unreg_notifier(&chip->nb);
destroy_votable:
	destroy_votable(chip->pl_disable_votable);
	destroy_votable(chip->fv_votable);
	destroy_votable(chip->fcc_votable);
release_wakeup_source:
	wakeup_source_unregister(chip->pl_ws);
cleanup:
	kfree(chip);
	return rc;
}

static void pl_deinit(void)
{
	struct pl_data *chip = the_chip;

	power_supply_unreg_notifier(&chip->nb);
	destroy_votable(chip->pl_disable_votable);
	destroy_votable(chip->fv_votable);
	destroy_votable(chip->fcc_votable);
	wakeup_source_unregister(chip->pl_ws);
	kfree(chip);
}

module_init(pl_init);
module_exit(pl_deinit)

MODULE_DESCRIPTION("");
MODULE_LICENSE("GPL v2");
+116 −30

File changed.

Preview size limit exceeded, changes collapsed.

+49 −266

File changed.

Preview size limit exceeded, changes collapsed.

+5 −7
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ enum print_reason {
#define DCP_VOTER			"DCP_VOTER"
#define USB_PSY_VOTER			"USB_PSY_VOTER"
#define PL_TAPER_WORK_RUNNING_VOTER	"PL_TAPER_WORK_RUNNING_VOTER"
#define PARALLEL_PSY_VOTER		"PARALLEL_PSY_VOTER"
#define PL_INDIRECT_VOTER		"PL_INDIRECT_VOTER"
#define USBIN_I_VOTER			"USBIN_I_VOTER"
#define USBIN_V_VOTER			"USBIN_V_VOTER"
@@ -128,9 +127,6 @@ struct smb_params {

struct parallel_params {
	struct power_supply	*psy;
	int			slave_pct;
	int			taper_pct;
	int			slave_fcc_ua;
};

struct smb_iio {
@@ -170,6 +166,7 @@ struct smb_charger {
	struct power_supply		*dc_psy;
	struct power_supply		*bms_psy;
	struct power_supply_desc	usb_psy_desc;
	struct power_supply		*usb_main_psy;

	/* notifiers */
	struct notifier_block	nb;
@@ -202,11 +199,9 @@ struct smb_charger {

	/* work */
	struct work_struct	bms_update_work;
	struct work_struct	pl_detect_work;
	struct work_struct	rdstd_cc2_detach_work;
	struct delayed_work	hvdcp_detect_work;
	struct delayed_work	ps_change_timeout_work;
	struct delayed_work	pl_taper_work;
	struct delayed_work	step_soc_req_work;
	struct delayed_work	clear_hdc_work;

@@ -226,7 +221,6 @@ struct smb_charger {
	bool			is_hdc;
	bool			chg_done;
	bool			micro_usb_mode;
	int			input_limited_fcc_ua;
	bool			otg_en;
	bool			vconn_en;
	int			otg_attempts;
@@ -240,6 +234,8 @@ struct smb_charger {
	/* extcon for VBUS / ID notification to USB for uUSB */
	struct extcon_dev	*extcon;
	bool			usb_ever_removed;

	int			icl_reduction_ua;
};

int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -386,6 +382,8 @@ int smblib_set_prop_ship_mode(struct smb_charger *chg,
				const union power_supply_propval *val);
void smblib_suspend_on_debug_battery(struct smb_charger *chg);
int smblib_rerun_apsd_if_required(struct smb_charger *chg);
int smblib_get_prop_fcc_delta(struct smb_charger *chg,
			       union power_supply_propval *val);

int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
Loading