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

Commit ced3f0d1 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: smb5-lib: Fix charger reverse boost on soft JEITA"

parents e99b33e4 79579ba5
Loading
Loading
Loading
Loading
+19 −4
Original line number Diff line number Diff line
@@ -120,13 +120,28 @@ Profile data node optional properties:
			The threshold values in range should be in ascending
			and shouldn't overlap. It support 8 ranges at max.
- qcom,jeita-soft-thresholds: A tuple entry to specify ADC code for battery's soft JEITA
				threshold.
				<SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>.
			threshold. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>.
- qcom,jeita-hard-thresholds: A tuple entry to specify ADC code for battery's hard JEITA
				threshold.
				<HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>.
			threshold. <HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>.
- qcom,jeita-soft-hys-thresholds: A tuple entry to specify ADC code for battery's soft JEITA
			threshold with hysteresis adjustment.
			<SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>.
			These "hysteresis" values should be specified if
			"qcom,jeita-soft-thresholds" are specified. Without which SW JEITA
			compensation won't function properly.
- qcom,jeita-soft-fcc-ua: A tuple entry to specify the values of Fast
			charging current (in uA) that needs to be applied during
			soft JEITA conditions (cool/warm).
			Element 0 - FCC value for soft cool.
			Element 1 - FCC value for soft warm.
- qcom,jeita-soft-fv-uv: A tuple entry to specify the values of Float
			voltage (in uV) that needs to be applied during soft
			JEITA conditions (cool/warm).
			Element 0 - FV value for soft cool.
			Element 1 - FV value for soft warm.
- qcom,batt-age-level:  Battery age level. This is used only when multiple
			profile loading is supported.

Profile data node required subnodes:
- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes
			temperature to fcc lookup. The units for this lookup
+61 −3
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
 */

#define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__
@@ -40,6 +40,7 @@
#define PL_FCC_LOW_VOTER		"PL_FCC_LOW_VOTER"
#define ICL_LIMIT_VOTER			"ICL_LIMIT_VOTER"
#define FCC_STEPPER_VOTER		"FCC_STEPPER_VOTER"
#define FCC_VOTER			"FCC_VOTER"

struct pl_data {
	int			pl_mode;
@@ -57,6 +58,7 @@ struct pl_data {
	struct votable		*hvdcp_hw_inov_dis_votable;
	struct votable		*usb_icl_votable;
	struct votable		*pl_enable_votable_indirect;
	struct votable		*cp_ilim_votable;
	struct delayed_work	status_change_work;
	struct work_struct	pl_disable_forever_work;
	struct work_struct	pl_taper_work;
@@ -68,6 +70,7 @@ struct pl_data {
	struct power_supply	*batt_psy;
	struct power_supply	*usb_psy;
	struct power_supply	*dc_psy;
	struct power_supply	*cp_master_psy;
	int			charge_type;
	int			total_settled_ua;
	int			pl_settled_ua;
@@ -85,6 +88,7 @@ struct pl_data {
	struct wakeup_source	*pl_ws;
	struct notifier_block	nb;
	bool			pl_disable;
	bool			cp_disabled;
	int			taper_entry_fv;
	int			main_fcc_max;
	/* debugfs directory */
@@ -476,6 +480,46 @@ static void get_fcc_split(struct pl_data *chip, int total_ua,
	}
}

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

	if (!chip->cp_master_psy)
		chip->cp_master_psy =
			power_supply_get_by_name("charge_pump_master");
	if (!chip->cp_master_psy)
		goto out;

	rc = power_supply_get_property(chip->cp_master_psy,
			POWER_SUPPLY_PROP_CP_SWITCHER_EN, &pval);
	if (rc < 0) {
		pr_err("Couldn't get switcher enable status, rc=%d\n", rc);
		goto out;
	}

	if (!pval.intval) {
		/*
		 * To honor main charger upper FCC limit, on CP switcher
		 * disable, skip fcc slewing as it will cause delay in limiting
		 * the charge current flowing through main charger.
		 */
		if (!chip->cp_disabled) {
			chip->fcc_stepper_enable = false;
			pl_dbg(chip, PR_PARALLEL,
				"Disabling FCC slewing on CP Switcher disable\n");
		}
		chip->cp_disabled = true;
	} else {
		chip->cp_disabled = false;
		pl_dbg(chip, PR_PARALLEL,
			"CP Switcher is enabled, don't limit main fcc\n");
		return;
	}
out:
	*total_fcc = min(*total_fcc, chip->main_fcc_max);
}

static void get_fcc_stepper_params(struct pl_data *chip, int main_fcc_ua,
			int parallel_fcc_ua)
{
@@ -618,6 +662,9 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data,
	if (!chip->main_psy)
		return 0;

	if (!chip->cp_ilim_votable)
		chip->cp_ilim_votable = find_votable("CP_ILIM");

	if (chip->pl_mode != POWER_SUPPLY_PL_NONE) {
		get_fcc_split(chip, total_fcc_ua, &master_fcc_ua,
				&slave_fcc_ua);
@@ -814,6 +861,10 @@ static void fcc_stepper_work(struct work_struct *work)
	chip->main_fcc_ua = main_fcc;
	chip->slave_fcc_ua = parallel_fcc;

	if (chip->cp_ilim_votable)
		vote(chip->cp_ilim_votable, FCC_VOTER, true,
					chip->main_fcc_ua / 2);

	if (reschedule_ms) {
		schedule_delayed_work(&chip->fcc_stepper_work,
				msecs_to_jiffies(reschedule_ms));
@@ -929,6 +980,9 @@ static int usb_icl_vote_callback(struct votable *votable, void *data,

	vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0);

	if (chip->cp_ilim_votable)
		vote(chip->cp_ilim_votable, ICL_CHANGE_VOTER, true, icl_ua);

	return 0;
}

@@ -1161,8 +1215,7 @@ static int pl_disable_vote_callback(struct votable *votable,
			(slave_fcc_ua * 100) / total_fcc_ua);
	} else {
		if (chip->main_fcc_max)
			total_fcc_ua = min(total_fcc_ua,
						chip->main_fcc_max);
			get_main_fcc_config(chip, &total_fcc_ua);

		if (!chip->fcc_stepper_enable) {
			if (IS_USBIN(chip->pl_mode))
@@ -1188,6 +1241,10 @@ static int pl_disable_vote_callback(struct votable *votable,
				return rc;
			}

			if (chip->cp_ilim_votable)
				vote(chip->cp_ilim_votable, FCC_VOTER, true,
						total_fcc_ua / 2);

			/* reset parallel FCC */
			chip->slave_fcc_ua = 0;
			chip->total_settled_ua = 0;
@@ -1700,6 +1757,7 @@ int qcom_batt_init(int smb_version)
	}

	chip->pl_disable = true;
	chip->cp_disabled = true;
	chip->qcom_batt_class.name = "qcom-battery",
	chip->qcom_batt_class.owner = THIS_MODULE,
	chip->qcom_batt_class.class_groups = batt_class_groups;
+9 −20
Original line number Diff line number Diff line
@@ -1301,8 +1301,7 @@ static int smb5_batt_get_prop(struct power_supply *psy,
				QNOVO_VOTER);
		break;
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		rc = smblib_get_prop_from_bms(chg,
				POWER_SUPPLY_PROP_CURRENT_NOW, val);
		rc = smblib_get_batt_current_now(chg, val);
		break;
	case POWER_SUPPLY_PROP_CURRENT_QNOVO:
		val->intval = get_client_vote_locked(chg->fcc_votable,
@@ -1333,10 +1332,7 @@ static int smb5_batt_get_prop(struct power_supply *psy,
		val->intval = 0;
		break;
	case POWER_SUPPLY_PROP_DIE_HEALTH:
		if (chg->die_health == -EINVAL)
			val->intval = smblib_get_prop_die_health(chg);
		else
			val->intval = chg->die_health;
		rc = smblib_get_die_health(chg, val);
		break;
	case POWER_SUPPLY_PROP_DP_DM:
		val->intval = chg->pulse_cnt;
@@ -1415,13 +1411,6 @@ static int smb5_batt_set_prop(struct power_supply *psy,
	case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
		chg->step_chg_enabled = !!val->intval;
		break;
	case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
		if (chg->sw_jeita_enabled != (!!val->intval)) {
			rc = smblib_disable_hw_jeita(chg, !!val->intval);
			if (rc == 0)
				chg->sw_jeita_enabled = !!val->intval;
		}
		break;
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
		chg->batt_profile_fcc_ua = val->intval;
		vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval);
@@ -1472,6 +1461,9 @@ static int smb5_batt_set_prop(struct power_supply *psy,
			vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
					false, 0);
		break;
	case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE:
		chg->fcc_stepper_enable = val->intval;
		break;
	default:
		rc = -EINVAL;
	}
@@ -1492,7 +1484,6 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy,
	case POWER_SUPPLY_PROP_RERUN_AICL:
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
	case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
	case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
	case POWER_SUPPLY_PROP_DIE_HEALTH:
		return 1;
	default:
@@ -2152,13 +2143,11 @@ static int smb5_init_hw(struct smb5 *chip)
		}
	}

	if (chg->sw_jeita_enabled) {
	rc = smblib_disable_hw_jeita(chg, true);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't set hw jeita rc=%d\n", rc);
		return rc;
	}
	}

	rc = smblib_masked_write(chg, DCDC_ENG_SDCDC_CFG5_REG,
			ENG_SDCDC_BAT_HPWR_MASK, BOOST_MODE_THRESH_3P6_V);
+79 −20
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@
#define CP_VOTER		"CP_VOTER"
#define USER_VOTER		"USER_VOTER"
#define ILIM_VOTER		"ILIM_VOTER"
#define TAPER_END_VOTER		"TAPER_END_VOTER"
#define FCC_VOTER		"FCC_VOTER"
#define ICL_VOTER		"ICL_VOTER"
#define WIRELESS_VOTER		"WIRELESS_VOTER"
@@ -122,6 +123,7 @@ struct smb1390 {
	struct votable		*disable_votable;
	struct votable		*ilim_votable;
	struct votable		*fcc_votable;
	struct votable		*fv_votable;
	struct votable		*cp_awake_votable;

	/* power supplies */
@@ -135,6 +137,8 @@ struct smb1390 {
	bool			taper_work_running;
	struct smb1390_iio	iio;
	int			irq_status;
	int			taper_entry_fv;
	bool			switcher_disabled;
};

struct smb_irq {
@@ -204,6 +208,19 @@ static bool is_psy_voter_available(struct smb1390 *chip)
		}
	}

	if (!chip->fv_votable) {
		chip->fv_votable = find_votable("FV");
		if (!chip->fv_votable) {
			pr_debug("Couldn't find FV votable\n");
			return false;
		}
	}

	if (!chip->disable_votable) {
		pr_debug("Couldn't find CP DISABLE votable\n");
		return false;
	}

	return true;
}

@@ -247,7 +264,8 @@ static int smb1390_get_cp_en_status(struct smb1390 *chip, int id, bool *enable)
static irqreturn_t default_irq_handler(int irq, void *data)
{
	struct smb1390 *chip = data;
	int i;
	int i, rc;
	bool enable;

	for (i = 0; i < NUM_IRQS; ++i) {
		if (irq == chip->irqs[i]) {
@@ -256,8 +274,18 @@ static irqreturn_t default_irq_handler(int irq, void *data)
		}
	}

	rc = smb1390_get_cp_en_status(chip, SWITCHER_EN, &enable);
	if (!rc) {
		if (chip->switcher_disabled == enable) {
			chip->switcher_disabled = !chip->switcher_disabled;
			if (chip->fcc_votable)
				rerun_election(chip->fcc_votable);
		}
	}

	if (chip->cp_master_psy)
		power_supply_changed(chip->cp_master_psy);

	return IRQ_HANDLED;
}

@@ -435,18 +463,20 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data,
		return -EINVAL;
	}

	rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG,
		CFG_ILIM_MASK,
		DIV_ROUND_CLOSEST(max(ilim_uA, 500000) - 500000, 100000));
	if (rc < 0) {
		pr_err("Failed to write ILIM Register, rc=%d\n", rc);
		return rc;
	}

	/* ILIM less than 1A is not accurate; disable charging */
	if (ilim_uA < 1000000) {
		pr_debug("ILIM %duA is too low to allow charging\n", ilim_uA);
		vote(chip->disable_votable, ILIM_VOTER, true, 0);
	} else {
		pr_debug("setting ILIM to %duA\n", ilim_uA);
		rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG,
				CFG_ILIM_MASK,
				DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000));
		if (rc < 0)
			pr_err("Failed to write ILIM Register, rc=%d\n", rc);
		if (rc >= 0)
		pr_debug("ILIM set to %duA\n", ilim_uA);
		vote(chip->disable_votable, ILIM_VOTER, false, 0);
	}

