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

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

Merge "power: smb5: Add support for typeC role reversal"

parents 8dab3157 6a7cf05e
Loading
Loading
Loading
Loading
+211 −0
Original line number Original line Diff line number Diff line
@@ -1678,6 +1678,209 @@ static int smb5_init_batt_psy(struct smb5 *chip)
	return rc;
	return rc;
}
}


/********************************
 * DUAL-ROLE CLASS REGISTRATION *
 ********************************/
static enum dual_role_property smb5_dr_properties[] = {
	DUAL_ROLE_PROP_SUPPORTED_MODES,
	DUAL_ROLE_PROP_MODE,
	DUAL_ROLE_PROP_PR,
	DUAL_ROLE_PROP_DR,
};

static int smb5_dr_get_property(struct dual_role_phy_instance *dual_role,
			enum dual_role_property prop, unsigned int *val)
{
	struct smb_charger *chg = dual_role_get_drvdata(dual_role);
	union power_supply_propval pval = {0, };
	/* Initializing pr, dr and mode to value 2 corresponding to NONE case */
	int mode = 2, pr = 2, dr = 2, rc = 0;

	if (!chg)
		return -ENODEV;

	rc = smblib_get_prop_usb_present(chg, &pval);
	if (rc < 0) {
		pr_err("Couldn't get usb present status, rc=%d\n", rc);
		return rc;
	}

	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) {
		if (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
			mode = DUAL_ROLE_PROP_MODE_NONE;
			pr = DUAL_ROLE_PROP_PR_NONE;
			dr = DUAL_ROLE_PROP_DR_NONE;
		} else if (chg->typec_mode <
				POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
			mode = DUAL_ROLE_PROP_MODE_DFP;
			pr = DUAL_ROLE_PROP_PR_SRC;
			dr = DUAL_ROLE_PROP_DR_HOST;
		} else if (chg->typec_mode >=
				POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
			mode = DUAL_ROLE_PROP_MODE_UFP;
			pr = DUAL_ROLE_PROP_PR_SNK;
			dr = DUAL_ROLE_PROP_DR_DEVICE;
		}
	} else if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
		pr = DUAL_ROLE_PROP_PR_NONE;

		if (chg->otg_present) {
			mode = DUAL_ROLE_PROP_MODE_DFP;
			dr = DUAL_ROLE_PROP_DR_HOST;
		} else if (pval.intval) {
			mode = DUAL_ROLE_PROP_MODE_UFP;
			dr = DUAL_ROLE_PROP_DR_DEVICE;
		} else {
			mode = DUAL_ROLE_PROP_MODE_NONE;
			dr = DUAL_ROLE_PROP_DR_NONE;
		}
	}

	switch (prop) {
	case DUAL_ROLE_PROP_MODE:
		*val = mode;
		break;
	case DUAL_ROLE_PROP_PR:
		*val = pr;
		break;
	case DUAL_ROLE_PROP_DR:
		*val = dr;
		break;
	default:
		pr_err("dual role class get property %d not supported\n", prop);
		return -EINVAL;
	}

	return 0;
}

static int smb5_dr_set_property(struct dual_role_phy_instance *dual_role,
			enum dual_role_property prop, const unsigned int *val)
{
	struct smb_charger *chg = dual_role_get_drvdata(dual_role);
	int rc = 0;

	if (!chg)
		return -ENODEV;

	mutex_lock(&chg->dr_lock);
	switch (prop) {
	case DUAL_ROLE_PROP_MODE:
		if (chg->pr_swap_in_progress) {
			pr_debug("Already in mode transition. Skipping request\n");
			mutex_unlock(&chg->dr_lock);
			return 0;
		}

		switch (*val) {
		case DUAL_ROLE_PROP_MODE_UFP:
			if (chg->typec_mode >=
					POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
				chg->dr_mode = DUAL_ROLE_PROP_MODE_UFP;
			} else {
				chg->pr_swap_in_progress = true;
				rc = smblib_force_dr_mode(chg,
						DUAL_ROLE_PROP_MODE_UFP);
				if (rc < 0) {
					chg->pr_swap_in_progress = false;
					pr_err("Failed to force UFP mode, rc=%d\n",
						rc);
				}
			}
			break;
		case DUAL_ROLE_PROP_MODE_DFP:
			if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
				&& chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
				chg->dr_mode = DUAL_ROLE_PROP_MODE_DFP;
			} else {
				chg->pr_swap_in_progress = true;
				rc = smblib_force_dr_mode(chg,
						DUAL_ROLE_PROP_MODE_DFP);
				if (rc < 0) {
					chg->pr_swap_in_progress = false;
					pr_err("Failed to force DFP mode, rc=%d\n",
						rc);
				}
			}
			break;
		default:
			pr_err("Invalid role (not DFP/UFP): %d\n", *val);
			rc = -EINVAL;
		}

