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

Commit 4aa2c0cf authored by Ashay Jaiswal's avatar Ashay Jaiswal Committed by Harry Yang
Browse files

qcom-charger: smb2: support for micro USB mode



Add support to disable TypeC mode of charger hardware
and enable micro USB mode. Enabling micro USB mode involves
following changes:
- hvdcp needs to be kept enabled. Create a new votable hvdcp_enable
  and vote true when uUSB connector is in use.
- Add support of extcon for sending notification to USB driver.

Change-Id: I109c07cd15052f4be15fee203f7cbaf02b6fd5cf
Signed-off-by: default avatarAshay Jaiswal <ashayj@codeaurora.org>
parent 7ade2843
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -152,6 +152,12 @@ Charger specific properties:
		be based off battery voltage. For both SOC and battery voltage,
		charger receives the signal from FG to resume charging.

- qcom,micro-usb
  Usage:      optional
  Value type: <empty>
  Definition: Boolean flag which indicates that the platform only support
		micro usb port.

=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
+133 −54
Original line number Diff line number Diff line
@@ -362,6 +362,9 @@ static int smb2_parse_dt(struct smb2 *chip)

	chip->dt.auto_recharge_soc = of_property_read_bool(node,
						"qcom,auto-recharge-soc");

	chg->micro_usb_mode = of_property_read_bool(node, "qcom,micro-usb");

	return 0;
}

@@ -429,15 +432,23 @@ static int smb2_usb_get_prop(struct power_supply *psy,
			val->intval = chg->usb_psy_desc.type;
		break;
	case POWER_SUPPLY_PROP_TYPEC_MODE:
		if (chip->bad_part)
		if (chg->micro_usb_mode)
			val->intval = POWER_SUPPLY_TYPEC_NONE;
		else if (chip->bad_part)
			val->intval = POWER_SUPPLY_TYPEC_SOURCE_DEFAULT;
		else
			rc = smblib_get_prop_typec_mode(chg, val);
		break;
	case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
		if (chg->micro_usb_mode)
			val->intval = POWER_SUPPLY_TYPEC_PR_NONE;
		else
			rc = smblib_get_prop_typec_power_role(chg, val);
		break;
	case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION:
		if (chg->micro_usb_mode)
			val->intval = 0;
		else
			rc = smblib_get_prop_typec_cc_orientation(chg, val);
		break;
	case POWER_SUPPLY_PROP_PD_ALLOWED:
@@ -1081,6 +1092,99 @@ static int smb2_config_wipower_input_power(struct smb2 *chip, int uw)
	return 0;
}

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

	/*
	 * trigger the usb-typec-change interrupt only when the CC state
	 * changes
	 */
	rc = smblib_write(chg, TYPE_C_INTRPT_ENB_REG,
			  TYPEC_CCSTATE_CHANGE_INT_EN_BIT);
	if (rc < 0) {
		dev_err(chg->dev,
			"Couldn't configure Type-C interrupts rc=%d\n", rc);
		return rc;
	}

	/* configure power role for dual-role */
	rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
				 TYPEC_POWER_ROLE_CMD_MASK, 0);
	if (rc < 0) {
		dev_err(chg->dev,
			"Couldn't configure power role for DRP rc=%d\n", rc);
		return rc;
	}

	/*
	 * disable Type-C factory mode and stay in Attached.SRC state when VCONN
	 * over-current happens
	 */
	rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
			FACTORY_MODE_DETECTION_EN_BIT | VCONN_OC_CFG_BIT, 0);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't configure Type-C rc=%d\n", rc);
		return rc;
	}

	/* increase VCONN softstart */
	rc = smblib_masked_write(chg, TYPE_C_CFG_2_REG,
			VCONN_SOFTSTART_CFG_MASK, VCONN_SOFTSTART_CFG_MASK);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't increase VCONN softstart rc=%d\n",
			rc);
		return rc;
	}

	/* disable try.SINK mode */
	rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
		return rc;
	}

	rc = smblib_validate_initial_typec_legacy_status(chg);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't validate typec legacy status rc=%d\n",
			rc);
		return rc;
	}

	return rc;
}

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

	/* configure FSM in idle state */
	rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
			TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc);
		return rc;
	}

	/* configure micro USB mode */
	rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
			TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
		return rc;
	}

	/* release FSM from idle state */
	rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
			TYPEC_DISABLE_CMD_BIT, 0);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc);
		return rc;
	}

	return rc;
}