@@ -544,9 +574,13 @@ static void smb1390_status_change_work(struct work_struct *work)
								pval.intval);
		}

		/* input current is always half the charge current */
		vote(chip->ilim_votable, FCC_VOTER, true,
				get_effective_result(chip->fcc_votable) / 2);
		/*
		 * Remove SMB1390 Taper condition disable vote if float voltage
		 * increased in comparison to voltage at which it entered taper.
		 */
		if (chip->taper_entry_fv <
				get_effective_result(chip->fv_votable))
			vote(chip->disable_votable, TAPER_END_VOTER, false, 0);

		/*
		 * all votes that would result in disabling the charge pump have
@@ -574,6 +608,7 @@ static void smb1390_status_change_work(struct work_struct *work)
		}
	} else {
		vote(chip->disable_votable, SRC_VOTER, true, 0);
		vote(chip->disable_votable, TAPER_END_VOTER, false, 0);
		vote(chip->fcc_votable, CP_VOTER, false, 0);
	}

@@ -591,11 +626,8 @@ static void smb1390_taper_work(struct work_struct *work)
	if (!is_psy_voter_available(chip))
		goto out;

	do {
		fcc_uA = get_effective_result(chip->fcc_votable) - 100000;
		pr_debug("taper work reducing FCC to %duA\n", fcc_uA);
		vote(chip->fcc_votable, CP_VOTER, true, fcc_uA);

	chip->taper_entry_fv = get_effective_result(chip->fv_votable);
	while (true) {
		rc = power_supply_get_property(chip->batt_psy,
					POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
		if (rc < 0) {
@@ -603,12 +635,30 @@ static void smb1390_taper_work(struct work_struct *work)
			goto out;
		}

		msleep(500);
	} while (fcc_uA >= 2000000
		 && pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER);
		if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
			fcc_uA = get_effective_result(chip->fcc_votable)
								- 100000;
			pr_debug("taper work reducing FCC to %duA\n", fcc_uA);
			vote(chip->fcc_votable, CP_VOTER, true, fcc_uA);

			if (fcc_uA < 2000000) {
				vote(chip->disable_votable, TAPER_END_VOTER,
								true, 0);
				goto out;
			}
		} else if (get_effective_result(chip->fv_votable) >
						chip->taper_entry_fv) {
			pr_debug("Float voltage increased. Exiting taper\n");
			goto out;
		} else {
			pr_debug("In fast charging. Wait for next taper\n");
		}

		msleep(500);
	}
out:
	pr_debug("taper work exit\n");
	vote(chip->fcc_votable, CP_VOTER, false, 0);
	chip->taper_work_running = false;
}

@@ -810,6 +860,14 @@ static int smb1390_create_votables(struct smb1390 *chip)
	 */
	vote(chip->disable_votable, USER_VOTER, true, 0);

	/*
	 * In case SMB1390 probe happens after FCC value has been configured,
	 * update ilim vote to reflect FCC / 2 value.
	 */
	if (chip->fcc_votable)
		vote(chip->ilim_votable, FCC_VOTER, true,
			get_effective_result(chip->fcc_votable) / 2);

	return 0;
}