		/*
		 * Schedule delayed work to check if the device latched to
		 * the requested mode.
		 */
		if (chg->pr_swap_in_progress && !rc) {
			cancel_delayed_work_sync(&chg->role_reversal_check);
			vote(chg->awake_votable, DR_SWAP_VOTER, true, 0);
			schedule_delayed_work(&chg->role_reversal_check,
				msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS));
		}
		break;
	default:
		pr_err("dual role class set property %d not supported\n", prop);
		rc = -EINVAL;
	}

	mutex_unlock(&chg->dr_lock);
	return rc;
}

static int smb5_dr_prop_writeable(struct dual_role_phy_instance *dual_role,
			enum dual_role_property prop)
{
	struct smb_charger *chg = dual_role_get_drvdata(dual_role);

	if (!chg)
		return -ENODEV;

	/* uUSB connector does not support role switch */
	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
		return 0;

	switch (prop) {
	case DUAL_ROLE_PROP_MODE:
		return 1;
	default:
		break;
	}

	return 0;
}

static const struct dual_role_phy_desc dr_desc = {
	.name = "otg_default",
	.supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP,
	.properties = smb5_dr_properties,
	.num_properties = ARRAY_SIZE(smb5_dr_properties),
	.get_property = smb5_dr_get_property,
	.set_property = smb5_dr_set_property,
	.property_is_writeable = smb5_dr_prop_writeable,
};

static int smb5_init_dual_role_class(struct smb5 *chip)
{
	struct smb_charger *chg = &chip->chg;
	int rc = 0;

	/* Register dual role class for only non-PD TypeC and uUSB designs */
	if (!chg->pd_not_supported)
		return rc;

	mutex_init(&chg->dr_lock);
	chg->dual_role = devm_dual_role_instance_register(chg->dev, &dr_desc);
	if (IS_ERR(chg->dual_role)) {
		pr_err("Couldn't register dual role class\n");
		rc = PTR_ERR(chg->dual_role);
	} else {
		chg->dual_role->drv_data = chg;
	}

	return rc;
}

/******************************
/******************************
 * VBUS REGULATOR REGISTRATION *
 * VBUS REGULATOR REGISTRATION *
 ******************************/
 ******************************/
@@ -3224,6 +3427,14 @@ static int smb5_probe(struct platform_device *pdev)
		goto cleanup;
		goto cleanup;
	}
	}


	/* Register android dual-role class */
	rc = smb5_init_dual_role_class(chip);
	if (rc < 0) {
		pr_err("Couldn't initialize dual role class, rc=%d\n",
			rc);
		goto cleanup;
	}

	rc = smb5_determine_initial_status(chip);
	rc = smb5_determine_initial_status(chip);
	if (rc < 0) {
	if (rc < 0) {
		pr_err("Couldn't determine initial status rc=%d\n",
		pr_err("Couldn't determine initial status rc=%d\n",
+112 −0
Original line number Original line Diff line number Diff line
@@ -4718,6 +4718,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
		smblib_micro_usb_plugin(chg, vbus_rising);
		smblib_micro_usb_plugin(chg, vbus_rising);


	power_supply_changed(chg->usb_psy);
	power_supply_changed(chg->usb_psy);
	if (chg->dual_role)
		dual_role_instance_changed(chg->dual_role);
	smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
	smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
					vbus_rising ? "attached" : "detached");
					vbus_rising ? "attached" : "detached");
}
}
@@ -4957,6 +4959,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data)
	smblib_hvdcp_adaptive_voltage_change(chg);
	smblib_hvdcp_adaptive_voltage_change(chg);


	power_supply_changed(chg->usb_psy);
	power_supply_changed(chg->usb_psy);
	if (chg->dual_role)
		dual_role_instance_changed(chg->dual_role);


	rc = smblib_read(chg, APSD_STATUS_REG, &stat);
	rc = smblib_read(chg, APSD_STATUS_REG, &stat);
	if (rc < 0) {
	if (rc < 0) {
@@ -5358,6 +5362,8 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data)
				smblib_typec_mode_name[chg->typec_mode]);
				smblib_typec_mode_name[chg->typec_mode]);


	power_supply_changed(chg->usb_psy);
	power_supply_changed(chg->usb_psy);
	if (chg->dual_role)
		dual_role_instance_changed(chg->dual_role);


	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}
@@ -5434,6 +5440,15 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
			chg->sink_src_mode = UNATTACHED_MODE;
			chg->sink_src_mode = UNATTACHED_MODE;
			chg->early_usb_attach = false;
			chg->early_usb_attach = false;
			smblib_apsd_enable(chg, true);
			smblib_apsd_enable(chg, true);

			/*
			 * Restore DRP mode on type-C cable disconnect if role
			 * swap is not in progress, to ensure forced sink or src
			 * mode configuration is reset properly.
			 */
			if (chg->dual_role)
				smblib_force_dr_mode(chg,
						DUAL_ROLE_PROP_MODE_NONE);
		}
		}


		if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
		if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
