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

Commit b5a4cf7b authored by Ashay Jaiswal's avatar Ashay Jaiswal
Browse files

power: qcom-charger: Add support for software INOV



Hardware autonomous INOV does adapter's output voltage
manipulation only based on the input limited state of main
charger. This works well with MID-MID parallel configuration
but with USBIN-USBIN parallel configuration parallel charger's
input limited state also plays role for INOV manipulations.
Thus disable hardware based autonomous INOV and add support
for software based INOV for USBIN-USBIN configuration.

Note that if authentication is disabled, the hw assumes QC3.0 and
if the autonomous bit is enabled, it issues increment/decrement pulses.
This happens even when QC3.0 has not been actually authenticated.

Change-Id: I397acb558c9ba3b6fc7d7b974d64459f278697fd
Signed-off-by: default avatarAshay Jaiswal <ashayj@codeaurora.org>
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent 58ca30db
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ struct pl_data {
	struct votable		*fv_votable;
	struct votable		*pl_disable_votable;
	struct votable		*pl_awake_votable;
	struct votable		*hvdcp_hw_inov_dis_votable;
	struct work_struct	status_change_work;
	struct work_struct	pl_disable_forever_work;
	struct delayed_work	pl_taper_work;
@@ -420,6 +421,10 @@ static void pl_disable_forever_work(struct work_struct *work)

	/* Disable Parallel charger forever */
	vote(chip->pl_disable_votable, PL_HW_ABSENT_VOTER, true, 0);

	/* Re-enable autonomous mode */
	if (chip->hvdcp_hw_inov_dis_votable)
		vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, false, 0);
}

static int pl_disable_vote_callback(struct votable *votable,
@@ -563,6 +568,21 @@ static bool is_parallel_available(struct pl_data *chip)
	 * pl_psy is present and valid.
	 */
	chip->pl_mode = pval.intval;

	/* Disable autonomous votage increments for USBIN-USBIN */
	if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
		|| (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) {
		if (!chip->hvdcp_hw_inov_dis_votable)
			chip->hvdcp_hw_inov_dis_votable =
					find_votable("HVDCP_HW_INOV_DIS");
		if (chip->hvdcp_hw_inov_dis_votable)
			/* Read current pulse count */
			vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER,
					true, 0);
		else
			return false;
	}

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

	return true;
+16 −0
Original line number Diff line number Diff line
@@ -848,6 +848,8 @@ static enum power_supply_property smb2_batt_props[] = {
	POWER_SUPPLY_PROP_PARALLEL_DISABLE,
	POWER_SUPPLY_PROP_SET_SHIP_MODE,
	POWER_SUPPLY_PROP_DIE_HEALTH,
	POWER_SUPPLY_PROP_RERUN_AICL,
	POWER_SUPPLY_PROP_DP_DM,
};

static int smb2_batt_get_prop(struct power_supply *psy,
@@ -933,6 +935,12 @@ static int smb2_batt_get_prop(struct power_supply *psy,
	case POWER_SUPPLY_PROP_DIE_HEALTH:
		rc = smblib_get_prop_die_health(chg, val);
		break;
	case POWER_SUPPLY_PROP_DP_DM:
		val->intval = chg->pulse_cnt;
		break;
	case POWER_SUPPLY_PROP_RERUN_AICL:
		val->intval = 0;
		break;
	default:
		pr_err("batt power supply prop %d not supported\n", psp);
		return -EINVAL;
@@ -986,6 +994,12 @@ static int smb2_batt_set_prop(struct power_supply *psy,
			break;
		rc = smblib_set_prop_ship_mode(chg, val);
		break;
	case POWER_SUPPLY_PROP_RERUN_AICL:
		rc = smblib_rerun_aicl(chg);
		break;
	case POWER_SUPPLY_PROP_DP_DM:
		rc = smblib_dp_dm(chg, val->intval);
		break;
	default:
		rc = -EINVAL;
	}
@@ -1001,6 +1015,8 @@ 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_DP_DM:
	case POWER_SUPPLY_PROP_RERUN_AICL:
		return 1;
	default:
		break;
+183 −5
Original line number Diff line number Diff line
@@ -58,6 +58,12 @@ int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
	return rc;
}

int smblib_multibyte_read(struct smb_charger *chg, u16 addr, u8 *val,
				int count)
{
	return regmap_bulk_read(chg->regmap, addr, val, count);
}

int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val)
{
	int rc = 0;
@@ -652,6 +658,8 @@ static void smblib_uusb_removal(struct smb_charger *chg)

	chg->voltage_min_uv = MICRO_5V;
	chg->voltage_max_uv = MICRO_5V;
	chg->usb_icl_delta_ua = 0;
	chg->pulse_cnt = 0;

	/* clear USB ICL vote for USB_PSY_VOTER */
	rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
@@ -741,6 +749,40 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
	return 0;
}

static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
{
	int rc;
	u8 val[2];

	switch (chg->smb_version) {
	case PMI8998_SUBTYPE:
		rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, val);
		if (rc) {
			pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n",
					rc);
			return rc;
		}
		*count = val[0] & QC_PULSE_COUNT_MASK;
		break;
	case PM660_SUBTYPE:
		rc = smblib_multibyte_read(chg,
				QC_PULSE_COUNT_STATUS_1_REG, val, 2);
		if (rc) {
			pr_err("failed to read QC_PULSE_COUNT_STATUS_1_REG rc=%d\n",
					rc);
			return rc;
		}
		*count = (val[1] << 8) | val[0];
		break;
	default:
		smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n",
				chg->smb_version);
		return -EINVAL;
	}

	return 0;
}

/*********************
 * VOTABLE CALLBACKS *
 *********************/
@@ -975,8 +1017,10 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
{
	struct smb_charger *chg = data;
	int rc;
	u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT
		| HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_EN_BIT;
	u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;

	/* vote to enable/disable HW autonomous INOV */
	vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0);

	/*
	 * Disable the autonomous bit and auth bit for disabling hvdcp.
@@ -987,9 +1031,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
		val = HVDCP_EN_BIT;

	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
				 HVDCP_EN_BIT
				 | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT
				 | HVDCP_AUTH_ALG_EN_CFG_BIT,
				 HVDCP_EN_BIT | HVDCP_AUTH_ALG_EN_CFG_BIT,
				 val);
	if (rc < 0) {
		smblib_err(chg, "Couldn't %s hvdcp rc=%d\n",
@@ -1058,6 +1100,37 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
	return 0;
}

static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable,
				void *data, int disable, const char *client)
{
	struct smb_charger *chg = data;
	int rc;

	if (disable) {
		/*
		 * the pulse count register get zeroed when autonomous mode is
		 * disabled. Track that in variables before disabling
		 */
		rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt);
		if (rc < 0) {
			pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n",
					rc);
			return rc;
		}
	}

	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
			HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
			disable ? 0 : HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT);
	if (rc < 0) {
		smblib_err(chg, "Couldn't %s hvdcp rc=%d\n",
				disable ? "disable" : "enable", rc);
		return rc;
	}

	return rc;
}

/*******************
 * VCONN REGULATOR *
 * *****************/
@@ -1645,6 +1718,98 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
	return 0;
}

int smblib_rerun_aicl(struct smb_charger *chg)
{
	int rc = 0;
	u8 val;

	/*
	 * Use restart_AICL instead of trigger_AICL as it runs the
	 * complete AICL instead of starting from the last settled value.
	 *
	 * 8998 only supports trigger_AICL return error for 8998
	 */
	switch (chg->smb_version) {
	case PMI8998_SUBTYPE:
		smblib_dbg(chg, PR_PARALLEL, "AICL rerun not supported\n");
		return -EINVAL;
	case PM660_SUBTYPE:
		val = RESTART_AICL_BIT;
		break;
	default:
		smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n",
				chg->smb_version);
		return -EINVAL;
	}
	rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val);
	if (rc < 0)
		smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
				rc);

	return rc;
}

static int smblib_dp_pulse(struct smb_charger *chg)
{
	int rc;

	/* QC 3.0 increment */
	rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_INCREMENT_BIT,
			SINGLE_INCREMENT_BIT);
	if (rc < 0)
		smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
				rc);

	return rc;
}

static int smblib_dm_pulse(struct smb_charger *chg)
{
	int rc;

	/* QC 3.0 decrement */
	rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_DECREMENT_BIT,
			SINGLE_DECREMENT_BIT);
	if (rc < 0)
		smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
				rc);

	return rc;
}