@@ -926,6 +984,7 @@ static int smb1390_probe(struct platform_device *pdev)
	chip->dev = &pdev->dev;
	spin_lock_init(&chip->status_change_lock);
	mutex_init(&chip->die_chan_lock);
	chip->switcher_disabled = true;

	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
	if (!chip->regmap) {
+256 −85
Original line number Diff line number Diff line
@@ -908,7 +908,7 @@ static int smblib_notifier_call(struct notifier_block *nb,
			schedule_work(&chg->bms_update_work);
	}

	if (!chg->jeita_configured)
	if (chg->jeita_configured == JEITA_CFG_NONE)
		schedule_work(&chg->jeita_update_work);

	if (chg->sec_pl_present && !chg->pl.psy
@@ -1873,6 +1873,19 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
	return 0;
}

int smblib_get_batt_current_now(struct smb_charger *chg,
					union power_supply_propval *val)
{
	int rc;

	rc = smblib_get_prop_from_bms(chg,
			POWER_SUPPLY_PROP_CURRENT_NOW, val);
	if (!rc)
		val->intval *= (-1);

	return rc;
}

/***********************
 * BATTERY PSY SETTERS *
 ***********************/
@@ -2263,6 +2276,7 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable)
				rc);
		return rc;
	}

	return 0;
}