@@ -5442,6 +5457,8 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
	}
	}


	power_supply_changed(chg->usb_psy);
	power_supply_changed(chg->usb_psy);
	if (chg->dual_role)
		dual_role_instance_changed(chg->dual_role);


	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}
@@ -6331,6 +6348,97 @@ static void smblib_lpd_detach_work(struct work_struct *work)
		chg->lpd_stage = LPD_STAGE_NONE;
		chg->lpd_stage = LPD_STAGE_NONE;
}
}


static char *dr_mode_text[] = {
	"ufp", "dfp", "none"
};

int smblib_force_dr_mode(struct smb_charger *chg, int mode)
{
	int rc = 0;

	switch (mode) {
	case DUAL_ROLE_PROP_MODE_UFP:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
				TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT,
				EN_SNK_ONLY_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc);
			return rc;
		}
		break;
	case DUAL_ROLE_PROP_MODE_DFP:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
				TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT,
				EN_SRC_ONLY_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable src, rc=%d\n", rc);
			return rc;
		}
		break;
	case DUAL_ROLE_PROP_MODE_NONE:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
				TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT,
				EN_TRY_SNK_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable try.snk, rc=%d\n", rc);
			return rc;
		}
		break;
	default:
		smblib_err(chg, "Power role %d not supported\n", mode);
		return -EINVAL;
	}

	if (chg->dr_mode != mode) {
		chg->dr_mode = mode;
		smblib_dbg(chg, PR_MISC, "Forced mode: %s\n",
					dr_mode_text[chg->dr_mode]);
	}

	return rc;
}

static void smblib_dual_role_check_work(struct work_struct *work)
{
	struct smb_charger *chg = container_of(work, struct smb_charger,
					role_reversal_check.work);
	int rc = 0;

	mutex_lock(&chg->dr_lock);

	switch (chg->dr_mode) {
	case DUAL_ROLE_PROP_MODE_UFP:
		if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
			smblib_dbg(chg, PR_MISC, "Role reversal not latched to UFP in %d msecs. Resetting to DRP mode\n",
				ROLE_REVERSAL_DELAY_MS);
			rc = smblib_force_dr_mode(chg,
						DUAL_ROLE_PROP_MODE_NONE);
			if (rc < 0)
				pr_err("Failed to set DRP mode, rc=%d\n", rc);
		}
		chg->pr_swap_in_progress = false;
		break;
	case DUAL_ROLE_PROP_MODE_DFP:
		if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ||
				chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
			smblib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n",
				ROLE_REVERSAL_DELAY_MS);
			rc = smblib_force_dr_mode(chg,
						DUAL_ROLE_PROP_MODE_NONE);
			if (rc < 0)
				pr_err("Failed to set DRP mode, rc=%d\n", rc);
		}
		chg->pr_swap_in_progress = false;
		break;
	default:
		pr_debug("Already in DRP mode\n");
		break;
	}

	mutex_unlock(&chg->dr_lock);
	vote(chg->awake_votable, DR_SWAP_VOTER, false, 0);
}