int smblib_dp_dm(struct smb_charger *chg, int val)
{
	int target_icl_ua, rc = 0;

	switch (val) {
	case POWER_SUPPLY_DP_DM_DP_PULSE:
		rc = smblib_dp_pulse(chg);
		if (!rc)
			chg->pulse_cnt++;
		smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n",
				rc, chg->pulse_cnt);
		break;
	case POWER_SUPPLY_DP_DM_DM_PULSE:
		rc = smblib_dm_pulse(chg);
		if (!rc && chg->pulse_cnt)
			chg->pulse_cnt--;
		smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n",
				rc, chg->pulse_cnt);
		break;
	case POWER_SUPPLY_DP_DM_ICL_DOWN:
		chg->usb_icl_delta_ua -= 100000;
		target_icl_ua = get_effective_result(chg->usb_icl_votable);
		vote(chg->usb_icl_votable, SW_QC3_VOTER, true,
				target_icl_ua + chg->usb_icl_delta_ua);
		break;
	case POWER_SUPPLY_DP_DM_ICL_UP:
	default:
		break;
	}

	return rc;
}

/*******************
 * DC PSY GETTERS *
 *******************/
@@ -3279,6 +3444,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)

	chg->vconn_attempts = 0;
	chg->otg_attempts = 0;
	chg->pulse_cnt = 0;
	chg->usb_icl_delta_ua = 0;

	chg->usb_ever_removed = true;

@@ -3917,6 +4084,15 @@ static int smblib_create_votables(struct smb_charger *chg)
		return rc;
	}

	chg->hvdcp_hw_inov_dis_votable = create_votable("HVDCP_HW_INOV_DIS",
					VOTE_SET_ANY,
					smblib_hvdcp_hw_inov_dis_vote_callback,
					chg);
	if (IS_ERR(chg->hvdcp_hw_inov_dis_votable)) {
		rc = PTR_ERR(chg->hvdcp_hw_inov_dis_votable);
		return rc;
	}

	return rc;
}

@@ -3940,6 +4116,8 @@ static void smblib_destroy_votables(struct smb_charger *chg)
		destroy_votable(chg->pl_enable_votable_indirect);
	if (chg->apsd_disable_votable)
		destroy_votable(chg->apsd_disable_votable);
	if (chg->hvdcp_hw_inov_dis_votable)
		destroy_votable(chg->hvdcp_hw_inov_dis_votable);
}

static void smblib_iio_deinit(struct smb_charger *chg)
+6 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ enum print_reason {
#define PD_SUSPEND_SUPPORTED_VOTER	"PD_SUSPEND_SUPPORTED_VOTER"
#define PL_DELAY_HVDCP_VOTER		"PL_DELAY_HVDCP_VOTER"
#define CTM_VOTER			"CTM_VOTER"
#define SW_QC3_VOTER			"SW_QC3_VOTER"

#define VCONN_MAX_ATTEMPTS	3
#define OTG_MAX_ATTEMPTS	3
@@ -267,6 +268,7 @@ struct smb_charger {
	struct votable		*hvdcp_disable_votable_indirect;
	struct votable		*hvdcp_enable_votable;
	struct votable		*apsd_disable_votable;
	struct votable		*hvdcp_hw_inov_dis_votable;

	/* work */
	struct work_struct	bms_update_work;
@@ -315,6 +317,8 @@ struct smb_charger {
	/* qnovo */
	int			qnovo_fcc_ua;
	int			qnovo_fv_uv;
	int			usb_icl_delta_ua;
	int			pulse_cnt;
};

int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -472,6 +476,8 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg,
			       union power_supply_propval *val);
int smblib_icl_override(struct smb_charger *chg, bool override);
int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua);
int smblib_dp_dm(struct smb_charger *chg, int val);
int smblib_rerun_aicl(struct smb_charger *chg);

int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
+3 −0
Original line number Diff line number Diff line
@@ -535,6 +535,8 @@ enum {
#define USBIN_LT_3P6V_RT_STS_BIT		BIT(1)
#define USBIN_COLLAPSE_RT_STS_BIT		BIT(0)

#define QC_PULSE_COUNT_STATUS_1_REG		(USBIN_BASE + 0x30)

#define USBIN_CMD_IL_REG			(USBIN_BASE + 0x40)
#define BAT_2_SYS_FET_DIS_BIT			BIT(1)
#define USBIN_SUSPEND_BIT			BIT(0)
@@ -544,6 +546,7 @@ enum {
#define APSD_RERUN_BIT				BIT(0)

#define CMD_HVDCP_2_REG				(USBIN_BASE + 0x43)
#define RESTART_AICL_BIT			BIT(7)
#define TRIGGER_AICL_BIT			BIT(6)
#define FORCE_12V_BIT				BIT(5)
#define FORCE_9V_BIT				BIT(4)