@@ -2343,19 +2357,28 @@ static int smblib_update_thermal_readings(struct smb_charger *chg)
	}

	if (chg->sec_chg_selected == POWER_SUPPLY_CHARGER_SEC_CP) {
		rc = smblib_read_iio_channel(chg, chg->iio.smb_temp_chan,
					DIV_FACTOR_DECIDEGC, &chg->smb_temp);
		if (rc < 0) {
			smblib_err(chg, "Couldn't read SMB TEMP channel, rc=%d\n",
		if (!chg->cp_psy)
			chg->cp_psy =
				power_supply_get_by_name("charge_pump_master");
		if (chg->cp_psy) {
			rc = power_supply_get_property(chg->cp_psy,
				POWER_SUPPLY_PROP_CP_DIE_TEMP, &pval);
			if (rc < 0) {
				smblib_err(chg, "Couldn't get smb1390 charger temp, rc=%d\n",
					rc);
				return rc;
			}
			chg->smb_temp = pval.intval;
		} else {
			smblib_dbg(chg, PR_MISC, "Coudln't find cp_psy\n");
			chg->smb_temp = -ENODATA;
		}
	} else if (chg->pl.psy && chg->sec_chg_selected ==
					POWER_SUPPLY_CHARGER_SEC_PL) {
		rc = power_supply_get_property(chg->pl.psy,
				POWER_SUPPLY_PROP_CHARGER_TEMP, &pval);
		if (rc < 0) {
			smblib_err(chg, "Couldn't get smb charger temp, rc=%d\n",
			smblib_err(chg, "Couldn't get smb1355 charger temp, rc=%d\n",
					rc);
			return rc;
		}
@@ -3262,6 +3285,17 @@ int smblib_get_prop_die_health(struct smb_charger *chg)
	return POWER_SUPPLY_HEALTH_COOL;
}

int smblib_get_die_health(struct smb_charger *chg,
			union power_supply_propval *val)
{
	if (chg->die_health == -EINVAL)
		val->intval = smblib_get_prop_die_health(chg);
	else
		val->intval = chg->die_health;

	return 0;
}

int smblib_get_prop_connector_health(struct smb_charger *chg)
{
	int rc;
@@ -3673,49 +3707,153 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
	return rc;
}

static int smblib_recover_from_soft_jeita(struct smb_charger *chg)
#define JEITA_SOFT			0
#define JEITA_HARD			1
static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds,
								int type)
{
	u8 stat1, stat7;
	int rc;
	u16 temp, base;

	rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat1);
	base = CHGR_JEITA_THRESHOLD_BASE_REG(type);

	temp = thresholds[1] & 0xFFFF;
	temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
	rc = smblib_batch_write(chg, base, (u8 *)&temp, 2);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
				rc);
		smblib_err(chg,
			"Couldn't configure Jeita %s hot threshold rc=%d\n",
			(type == JEITA_SOFT) ? "Soft" : "Hard", rc);
		return rc;
	}

	rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat7);
	temp = thresholds[0] & 0xFFFF;
	temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
	rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
				rc);
		smblib_err(chg,
			"Couldn't configure Jeita %s cold threshold rc=%d\n",
			(type == JEITA_SOFT) ? "Soft" : "Hard", rc);
		return rc;
	}

	if ((chg->jeita_status && !(stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK) &&
		((stat1 & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE))) {
		/*
		 * We are moving from JEITA soft -> Normal and charging
		 * is terminated
		 */
		rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, 0);
		if (rc < 0) {
			smblib_err(chg, "Couldn't disable charging rc=%d\n",
						rc);
	smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n",
				(type == JEITA_SOFT) ? "Soft" : "Hard");

	return 0;
}