static int smb2_init_hw(struct smb2 *chip)
{
	struct smb_charger *chg = &chip->chg;
@@ -1142,14 +1246,18 @@ static int smb2_init_hw(struct smb2 *chip)
		DCP_VOTER, true, chip->dt.usb_icl_ua);
	vote(chg->dc_icl_votable,
		DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
	vote(chg->hvdcp_disable_votable, DEFAULT_VOTER,
	vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
		chip->dt.hvdcp_disable, 0);
	vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER,
	vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
			true, 0);
	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
			true, 0);
	vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
			true, 0);
	vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
			chg->micro_usb_mode, 0);
	vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER,
			chg->micro_usb_mode, 0);

	/* Configure charge enable for software control; active high */
	rc = smblib_masked_write(chg, CHGR_CFG2_REG,
@@ -1167,12 +1275,10 @@ static int smb2_init_hw(struct smb2 *chip)
		return rc;
	}

	/*
	 * trigger the usb-typec-change interrupt only when the CC state
	 * changes
	 */
	rc = smblib_write(chg, TYPE_C_INTRPT_ENB_REG,
			  TYPEC_CCSTATE_CHANGE_INT_EN_BIT);
	if (chg->micro_usb_mode)
		rc = smb2_disable_typec(chg);
	else
		rc = smb2_configure_typec(chg);
	if (rc < 0) {
		dev_err(chg->dev,
			"Couldn't configure Type-C interrupts rc=%d\n", rc);
@@ -1197,42 +1303,6 @@ static int smb2_init_hw(struct smb2 *chip)
		return rc;
	}

	/* configure power role for dual-role */
	rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
				 TYPEC_POWER_ROLE_CMD_MASK, 0);
	if (rc < 0) {
		dev_err(chg->dev,
			"Couldn't configure power role for DRP rc=%d\n", rc);
		return rc;
	}

	/*
	 * disable Type-C factory mode and stay in Attached.SRC state when VCONN
	 * over-current happens
	 */
	rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
			FACTORY_MODE_DETECTION_EN_BIT | VCONN_OC_CFG_BIT, 0);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't configure Type-C rc=%d\n", rc);
		return rc;
	}

	/* increase VCONN softstart */
	rc = smblib_masked_write(chg, TYPE_C_CFG_2_REG,
			VCONN_SOFTSTART_CFG_MASK, VCONN_SOFTSTART_CFG_MASK);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't increase VCONN softstart rc=%d\n",
			rc);
		return rc;
	}

	/* disable try.SINK mode */
	rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
		return rc;
	}

	rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
			QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
	if (rc < 0) {
@@ -1327,13 +1397,6 @@ static int smb2_init_hw(struct smb2 *chip)
		return rc;
	}

	rc = smblib_validate_initial_typec_legacy_status(chg);
	if (rc < 0) {
		dev_err(chg->dev, "Couldn't validate typec legacy status rc=%d\n",
			rc);
		return rc;
	}

	if (chip->dt.auto_recharge_soc) {
		rc = smblib_masked_write(chg, FG_UPDATE_CFG_2_SEL_REG,
				SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT |
@@ -1812,6 +1875,22 @@ static int smb2_probe(struct platform_device *pdev)
		goto cleanup;
	}

	/* extcon registration */
	chg->extcon = devm_extcon_dev_allocate(chg->dev, smblib_extcon_cable);
	if (IS_ERR(chg->extcon)) {
		rc = PTR_ERR(chg->extcon);
		dev_err(chg->dev, "failed to allocate extcon device rc=%d\n",
				rc);
		goto cleanup;
	}

	rc = devm_extcon_dev_register(chg->dev, chg->extcon);
	if (rc < 0) {
		dev_err(chg->dev, "failed to register extcon device rc=%d\n",
				rc);
		goto cleanup;
	}

	rc = smb2_init_dc_psy(chip);
	if (rc < 0) {
		pr_err("Couldn't initialize dc psy rc=%d\n", rc);
+110 −19
Original line number Diff line number Diff line
@@ -489,10 +489,13 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
	 * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether
	 * apsd rerun was tried earlier
	 */
	if (get_client_vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER)) {
		vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER, false, 0);
	if (get_client_vote(chg->hvdcp_disable_votable_indirect,
						PD_INACTIVE_VOTER)) {
		vote(chg->hvdcp_disable_votable_indirect,
				PD_INACTIVE_VOTER, false, 0);
		/* ensure hvdcp is enabled */
		if (!get_effective_result(chg->hvdcp_disable_votable)) {
		if (!get_effective_result(
				chg->hvdcp_disable_votable_indirect)) {
			apsd_result = smblib_get_apsd_result(chg);
			if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
				/* rerun APSD */
@@ -590,6 +593,40 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param,
	return 0;
}

static void smblib_uusb_removal(struct smb_charger *chg)
{
	int rc;

	/* 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);

	/* reset AUTH_IRQ_EN_CFG_BIT */
	rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
				 AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
	if (rc < 0)
		smblib_err(chg, "Couldn't enable QC auth setting rc=%d\n", rc);

	/* reconfigure allowed voltage for HVDCP */
	rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
			  USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
	if (rc < 0)
		smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
			rc);

	chg->voltage_min_uv = MICRO_5V;
	chg->voltage_max_uv = MICRO_5V;

	/* clear USB ICL vote for USB_PSY_VOTER */
	rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
	if (rc < 0)
		smblib_err(chg, "Couldn't un-vote for USB ICL rc=%d\n", rc);
}

/*********************
 * VOTABLE CALLBACKS *
 *********************/
@@ -901,9 +938,9 @@ static int smblib_pl_enable_indirect_vote_callback(struct votable *votable,
	return 0;
}

static int smblib_hvdcp_disable_vote_callback(struct votable *votable,
static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
			void *data,
			int hvdcp_disable, const char *client)
			int hvdcp_enable, const char *client)
{
	struct smb_charger *chg = data;
	int rc;
@@ -915,7 +952,7 @@ static int smblib_hvdcp_disable_vote_callback(struct votable *votable,
	 * This ensures only qc 2.0 detection runs but no vbus
	 * negotiation happens.
	 */
	if (hvdcp_disable)
	if (!hvdcp_enable)
		val = HVDCP_EN_BIT;

	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
@@ -925,13 +962,24 @@ static int smblib_hvdcp_disable_vote_callback(struct votable *votable,
				 val);
	if (rc < 0) {
		smblib_err(chg, "Couldn't %s hvdcp rc=%d\n",
			hvdcp_disable ? "disable" : "enable", rc);
			hvdcp_enable ? "enable" : "disable", rc);
		return rc;
	}

	return 0;
}

static int smblib_hvdcp_disable_indirect_vote_callback(struct votable *votable,
			void *data, int hvdcp_disable, const char *client)
{
	struct smb_charger *chg = data;

	vote(chg->hvdcp_enable_votable, HVDCP_INDIRECT_VOTER,
			!hvdcp_disable, 0);

	return 0;
}

static int smblib_apsd_disable_vote_callback(struct votable *votable,
			void *data,
			int apsd_disable, const char *client)
@@ -2468,6 +2516,12 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
				smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
					rc);
		}

		if (chg->micro_usb_mode) {
			smblib_update_usb_type(chg);
			extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
			smblib_uusb_removal(chg);
		}
	}

	power_supply_changed(chg->usb_psy);
