summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorAbhijeet Dharmapurikar <adharmap@codeaurora.org>2017-01-30 15:43:04 -0800
committerAbhijeet Dharmapurikar <adharmap@codeaurora.org>2017-01-31 22:23:39 -0800
commitf0cf35f5921410fed20e7d2d5f89ed3326430a08 (patch)
tree429f70ea0e0e58babe4a3be363c7634e8c9c5b30 /drivers/power
parent684caa6de452edc82398b9f5b35c706caa535225 (diff)
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: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/supply/qcom/Makefile6
-rw-r--r--drivers/power/supply/qcom/battery.c730
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c146
-rw-r--r--drivers/power/supply/qcom/smb-lib.c315
-rw-r--r--drivers/power/supply/qcom/smb-lib.h12
-rw-r--r--drivers/power/supply/qcom/smb1351-charger.c7
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c10
7 files changed, 917 insertions, 309 deletions
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 0126d2d0a18e..912acb0c788a 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -6,6 +6,6 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o
obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.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
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
new file mode 100644
index 000000000000..5f1b6b294b5f
--- /dev/null
+++ b/drivers/power/supply/qcom/battery.c
@@ -0,0 +1,730 @@
+/* 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, S_IRUSR | S_IWUSR);
+
+#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, S_IRUGO | S_IWUSR,
+ 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");
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 8dc93f42b059..dea932ae37ad 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -479,7 +479,7 @@ static int smb2_usb_get_prop(struct power_supply *psy,
rc = smblib_get_pe_start(chg, val);
break;
default:
- pr_err("get prop %d is not supported\n", psp);
+ pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
break;
}
@@ -574,6 +574,118 @@ static int smb2_init_usb_psy(struct smb2 *chip)
return 0;
}
+/*****************************
+ * USB MAIN PSY REGISTRATION *
+ *****************************/
+
+static enum power_supply_property smb2_usb_main_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_ICL_REDUCTION,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+ POWER_SUPPLY_PROP_FCC_DELTA,
+ /*
+ * TODO move the TEMP and TEMP_MAX properties here,
+ * and update the thermal balancer to look here
+ */
+};
+
+static int smb2_usb_main_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct smb2 *chip = power_supply_get_drvdata(psy);
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
+ break;
+ case POWER_SUPPLY_PROP_ICL_REDUCTION:
+ val->intval = chg->icl_reduction_ua;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ rc = smblib_get_charge_param(chg, &chg->param.fcc,
+ &val->intval);
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_MAIN;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
+ rc = smblib_get_prop_input_current_settled(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_FCC_DELTA:
+ rc = smblib_get_prop_fcc_delta(chg, val);
+ break;
+ default:
+ pr_err("get prop %d is not supported in usb-main\n", psp);
+ rc = -EINVAL;
+ break;
+ }
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
+ return 0;
+}
+
+static int smb2_usb_main_set_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smb2 *chip = power_supply_get_drvdata(psy);
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_ICL_REDUCTION:
+ chg->icl_reduction_ua = val->intval;
+ rc = rerun_election(chg->usb_icl_votable);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval);
+ break;
+ default:
+ pr_err("set prop %d is not supported\n", psp);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct power_supply_desc usb_main_psy_desc = {
+ .name = "main",
+ .type = POWER_SUPPLY_TYPE_MAIN,
+ .properties = smb2_usb_main_props,
+ .num_properties = ARRAY_SIZE(smb2_usb_main_props),
+ .get_property = smb2_usb_main_get_prop,
+ .set_property = smb2_usb_main_set_prop,
+};
+
+static int smb2_init_usb_main_psy(struct smb2 *chip)
+{
+ struct power_supply_config usb_main_cfg = {};
+ struct smb_charger *chg = &chip->chg;
+
+ usb_main_cfg.drv_data = chip;
+ usb_main_cfg.of_node = chg->dev->of_node;
+ chg->usb_main_psy = devm_power_supply_register(chg->dev,
+ &usb_main_psy_desc,
+ &usb_main_cfg);
+ if (IS_ERR(chg->usb_main_psy)) {
+ pr_err("Couldn't register USB main power supply\n");
+ return PTR_ERR(chg->usb_main_psy);
+ }
+
+ return 0;
+}
+
/*************************
* DC PSY REGISTRATION *
*************************/
@@ -701,7 +813,6 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_STEP_CHARGING_STEP,
POWER_SUPPLY_PROP_CHARGE_DONE,
POWER_SUPPLY_PROP_PARALLEL_DISABLE,
- POWER_SUPPLY_PROP_PARALLEL_PERCENT,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
};
@@ -775,9 +886,6 @@ static int smb2_batt_get_prop(struct power_supply *psy,
val->intval = get_client_vote(chg->pl_disable_votable,
USER_VOTER);
break;
- case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
- val->intval = chg->pl.slave_pct;
- break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as device is active */
val->intval = 0;
@@ -815,12 +923,6 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0);
break;
- case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
- if (val->intval < 0 || val->intval > 100)
- return -EINVAL;
- chg->pl.slave_pct = val->intval;
- rerun_election(chg->fcc_votable);
- break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
break;
@@ -848,7 +950,6 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
case POWER_SUPPLY_PROP_CAPACITY:
case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
- case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
return 1;
default:
break;
@@ -1226,12 +1327,6 @@ static int smb2_init_hw(struct smb2 *chip)
}
/* votes must be cast before configuring software control */
- vote(chg->pl_disable_votable,
- PL_INDIRECT_VOTER, true, 0);
- vote(chg->pl_disable_votable,
- CHG_STATE_VOTER, true, 0);
- vote(chg->pl_disable_votable,
- PARALLEL_PSY_VOTER, true, 0);
vote(chg->usb_suspend_votable,
DEFAULT_VOTER, chip->dt.no_battery, 0);
vote(chg->dc_suspend_votable,
@@ -1821,7 +1916,6 @@ static int smb2_probe(struct platform_device *pdev)
struct smb2 *chip;
struct smb_charger *chg;
int rc = 0;
- u8 stat;
union power_supply_propval val;
int usb_present, batt_present, batt_health, batt_charge_type;
@@ -1912,20 +2006,12 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
- rc = smblib_read(chg, SHDN_CMD_REG, &stat);
+ rc = smb2_init_usb_main_psy(chip);
if (rc < 0) {
- pr_err("Couldn't read MISC_SHDN_CMD_REG rc=%d\n", rc);
- return rc;
- }
-
- if (stat) {
- pr_err("bad charger part; faking USB insertion\n");
- chip->bad_part = true;
- power_supply_changed(chg->usb_psy);
- return 0;
+ pr_err("Couldn't initialize usb psy rc=%d\n", rc);
+ goto cleanup;
}
- chg->pl.slave_pct = 50;
rc = smb2_init_batt_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize batt psy rc=%d\n", rc);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index dbda0b6a9c9a..5faa05c1c270 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -39,7 +39,6 @@
static bool is_secure(struct smb_charger *chg, int addr)
{
-
if (addr == SHIP_MODE_REG)
return true;
/* assume everything above 0xA0 is secure */
@@ -152,36 +151,6 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
return 0;
}
-static void smblib_split_fcc(struct smb_charger *chg, int total_ua,
- int *master_ua, int *slave_ua)
-{
- int rc, jeita_cc_delta_ua, step_cc_delta_ua, effective_total_ua,
- slave_limited_ua, hw_cc_delta_ua = 0;
-
- rc = smblib_get_step_cc_delta(chg, &step_cc_delta_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc);
- step_cc_delta_ua = 0;
- } else {
- hw_cc_delta_ua = step_cc_delta_ua;
- }
-
- rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
- jeita_cc_delta_ua = 0;
- } else if (jeita_cc_delta_ua < 0) {
- /* HW will take the min between JEITA and step charge */
- hw_cc_delta_ua = min(hw_cc_delta_ua, jeita_cc_delta_ua);
- }
-
- effective_total_ua = max(0, total_ua + hw_cc_delta_ua);
- slave_limited_ua = min(effective_total_ua, chg->input_limited_fcc_ua);
- *slave_ua = (slave_limited_ua * chg->pl.slave_pct) / 100;
- *slave_ua = (*slave_ua * chg->pl.taper_pct) / 100;
- *master_ua = max(0, total_ua - *slave_ua);
-}
-
/********************
* REGISTER GETTERS *
********************/
@@ -536,10 +505,8 @@ static int smblib_notifier_call(struct notifier_block *nb,
schedule_work(&chg->bms_update_work);
}
- if (!chg->pl.psy && !strcmp(psy->desc->name, "parallel")) {
+ if (!chg->pl.psy && !strcmp(psy->desc->name, "usb-parallel"))
chg->pl.psy = psy;
- schedule_work(&chg->pl_detect_work);
- }
return NOTIFY_OK;
}
@@ -600,8 +567,6 @@ static void smblib_uusb_removal(struct smb_charger *chg)
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
- /* reset taper_end voter here */
- vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
@@ -729,84 +694,6 @@ static int smblib_fcc_max_vote_callback(struct votable *votable, void *data,
return vote(chg->fcc_votable, FCC_MAX_RESULT_VOTER, true, fcc_ua);
}
-static int smblib_fcc_vote_callback(struct votable *votable, void *data,
- int total_fcc_ua, const char *client)
-{
- struct smb_charger *chg = 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 (chg->mode == PARALLEL_MASTER
- && !get_effective_result_locked(chg->pl_disable_votable)) {
- smblib_split_fcc(chg, total_fcc_ua, &master_fcc_ua,
- &slave_fcc_ua);
-
- /*
- * parallel charger is not disabled, implying that
- * chg->pl.psy exists
- */
- pval.intval = slave_fcc_ua;
- rc = power_supply_set_property(chg->pl.psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- if (rc < 0) {
- smblib_err(chg, "Could not set parallel fcc, rc=%d\n",
- rc);
- return rc;
- }
-
- chg->pl.slave_fcc_ua = slave_fcc_ua;
- }
-
- rc = smblib_set_charge_param(chg, &chg->param.fcc, master_fcc_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set master fcc rc=%d\n", rc);
- return rc;
- }
-
- smblib_dbg(chg, 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 smblib_fv_vote_callback(struct votable *votable, void *data,
- int fv_uv, const char *client)
-{
- struct smb_charger *chg = data;
- union power_supply_propval pval = {0, };
- int rc = 0;
-
- if (fv_uv < 0) {
- smblib_dbg(chg, PR_MISC, "No Voter\n");
- return 0;
- }
-
- rc = smblib_set_charge_param(chg, &chg->param.fv, fv_uv);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set floating voltage rc=%d\n", rc);
- return rc;
- }
-
- if (chg->mode == PARALLEL_MASTER && chg->pl.psy) {
- pval.intval = fv_uv + PARALLEL_FLOAT_VOLTAGE_DELTA_UV;
- rc = power_supply_set_property(chg->pl.psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't set float on parallel rc=%d\n", rc);
- return rc;
- }
- }
-
- return 0;
-}
-
#define USBIN_25MA 25000
#define USBIN_100MA 100000
#define USBIN_150MA 150000
@@ -947,35 +834,6 @@ static int smblib_awake_vote_callback(struct votable *votable, void *data,
return 0;
}
-static int smblib_pl_disable_vote_callback(struct votable *votable, void *data,
- int pl_disable, const char *client)
-{
- struct smb_charger *chg = data;
- union power_supply_propval pval = {0, };
- int rc;
-
- if (chg->mode != PARALLEL_MASTER || !chg->pl.psy)
- return 0;
-
- chg->pl.taper_pct = 100;
- rerun_election(chg->fv_votable);
- rerun_election(chg->fcc_votable);
-
- pval.intval = pl_disable;
- rc = power_supply_set_property(chg->pl.psy,
- POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't change slave suspend state rc=%d\n", rc);
- return rc;
- }
-
- smblib_dbg(chg, PR_PARALLEL, "parallel charging %s\n",
- pl_disable ? "disabled" : "enabled");
-
- return 0;
-}
-
static int smblib_chg_disable_vote_callback(struct votable *votable, void *data,
int chg_disable, const char *client)
{
@@ -2331,6 +2189,39 @@ int smblib_set_prop_ship_mode(struct smb_charger *chg,
return rc;
}
+/***********************
+* USB MAIN PSY GETTERS *
+*************************/
+int smblib_get_prop_fcc_delta(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc, jeita_cc_delta_ua, step_cc_delta_ua, hw_cc_delta_ua = 0;
+
+ rc = smblib_get_step_cc_delta(chg, &step_cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc);
+ step_cc_delta_ua = 0;
+ } else {
+ hw_cc_delta_ua = step_cc_delta_ua;
+ }
+
+ rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
+ jeita_cc_delta_ua = 0;
+ } else if (jeita_cc_delta_ua < 0) {
+ /* HW will take the min between JEITA and step charge */
+ hw_cc_delta_ua = min(hw_cc_delta_ua, jeita_cc_delta_ua);
+ }
+
+ val->intval = hw_cc_delta_ua;
+ return 0;
+}
+
+/***********************
+* USB MAIN PSY SETTERS *
+*************************/
+
int smblib_reg_block_update(struct smb_charger *chg,
struct reg_info *entry)
{
@@ -2595,35 +2486,6 @@ unlock:
return IRQ_HANDLED;
}
-static void smblib_pl_handle_chg_state_change(struct smb_charger *chg, u8 stat)
-{
- bool pl_enabled;
-
- if (chg->mode != PARALLEL_MASTER)
- return;
-
- pl_enabled = !get_effective_result_locked(chg->pl_disable_votable);
- switch (stat) {
- case FAST_CHARGE:
- case FULLON_CHARGE:
- vote(chg->pl_disable_votable, CHG_STATE_VOTER, false, 0);
- break;
- case TAPER_CHARGE:
- if (pl_enabled) {
- cancel_delayed_work_sync(&chg->pl_taper_work);
- schedule_delayed_work(&chg->pl_taper_work, 0);
- }
- break;
- case TERMINATE_CHARGE:
- case INHIBIT_CHARGE:
- case DISABLE_CHARGE:
- vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
- break;
- default:
- break;
- }
-}
-
irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -2641,7 +2503,6 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
}
stat = stat & BATTERY_CHARGER_STATUS_MASK;
- smblib_pl_handle_chg_state_change(chg, stat);
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
}
@@ -2791,16 +2652,15 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
}
#define USB_WEAK_INPUT_UA 1400000
-#define EFFICIENCY_PCT 80
irqreturn_t smblib_handle_icl_change(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- int rc, icl_ua;
+ int rc, settled_ua;
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ua);
+ rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc);
return IRQ_HANDLED;
@@ -2809,15 +2669,10 @@ irqreturn_t smblib_handle_icl_change(int irq, void *data)
if (chg->mode != PARALLEL_MASTER)
return IRQ_HANDLED;
- chg->input_limited_fcc_ua = div64_s64(
- (s64)icl_ua * MICRO_5V * EFFICIENCY_PCT,
- (s64)get_effective_result(chg->fv_votable) * 100);
-
- if (!get_effective_result(chg->pl_disable_votable))
- rerun_election(chg->fcc_votable);
+ power_supply_changed(chg->usb_main_psy);
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
- icl_ua >= USB_WEAK_INPUT_UA, 0);
+ settled_ua >= USB_WEAK_INPUT_UA, 0);
return IRQ_HANDLED;
}
@@ -3035,12 +2890,9 @@ static void typec_source_removal(struct smb_charger *chg)
{
int rc;
- vote(chg->pl_disable_votable, TYPEC_SRC_VOTER, true, 0);
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
- /* reset taper_end voter here */
- vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
@@ -3073,7 +2925,6 @@ static void typec_source_removal(struct smb_charger *chg)
static void typec_source_insertion(struct smb_charger *chg)
{
- vote(chg->pl_disable_votable, TYPEC_SRC_VOTER, false, 0);
}
static void typec_sink_insertion(struct smb_charger *chg)
@@ -3407,58 +3258,6 @@ static void step_soc_req_work(struct work_struct *work)
step_charge_soc_update(chg, pval.intval);
}
-static void smblib_pl_detect_work(struct work_struct *work)
-{
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pl_detect_work);
-
- vote(chg->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0);
-}
-
-#define MINIMUM_PARALLEL_FCC_UA 500000
-#define PL_TAPER_WORK_DELAY_MS 100
-#define TAPER_RESIDUAL_PCT 75
-static void smblib_pl_taper_work(struct work_struct *work)
-{
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pl_taper_work.work);
- union power_supply_propval pval = {0, };
- int rc;
-
- smblib_dbg(chg, PR_PARALLEL, "starting parallel taper work\n");
- if (chg->pl.slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) {
- smblib_dbg(chg, PR_PARALLEL, "parallel taper is done\n");
- vote(chg->pl_disable_votable, TAPER_END_VOTER, true, 0);
- goto done;
- }
-
- rc = smblib_get_prop_batt_charge_type(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get batt charge type rc=%d\n", rc);
- goto done;
- }
-
- if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
- smblib_dbg(chg, PR_PARALLEL, "master is taper charging; reducing slave FCC\n");
- vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, true, 0);
- /* Reduce the taper percent by 25 percent */
- chg->pl.taper_pct = chg->pl.taper_pct
- * TAPER_RESIDUAL_PCT / 100;
- rerun_election(chg->fcc_votable);
- schedule_delayed_work(&chg->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
- */
- smblib_dbg(chg, PR_PARALLEL, "master is fast charging; waiting for next taper\n");
-
-done:
- vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, false, 0);
-}
-
static void clear_hdc_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -3563,19 +3362,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
- chg->fcc_votable = create_votable("FCC", VOTE_MIN,
- smblib_fcc_vote_callback,
- chg);
- if (IS_ERR(chg->fcc_votable)) {
- rc = PTR_ERR(chg->fcc_votable);
+ chg->fcc_votable = find_votable("FCC");
+ if (!chg->fcc_votable) {
+ rc = -EPROBE_DEFER;
return rc;
}
- chg->fv_votable = create_votable("FV", VOTE_MAX,
- smblib_fv_vote_callback,
- chg);
- if (IS_ERR(chg->fv_votable)) {
- rc = PTR_ERR(chg->fv_votable);
+ chg->fv_votable = find_votable("FV");
+ if (!chg->fv_votable) {
+ rc = -EPROBE_DEFER;
return rc;
}
@@ -3618,13 +3413,12 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
- chg->pl_disable_votable = create_votable("PL_DISABLE", VOTE_SET_ANY,
- smblib_pl_disable_vote_callback,
- chg);
- if (IS_ERR(chg->pl_disable_votable)) {
- rc = PTR_ERR(chg->pl_disable_votable);
+ chg->pl_disable_votable = find_votable("PL_DISABLE");
+ if (!chg->pl_disable_votable) {
+ rc = -EPROBE_DEFER;
return rc;
}
+ vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
chg->chg_disable_votable = create_votable("CHG_DISABLE", VOTE_SET_ANY,
smblib_chg_disable_vote_callback,
@@ -3682,10 +3476,6 @@ static void smblib_destroy_votables(struct smb_charger *chg)
destroy_votable(chg->dc_suspend_votable);
if (chg->fcc_max_votable)
destroy_votable(chg->fcc_max_votable);
- if (chg->fcc_votable)
- destroy_votable(chg->fcc_votable);
- if (chg->fv_votable)
- destroy_votable(chg->fv_votable);
if (chg->usb_icl_votable)
destroy_votable(chg->usb_icl_votable);
if (chg->dc_icl_votable)
@@ -3696,8 +3486,6 @@ static void smblib_destroy_votables(struct smb_charger *chg)
destroy_votable(chg->pd_allowed_votable);
if (chg->awake_votable)
destroy_votable(chg->awake_votable);
- if (chg->pl_disable_votable)
- destroy_votable(chg->pl_disable_votable);
if (chg->chg_disable_votable)
destroy_votable(chg->chg_disable_votable);
if (chg->pl_enable_votable_indirect)
@@ -3727,10 +3515,8 @@ int smblib_init(struct smb_charger *chg)
mutex_init(&chg->write_lock);
mutex_init(&chg->otg_overcurrent_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
- INIT_WORK(&chg->pl_detect_work, smblib_pl_detect_work);
INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work);
INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
- INIT_DELAYED_WORK(&chg->pl_taper_work, smblib_pl_taper_work);
INIT_DELAYED_WORK(&chg->step_soc_req_work, step_soc_req_work);
INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work);
chg->fake_capacity = -EINVAL;
@@ -3752,10 +3538,7 @@ int smblib_init(struct smb_charger *chg)
}
chg->bms_psy = power_supply_get_by_name("bms");
- chg->pl.psy = power_supply_get_by_name("parallel");
- if (chg->pl.psy)
- vote(chg->pl_disable_votable, PARALLEL_PSY_VOTER,
- false, 0);
+ chg->pl.psy = power_supply_get_by_name("usb-parallel");
break;
case PARALLEL_SLAVE:
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index a4121224b121..b3fce23c6508 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -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);
diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c
index 628340310331..0c2943c7f2df 100644
--- a/drivers/power/supply/qcom/smb1351-charger.c
+++ b/drivers/power/supply/qcom/smb1351-charger.c
@@ -1414,6 +1414,7 @@ static enum power_supply_property smb1351_parallel_properties[] = {
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_PARALLEL_MODE,
};
static int smb1351_parallel_set_chg_present(struct smb1351_charger *chip,
@@ -1667,6 +1668,12 @@ static int smb1351_parallel_get_property(struct power_supply *psy,
else
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_PARALLEL_MODE:
+ if (chip->parallel_charger_present)
+ val->intval = POWER_SUPPLY_PARALLEL_USBIN_USBIN;
+ else
+ val->intval = POWER_SUPPLY_PARALLEL_NONE;
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 3d814236b692..ae15fef6c3a6 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -409,11 +409,12 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_PARALLEL_MODE,
};
static int smb138x_parallel_get_prop(struct power_supply *psy,
@@ -447,7 +448,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fcc,
&val->intval);
break;
@@ -463,6 +464,9 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "smb138x";
break;
+ case POWER_SUPPLY_PROP_PARALLEL_MODE:
+ val->intval = POWER_SUPPLY_PARALLEL_MID_MID;
+ break;
default:
pr_err("parallel power supply get prop %d not supported\n",
prop);
@@ -516,7 +520,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval);
break;
case POWER_SUPPLY_PROP_BUCK_FREQ: