diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-02-01 19:23:35 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-02-01 19:23:35 -0800 |
commit | 5e7d38562557ebcfea71a97bcccafd87d12d364b (patch) | |
tree | e5e7a851c1b0f032d5de3db9ebe58ac8a6c60d6c | |
parent | ffaa506460d58cf093409d0cb790d2e2795114de (diff) | |
parent | f0cf35f5921410fed20e7d2d5f89ed3326430a08 (diff) |
Merge "power: qcom-charger: separate parallel code"
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 5 | ||||
-rw-r--r-- | drivers/power/supply/qcom/Makefile | 6 | ||||
-rw-r--r-- | drivers/power/supply/qcom/battery.c | 730 | ||||
-rw-r--r-- | drivers/power/supply/qcom/qpnp-fg-gen3.c | 86 | ||||
-rw-r--r-- | drivers/power/supply/qcom/qpnp-smb2.c | 146 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb-lib.c | 315 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb-lib.h | 12 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb1351-charger.c | 13 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb135x-charger.c | 6 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb138x-charger.c | 12 | ||||
-rw-r--r-- | include/linux/power_supply.h | 16 |
11 files changed, 987 insertions, 360 deletions
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index e99bf1ee5ad2..7e6eca7f7a7e 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -46,7 +46,7 @@ static ssize_t power_supply_show_property(struct device *dev, static char *type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP", "USB_CDP", "USB_ACA", "USB_HVDCP", "USB_HVDCP_3", "USB_PD", - "Wireless", "BMS", "USB_Parallel", "Wipower", + "Wireless", "BMS", "Parallel", "Main", "Wipower", "TYPEC", "TYPEC_UFP", "TYPEC_DFP" }; static char *status_text[] = { @@ -280,6 +280,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(set_ship_mode), POWER_SUPPLY_ATTR(soc_reporting_ready), POWER_SUPPLY_ATTR(debug_battery), + POWER_SUPPLY_ATTR(fcc_delta), + POWER_SUPPLY_ATTR(icl_reduction), + POWER_SUPPLY_ATTR(parallel_mode), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ 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-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index a441ff310e9f..7ffa583ca01d 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -981,6 +981,38 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, /* Other functions HERE */ +static void fg_notify_charger(struct fg_chip *chip) +{ + union power_supply_propval prop = {0, }; + int rc; + + if (!chip->batt_psy) + return; + + if (!chip->profile_available) + return; + + prop.intval = chip->bp.float_volt_uv; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); + if (rc < 0) { + pr_err("Error in setting voltage_max property on batt_psy, rc=%d\n", + rc); + return; + } + + prop.intval = chip->bp.fastchg_curr_ma * 1000; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop); + if (rc < 0) { + pr_err("Error in setting constant_charge_current_max property on batt_psy, rc=%d\n", + rc); + return; + } + + fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n"); +} + static int fg_awake_cb(struct votable *votable, void *data, int awake, const char *client) { @@ -995,14 +1027,18 @@ static int fg_awake_cb(struct votable *votable, void *data, int awake, return 0; } -static bool is_charger_available(struct fg_chip *chip) +static bool batt_psy_initialized(struct fg_chip *chip) { - if (!chip->batt_psy) - chip->batt_psy = power_supply_get_by_name("battery"); + if (chip->batt_psy) + return true; + chip->batt_psy = power_supply_get_by_name("battery"); if (!chip->batt_psy) return false; + /* batt_psy is initialized, set the fcc and fv */ + fg_notify_charger(chip); + return true; } @@ -1359,7 +1395,7 @@ static int fg_charge_full_update(struct fg_chip *chip) if (!chip->dt.hold_soc_while_full) return 0; - if (!is_charger_available(chip)) + if (!batt_psy_initialized(chip)) return 0; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, @@ -1723,7 +1759,7 @@ static void status_change_work(struct work_struct *work) union power_supply_propval prop = {0, }; int rc; - if (!is_charger_available(chip)) { + if (!batt_psy_initialized(chip)) { fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); goto out; } @@ -2040,37 +2076,6 @@ out: return rc; } -static void fg_notify_charger(struct fg_chip *chip) -{ - union power_supply_propval prop = {0, }; - int rc; - - if (!is_charger_available(chip)) { - pr_warn("Charger not available yet?\n"); - return; - } - - prop.intval = chip->bp.float_volt_uv; - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); - if (rc < 0) { - pr_err("Error in setting voltage_max property on batt_psy, rc=%d\n", - rc); - return; - } - - prop.intval = chip->bp.fastchg_curr_ma * 1000; - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop); - if (rc < 0) { - pr_err("Error in setting constant_charge_current_max property on batt_psy, rc=%d\n", - rc); - return; - } - - fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n"); -} - static void profile_load_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, @@ -2136,6 +2141,7 @@ done: rc); } + batt_psy_initialized(chip); fg_notify_charger(chip); chip->profile_loaded = true; chip->soc_reporting_ready = true; @@ -2296,7 +2302,7 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val) return -ENODATA; } - if (!is_charger_available(chip)) { + if (!batt_psy_initialized(chip)) { fg_dbg(chip, FG_TTF, "charger is not available\n"); return -ENODATA; } @@ -3004,7 +3010,7 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in configuring ESR filter rc:%d\n", rc); - if (!is_charger_available(chip)) { + if (!batt_psy_initialized(chip)) { chip->last_batt_temp = batt_temp; return IRQ_HANDLED; } @@ -3062,7 +3068,7 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc); - if (is_charger_available(chip)) + if (batt_psy_initialized(chip)) power_supply_changed(chip->batt_psy); return IRQ_HANDLED; @@ -3073,7 +3079,7 @@ static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data) struct fg_chip *chip = data; fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); - if (is_charger_available(chip)) + if (batt_psy_initialized(chip)) power_supply_changed(chip->batt_psy); return IRQ_HANDLED; 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 2f612884fe0e..022ed49ce6eb 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) { @@ -2359,6 +2217,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) { @@ -2623,35 +2514,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; @@ -2669,7 +2531,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; } @@ -2819,16 +2680,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; @@ -2837,15 +2697,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; } @@ -3063,12 +2918,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); @@ -3101,7 +2953,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) @@ -3435,58 +3286,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, @@ -3591,19 +3390,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; } @@ -3646,13 +3441,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, @@ -3710,10 +3504,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) @@ -3724,8 +3514,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) @@ -3755,10 +3543,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; @@ -3780,10 +3566,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 e9d8c0e08447..0c2943c7f2df 100644 --- a/drivers/power/supply/qcom/smb1351-charger.c +++ b/drivers/power/supply/qcom/smb1351-charger.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-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 @@ -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; } @@ -3158,8 +3165,8 @@ static int smb1351_parallel_charger_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); - chip->parallel_psy_d.name = "usb-parallel"; - chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_USB_PARALLEL; + chip->parallel_psy_d.name = "parallel"; + chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_PARALLEL; chip->parallel_psy_d.get_property = smb1351_parallel_get_property; chip->parallel_psy_d.set_property = smb1351_parallel_set_property; chip->parallel_psy_d.properties = smb1351_parallel_properties; diff --git a/drivers/power/supply/qcom/smb135x-charger.c b/drivers/power/supply/qcom/smb135x-charger.c index 65d4ae56ff83..08af01544590 100644 --- a/drivers/power/supply/qcom/smb135x-charger.c +++ b/drivers/power/supply/qcom/smb135x-charger.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -4378,8 +4378,8 @@ static int smb135x_parallel_charger_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); - chip->parallel_psy_d.name = "usb-parallel"; - chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_USB_PARALLEL; + chip->parallel_psy_d.name = "parallel"; + chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_PARALLEL; chip->parallel_psy_d.get_property = smb135x_parallel_get_property; chip->parallel_psy_d.set_property = smb135x_parallel_set_property; chip->parallel_psy_d.properties = smb135x_parallel_properties; diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index c836e780fc86..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: @@ -540,7 +544,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy, static const struct power_supply_desc parallel_psy_desc = { .name = "parallel", - .type = POWER_SUPPLY_TYPE_USB_PARALLEL, + .type = POWER_SUPPLY_TYPE_PARALLEL, .properties = smb138x_parallel_props, .num_properties = ARRAY_SIZE(smb138x_parallel_props), .get_property = smb138x_parallel_get_prop, diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index de35fe9441fe..6a495ea6c523 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -104,6 +104,12 @@ enum { POWER_SUPPLY_DP_DM_ICL_UP = 12, }; +enum { + POWER_SUPPLY_PARALLEL_NONE, + POWER_SUPPLY_PARALLEL_USBIN_USBIN, + POWER_SUPPLY_PARALLEL_MID_MID, +}; + enum power_supply_property { /* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS = 0, @@ -229,6 +235,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, + POWER_SUPPLY_PROP_FCC_DELTA, + POWER_SUPPLY_PROP_ICL_REDUCTION, + POWER_SUPPLY_PROP_PARALLEL_MODE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ @@ -252,9 +261,10 @@ enum power_supply_type { POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery */ POWER_SUPPLY_TYPE_WIRELESS, /* Accessory Charger Adapters */ POWER_SUPPLY_TYPE_BMS, /* Battery Monitor System */ - POWER_SUPPLY_TYPE_USB_PARALLEL, /* USB Parallel Path */ - POWER_SUPPLY_TYPE_WIPOWER, /* Wipower */ - POWER_SUPPLY_TYPE_TYPEC, /*Type-C */ + POWER_SUPPLY_TYPE_PARALLEL, /* Parallel Path */ + POWER_SUPPLY_TYPE_MAIN, /* Main Path */ + POWER_SUPPLY_TYPE_WIPOWER, /* Wipower */ + POWER_SUPPLY_TYPE_TYPEC, /* Type-C */ POWER_SUPPLY_TYPE_UFP, /* Type-C UFP */ POWER_SUPPLY_TYPE_DFP, /* TYpe-C DFP */ }; |