@@ -2652,6 +2706,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
	switch (apsd_result->bit) {
	case SDP_CHARGER_BIT:
	case CDP_CHARGER_BIT:
		if (chg->micro_usb_mode)
			extcon_set_cable_state_(chg->extcon, EXTCON_USB,
					true);
	case OCP_CHARGER_BIT:
	case FLOAT_CHARGER_BIT:
		/* if not DCP then no hvdcp timeout happens. Enable pd here */
@@ -2782,10 +2839,10 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
	vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);

	/* reset votes from vbus_cc_short */
	vote(chg->hvdcp_disable_votable, VBUS_CC_SHORT_VOTER, true, 0);

	vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER, true, 0);

	vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
			true, 0);
	vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
			true, 0);
	/*
	 * cable could be removed during hard reset, remove its vote to
	 * disable apsd
@@ -2827,7 +2884,8 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
		}
	}

	vote(chg->hvdcp_disable_votable, VBUS_CC_SHORT_VOTER, vbus_cc_short, 0);
	vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
			vbus_cc_short, 0);
	vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER,
			vbus_cc_short, 0);
}
@@ -2870,6 +2928,26 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
		   smblib_typec_mode_name[pval.intval]);
}

irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
{
	int rc;
	u8 stat;

	rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
		return IRQ_HANDLED;
	}
	smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n",
		stat, !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));

	extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST,
			!!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));
	power_supply_changed(chg->usb_psy);

	return IRQ_HANDLED;
}

irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
{
	struct smb_irq_data *irq_data = data;
@@ -2878,6 +2956,9 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
	u8 stat4, stat5;
	bool debounce_done, sink_attached, legacy_cable;

	if (chg->micro_usb_mode)
		return smblib_handle_usb_typec_change_for_uusb(chg);

	/* WA - not when PD hard_reset WIP on cc2 in sink mode */
	if (chg->cc2_sink_detach_flag == CC2_SINK_STD)
		return IRQ_HANDLED;