static int smblib_create_votables(struct smb_charger *chg)
static int smblib_create_votables(struct smb_charger *chg)
{
{
	int rc = 0;
	int rc = 0;
@@ -6476,6 +6584,8 @@ int smblib_init(struct smb_charger *chg)
	INIT_DELAYED_WORK(&chg->thermal_regulation_work,
	INIT_DELAYED_WORK(&chg->thermal_regulation_work,
					smblib_thermal_regulation_work);
					smblib_thermal_regulation_work);
	INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work);
	INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work);
	INIT_DELAYED_WORK(&chg->role_reversal_check,
					smblib_dual_role_check_work);


	if (chg->wa_flags & CHG_TERMINATION_WA) {
	if (chg->wa_flags & CHG_TERMINATION_WA) {
		INIT_WORK(&chg->chg_termination_work,
		INIT_WORK(&chg->chg_termination_work,
@@ -6511,6 +6621,7 @@ int smblib_init(struct smb_charger *chg)
	chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE;
	chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE;
	chg->cp_reason = POWER_SUPPLY_CP_NONE;
	chg->cp_reason = POWER_SUPPLY_CP_NONE;
	chg->thermal_status = TEMP_BELOW_RANGE;
	chg->thermal_status = TEMP_BELOW_RANGE;
	chg->dr_mode = DUAL_ROLE_PROP_MODE_NONE;


	switch (chg->mode) {
	switch (chg->mode) {
	case PARALLEL_MASTER:
	case PARALLEL_MASTER:
@@ -6615,6 +6726,7 @@ int smblib_deinit(struct smb_charger *chg)
		cancel_delayed_work_sync(&chg->lpd_detach_work);
		cancel_delayed_work_sync(&chg->lpd_detach_work);
		cancel_delayed_work_sync(&chg->thermal_regulation_work);
		cancel_delayed_work_sync(&chg->thermal_regulation_work);
		cancel_delayed_work_sync(&chg->usbov_dbc_work);
		cancel_delayed_work_sync(&chg->usbov_dbc_work);
		cancel_delayed_work_sync(&chg->role_reversal_check);
		power_supply_unreg_notifier(&chg->nb);
		power_supply_unreg_notifier(&chg->nb);
		smblib_destroy_votables(chg);
		smblib_destroy_votables(chg);
		qcom_step_chg_deinit();
		qcom_step_chg_deinit();
+11 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/extcon.h>
#include <linux/extcon.h>
#include <linux/usb/class-dual-role.h>
#include "storm-watch.h"
#include "storm-watch.h"


enum print_reason {
enum print_reason {
@@ -75,6 +76,7 @@ enum print_reason {
#define CHG_TERMINATION_VOTER		"CHG_TERMINATION_VOTER"
#define CHG_TERMINATION_VOTER		"CHG_TERMINATION_VOTER"
#define THERMAL_THROTTLE_VOTER		"THERMAL_THROTTLE_VOTER"
#define THERMAL_THROTTLE_VOTER		"THERMAL_THROTTLE_VOTER"
#define VOUT_VOTER			"VOUT_VOTER"
#define VOUT_VOTER			"VOUT_VOTER"
#define DR_SWAP_VOTER			"DR_SWAP_VOTER"


#define BOOST_BACK_STORM_COUNT	3
#define BOOST_BACK_STORM_COUNT	3
#define WEAK_CHG_STORM_COUNT	8
#define WEAK_CHG_STORM_COUNT	8
@@ -94,6 +96,8 @@ enum print_reason {
#define TYPEC_MEDIUM_CURRENT_UA		1500000
#define TYPEC_MEDIUM_CURRENT_UA		1500000
#define TYPEC_HIGH_CURRENT_UA		3000000
#define TYPEC_HIGH_CURRENT_UA		3000000


#define ROLE_REVERSAL_DELAY_MS		2000

enum smb_mode {
enum smb_mode {
	PARALLEL_MASTER = 0,
	PARALLEL_MASTER = 0,
	PARALLEL_SLAVE,
	PARALLEL_SLAVE,
@@ -366,6 +370,7 @@ struct smb_charger {
	/* locks */
	/* locks */
	struct mutex		smb_lock;
	struct mutex		smb_lock;
	struct mutex		ps_change_lock;
	struct mutex		ps_change_lock;
	struct mutex		dr_lock;


	/* power supplies */
	/* power supplies */
	struct power_supply		*batt_psy;
	struct power_supply		*batt_psy;
@@ -378,6 +383,9 @@ struct smb_charger {
	struct power_supply		*cp_psy;
	struct power_supply		*cp_psy;
	enum power_supply_type		real_charger_type;
	enum power_supply_type		real_charger_type;


	/* dual role class */
	struct dual_role_phy_instance	*dual_role;

	/* notifiers */
	/* notifiers */
	struct notifier_block	nb;
	struct notifier_block	nb;


@@ -418,6 +426,7 @@ struct smb_charger {
	struct delayed_work	lpd_detach_work;
	struct delayed_work	lpd_detach_work;
	struct delayed_work	thermal_regulation_work;
	struct delayed_work	thermal_regulation_work;
	struct delayed_work	usbov_dbc_work;
	struct delayed_work	usbov_dbc_work;
	struct delayed_work	role_reversal_check;


	struct alarm		lpd_recheck_timer;
	struct alarm		lpd_recheck_timer;
	struct alarm		moisture_protection_alarm;
	struct alarm		moisture_protection_alarm;
@@ -503,6 +512,7 @@ struct smb_charger {
	bool			aicl_max_reached;
	bool			aicl_max_reached;
	int			charge_full_cc;
	int			charge_full_cc;
	int			cc_soc_ref;
	int			cc_soc_ref;
	int			dr_mode;


	/* workaround flag */
	/* workaround flag */
	u32			wa_flags;
	u32			wa_flags;
@@ -707,6 +717,7 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
				union power_supply_propval *val);
				union power_supply_propval *val);
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
				const union power_supply_propval *val);
				const union power_supply_propval *val);
int smblib_force_dr_mode(struct smb_charger *chg, int mode);
int smblib_get_prop_from_bms(struct smb_charger *chg,
int smblib_get_prop_from_bms(struct smb_charger *chg,
				enum power_supply_property psp,
				enum power_supply_property psp,
				union power_supply_propval *val);
				union power_supply_propval *val);