static int smblib_charge_inhibit_en(struct smb_charger *chg, bool enable)
{
	int rc;

	rc = smblib_masked_write(chg, CHGR_CFG2_REG,
					CHARGER_INHIBIT_BIT,
					enable ? CHARGER_INHIBIT_BIT : 0);
	return rc;
}
		rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG,
						CHARGING_ENABLE_CMD_BIT);

static int smblib_soft_jeita_arb_wa(struct smb_charger *chg)
{
	union power_supply_propval pval;
	int rc = 0;
	bool soft_jeita;

	rc = smblib_get_prop_batt_health(chg, &pval);
	if (rc < 0) {
			smblib_err(chg, "Couldn't enable charging rc=%d\n",
						rc);
		smblib_err(chg, "Couldn't get battery health rc=%d\n", rc);
		return rc;
	}
	}

	chg->jeita_status = stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK;
	/* Do nothing on entering hard JEITA condition */
	if (pval.intval == POWER_SUPPLY_HEALTH_COLD ||
		pval.intval == POWER_SUPPLY_HEALTH_HOT)
		return 0;

	if (chg->jeita_soft_fcc[0] < 0 || chg->jeita_soft_fcc[1] < 0 ||
		chg->jeita_soft_fv[0] < 0 || chg->jeita_soft_fv[1] < 0)
		return 0;

	soft_jeita = (pval.intval == POWER_SUPPLY_HEALTH_COOL) ||
			(pval.intval == POWER_SUPPLY_HEALTH_WARM);

	/* Do nothing on entering soft JEITA from hard JEITA */
	if (chg->jeita_arb_flag && soft_jeita)
		return 0;

	/* Do nothing, initial to health condition */
	if (!chg->jeita_arb_flag && !soft_jeita)
		return 0;

	if (!chg->cp_disable_votable)
		chg->cp_disable_votable = find_votable("CP_DISABLE");

	/* Entering soft JEITA from normal state */
	if (!chg->jeita_arb_flag && soft_jeita) {
		vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0);
		/* Disable parallel charging */
		if (chg->pl_disable_votable)
			vote(chg->pl_disable_votable, JEITA_ARB_VOTER, true, 0);
		if (chg->cp_disable_votable)
			vote(chg->cp_disable_votable, JEITA_ARB_VOTER, true, 0);

		rc = smblib_charge_inhibit_en(chg, true);
		if (rc < 0)
			smblib_err(chg, "Couldn't enable charge inhibit rc=%d\n",
					rc);

		rc = smblib_update_jeita(chg, chg->jeita_soft_hys_thlds,
					JEITA_SOFT);
		if (rc < 0)
			smblib_err(chg,
				"Couldn't configure Jeita soft threshold rc=%d\n",
				rc);

		if (pval.intval == POWER_SUPPLY_HEALTH_COOL) {
			vote(chg->fcc_votable, JEITA_ARB_VOTER, true,
						chg->jeita_soft_fcc[0]);
			vote(chg->fv_votable, JEITA_ARB_VOTER, true,
						chg->jeita_soft_fv[0]);
		} else {
			vote(chg->fcc_votable, JEITA_ARB_VOTER, true,
						chg->jeita_soft_fcc[1]);
			vote(chg->fv_votable, JEITA_ARB_VOTER, true,
						chg->jeita_soft_fv[1]);
		}

		vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0);
		chg->jeita_arb_flag = true;
	} else if (chg->jeita_arb_flag && !soft_jeita) {
		/* Exit to health state from soft JEITA */

		vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0);

		rc = smblib_charge_inhibit_en(chg, false);
		if (rc < 0)
			smblib_err(chg, "Couldn't disable charge inhibit rc=%d\n",
					rc);

		rc = smblib_update_jeita(chg, chg->jeita_soft_thlds,
							JEITA_SOFT);
		if (rc < 0)
			smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n",
				rc);

		vote(chg->fcc_votable, JEITA_ARB_VOTER, false, 0);
		vote(chg->fv_votable, JEITA_ARB_VOTER, false, 0);
		if (chg->pl_disable_votable)
			vote(chg->pl_disable_votable, JEITA_ARB_VOTER, false,
				0);
		if (chg->cp_disable_votable)
			vote(chg->cp_disable_votable, JEITA_ARB_VOTER, false,
				0);
		vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0);
		chg->jeita_arb_flag = false;
	}

	smblib_dbg(chg, PR_MISC, "JEITA ARB status %d, soft JEITA status %d\n",
			chg->jeita_arb_flag, soft_jeita);
	return rc;
}