@@ -3253,12 +3334,22 @@ static int smblib_create_votables(struct smb_charger *chg)
		return rc;
	}

	chg->hvdcp_disable_votable = create_votable("HVDCP_DISABLE",
	chg->hvdcp_disable_votable_indirect = create_votable(
				"HVDCP_DISABLE_INDIRECT",
				VOTE_SET_ANY,
				smblib_hvdcp_disable_indirect_vote_callback,
				chg);
	if (IS_ERR(chg->hvdcp_disable_votable_indirect)) {
		rc = PTR_ERR(chg->hvdcp_disable_votable_indirect);
		return rc;
	}

	chg->hvdcp_enable_votable = create_votable("HVDCP_ENABLE",
					VOTE_SET_ANY,
					smblib_hvdcp_disable_vote_callback,
					smblib_hvdcp_enable_vote_callback,
					chg);
	if (IS_ERR(chg->hvdcp_disable_votable)) {
		rc = PTR_ERR(chg->hvdcp_disable_votable);
	if (IS_ERR(chg->hvdcp_enable_votable)) {
		rc = PTR_ERR(chg->hvdcp_enable_votable);
		return rc;
	}

+15 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/irqreturn.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/extcon.h>
#include "storm-watch.h"

enum print_reason {
@@ -48,6 +49,8 @@ enum print_reason {
#define LEGACY_CABLE_VOTER		"LEGACY_CABLE_VOTER"
#define PD_INACTIVE_VOTER		"PD_INACTIVE_VOTER"
#define BOOST_BACK_VOTER		"BOOST_BACK_VOTER"
#define HVDCP_INDIRECT_VOTER		"HVDCP_INDIRECT_VOTER"
#define MICRO_USB_VOTER			"MICRO_USB_VOTER"

enum smb_mode {
	PARALLEL_MASTER = 0,
@@ -68,6 +71,12 @@ enum {
	TYPEC_CC2_REMOVAL_WA_BIT	= BIT(2),
};

static const unsigned int smblib_extcon_cable[] = {
	EXTCON_USB,
	EXTCON_USB_HOST,
	EXTCON_NONE,
};

struct smb_regulator {
	struct regulator_dev	*rdev;
	struct regulator_desc	rdesc;
@@ -181,7 +190,8 @@ struct smb_charger {
	struct votable		*pl_disable_votable;
	struct votable		*chg_disable_votable;
	struct votable		*pl_enable_votable_indirect;
	struct votable		*hvdcp_disable_votable;
	struct votable		*hvdcp_disable_votable_indirect;
	struct votable		*hvdcp_enable_votable;
	struct votable		*apsd_disable_votable;

	/* work */
@@ -213,12 +223,16 @@ struct smb_charger {
	bool			step_chg_enabled;
	bool			is_hdc;
	bool			chg_done;
	bool			micro_usb_mode;
	int			input_limited_fcc_ua;

	/* workaround flag */
	u32			wa_flags;
	enum cc2_sink_type	cc2_sink_detach_flag;
	int			boost_current_ua;

	/* extcon for VBUS / ID notification to USB for uUSB */
	struct extcon_dev	*extcon;
};

int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
+2 −2
Original line number Diff line number Diff line
@@ -494,9 +494,9 @@ enum {

#define TYPE_C_STATUS_3_REG			(USBIN_BASE + 0x0D)
#define ENABLE_BANDGAP_BIT			BIT(7)
#define U_USB_GROUND_NOVBUS_BIT			BIT(6)
#define U_USB_GND_NOVBUS_BIT			BIT(6)
#define U_USB_FLOAT_NOVBUS_BIT			BIT(5)
#define U_USB_GROUND_BIT			BIT(4)
#define U_USB_GND_BIT				BIT(4)
#define U_USB_FMB1_BIT				BIT(3)
#define U_USB_FLOAT1_BIT			BIT(2)
#define U_USB_FMB2_BIT				BIT(1)