/************************
@@ -3873,15 +4011,18 @@ irqreturn_t batt_temp_changed_irq_handler(int irq, void *data)
	struct smb_charger *chg = irq_data->parent_data;
	int rc;

	rc = smblib_recover_from_soft_jeita(chg);
	smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);

	if (chg->jeita_configured != JEITA_CFG_COMPLETE)
		return IRQ_HANDLED;

	rc = smblib_soft_jeita_arb_wa(chg);
	if (rc < 0) {
		smblib_err(chg, "Couldn't recover chg from soft jeita rc=%d\n",
		smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n",
				rc);
		return IRQ_HANDLED;
	}

	rerun_election(chg->fcc_votable);
	power_supply_changed(chg->batt_psy);
	return IRQ_HANDLED;
}

@@ -4959,7 +5100,7 @@ irqreturn_t wdog_bark_irq_handler(int irq, void *data)
	if (rc < 0)
		smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc);

	if (chg->step_chg_enabled || chg->sw_jeita_enabled)
	if (chg->step_chg_enabled)
		power_supply_changed(chg->batt_psy);

	return IRQ_HANDLED;
@@ -5173,42 +5314,6 @@ static void smblib_thermal_regulation_work(struct work_struct *work)
					rc);
}

#define JEITA_SOFT			0
#define JEITA_HARD			1
static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds,
								int type)
{
	int rc;
	u16 temp, base;

	base = CHGR_JEITA_THRESHOLD_BASE_REG(type);

	temp = thresholds[1] & 0xFFFF;
	temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
	rc = smblib_batch_write(chg, base, (u8 *)&temp, 2);
	if (rc < 0) {
		smblib_err(chg,
			"Couldn't configure Jeita %s hot threshold rc=%d\n",
			(type == JEITA_SOFT) ? "Soft" : "Hard", rc);
		return rc;
	}

	temp = thresholds[0] & 0xFFFF;
	temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
	rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2);
	if (rc < 0) {
		smblib_err(chg,
			"Couldn't configure Jeita %s cold threshold rc=%d\n",
			(type == JEITA_SOFT) ? "Soft" : "Hard", rc);
		return rc;
	}

	smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n",
				(type == JEITA_SOFT) ? "Soft" : "Hard");

	return 0;
}

static void jeita_update_work(struct work_struct *work)
{
	struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -5216,8 +5321,8 @@ static void jeita_update_work(struct work_struct *work)
	struct device_node *node = chg->dev->of_node;
	struct device_node *batt_node, *pnode;
	union power_supply_propval val;
	int rc;
	u32 jeita_thresholds[2];
	int rc, tmp[2], max_fcc_ma, max_fv_uv;
	u32 jeita_hard_thresholds[2];

	batt_node = of_find_node_by_name(node, "qcom,battery-data");
	if (!batt_node) {
@@ -5250,9 +5355,10 @@ static void jeita_update_work(struct work_struct *work)
	}

	rc = of_property_read_u32_array(pnode, "qcom,jeita-hard-thresholds",
				jeita_thresholds, 2);
				jeita_hard_thresholds, 2);
	if (!rc) {
		rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_HARD);
		rc = smblib_update_jeita(chg, jeita_hard_thresholds,
					JEITA_HARD);
		if (rc < 0) {
			smblib_err(chg, "Couldn't configure Hard Jeita rc=%d\n",
					rc);
@@ -5261,18 +5367,83 @@ static void jeita_update_work(struct work_struct *work)
	}

	rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-thresholds",
				jeita_thresholds, 2);
				chg->jeita_soft_thlds, 2);
	if (!rc) {
		rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_SOFT);
		rc = smblib_update_jeita(chg, chg->jeita_soft_thlds,
					JEITA_SOFT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't configure Soft Jeita rc=%d\n",
					rc);
			goto out;
		}

		rc = of_property_read_u32_array(pnode,
					"qcom,jeita-soft-hys-thresholds",
					chg->jeita_soft_hys_thlds, 2);
		if (rc < 0) {
			smblib_err(chg, "Couldn't get Soft Jeita hysteresis thresholds rc=%d\n",
					rc);
			goto out;
		}
	}

	chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL;
	chg->jeita_soft_fv[0] = chg->jeita_soft_fv[1] = -EINVAL;
	max_fcc_ma = max_fv_uv = -EINVAL;

	of_property_read_u32(pnode, "qcom,fastchg-current-ma", &max_fcc_ma);
	of_property_read_u32(pnode, "qcom,max-voltage-uv", &max_fv_uv);

	if (max_fcc_ma <= 0 || max_fv_uv <= 0) {
		smblib_err(chg, "Incorrect fastchg-current-ma or max-voltage-uv\n");
		goto out;
	}

	rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fcc-ua",
					tmp, 2);
	if (rc < 0) {
		smblib_err(chg, "Couldn't get fcc values for soft JEITA rc=%d\n",
				rc);
		goto out;
	}

	max_fcc_ma *= 1000;
	if (tmp[0] > max_fcc_ma || tmp[1] > max_fcc_ma) {
		smblib_err(chg, "Incorrect FCC value [%d %d] max: %d\n", tmp[0],
			tmp[1], max_fcc_ma);
		goto out;
	}
	chg->jeita_soft_fcc[0] = tmp[0];
	chg->jeita_soft_fcc[1] = tmp[1];

	rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fv-uv", tmp,
					2);
	if (rc < 0) {
		smblib_err(chg, "Couldn't get fv values for soft JEITA rc=%d\n",
				rc);
		goto out;
	}

	if (tmp[0] > max_fv_uv || tmp[1] > max_fv_uv) {
		smblib_err(chg, "Incorrect FV value [%d %d] max: %d\n", tmp[0],
			tmp[1], max_fv_uv);
		goto out;
	}
	chg->jeita_soft_fv[0] = tmp[0];
	chg->jeita_soft_fv[1] = tmp[1];

	rc = smblib_soft_jeita_arb_wa(chg);
	if (rc < 0) {
		smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n",
				rc);
		goto out;
	}

	chg->jeita_configured = JEITA_CFG_COMPLETE;
	return;

out:
	chg->jeita_configured = true;
	chg->jeita_configured = JEITA_CFG_FAILURE;
}

static void smblib_lpd_ra_open_work(struct work_struct *work)
@@ -5530,7 +5701,7 @@ int smblib_init(struct smb_charger *chg)
		}

		rc = qcom_step_chg_init(chg->dev, chg->step_chg_enabled,
						chg->sw_jeita_enabled);
						chg->sw_jeita_enabled, false);
		if (rc < 0) {
			smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
				rc);
Loading