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

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

Merge "power: smb5: Add Type-C class support for non-PD charger targets"

parents b67b79ba dd54070c
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/regulator/machine.h>
#include <linux/iio/consumer.h>
#include <linux/pmic-voter.h>
#include <linux/usb/typec.h>
#include "smb5-reg.h"
#include "smb5-lib.h"
#include "schgm-flash.h"
@@ -3399,6 +3400,37 @@ static int smb5_show_charger_status(struct smb5 *chip)
	return rc;
}

/*********************************
 * TYPEC CLASS REGISTRATION *
 **********************************/

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

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

	mutex_init(&chg->typec_lock);
	chg->typec_caps.type = TYPEC_PORT_DRP;
	chg->typec_caps.data = TYPEC_PORT_DRD;
	chg->typec_partner_desc.usb_pd = false;
	chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_NONE;
	chg->typec_caps.port_type_set = smblib_typec_port_type_set;
	chg->typec_caps.revision = 0x0130;

	chg->typec_port = typec_register_port(chg->dev, &chg->typec_caps);
	if (IS_ERR(chg->typec_port)) {
		rc = PTR_ERR(chg->typec_port);
		pr_err("failed to register typec_port rc=%d\n", rc);
		return rc;
	}

	return rc;
}

static int smb5_probe(struct platform_device *pdev)
{
	struct smb5 *chip;
@@ -3555,6 +3587,12 @@ static int smb5_probe(struct platform_device *pdev)
		goto cleanup;
	}

	rc = smb5_init_typec_class(chip);
	if (rc < 0) {
		pr_err("Couldn't initialize typec class rc=%d\n", rc);
		goto cleanup;
	}

	rc = smb5_determine_initial_status(chip);
	if (rc < 0) {
		pr_err("Couldn't determine initial status rc=%d\n",
+234 −0
Original line number Diff line number Diff line
@@ -5894,6 +5894,219 @@ static void typec_ra_ra_insertion(struct smb_charger *chg)
	smblib_hvdcp_detect_enable(chg, true);
}

static int typec_partner_register(struct smb_charger *chg)
{
	int typec_mode, rc = 0;

	if (!chg->typec_port)
		return 0;

	if (chg->typec_partner && chg->pr_swap_in_progress)
		return 0;

	if (chg->sink_src_mode == AUDIO_ACCESS_MODE)
		chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_AUDIO;
	else
		chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_NONE;

	chg->typec_partner = typec_register_partner(chg->typec_port,
			&chg->typec_partner_desc);
	if (IS_ERR(chg->typec_partner)) {
		rc = PTR_ERR(chg->typec_partner);
		pr_err("failed to register typec_partner rc=%d\n", rc);
		return rc;
	}

	typec_mode = smblib_get_prop_typec_mode(chg);

	if (typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
			|| typec_mode == POWER_SUPPLY_TYPEC_NONE) {
		typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
		typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
	} else {
		typec_set_data_role(chg->typec_port, TYPEC_HOST);
		typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
	}

	return rc;
}

static void typec_partner_unregister(struct smb_charger *chg)
{
	if (chg->typec_partner && !chg->pr_swap_in_progress) {
		smblib_dbg(chg, PR_MISC, "Un-registering typeC partner\n");
		typec_unregister_partner(chg->typec_partner);
		chg->typec_partner = NULL;
	}
}

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

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

	switch (mode) {
	case TYPEC_PORT_SNK:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
			TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc);
			return rc;
		}
		break;
	case TYPEC_PORT_SRC:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
			TYPEC_POWER_ROLE_CMD_MASK, EN_SRC_ONLY_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable src, rc=%d\n", rc);
			return rc;
		}
		break;
	case TYPEC_PORT_DRP:
		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
			TYPEC_POWER_ROLE_CMD_MASK, 0);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable DRP, rc=%d\n", rc);
			return rc;
		}
		break;
	default:
		smblib_err(chg, "Power role %d not supported\n", mode);
		return -EINVAL;
	}

	chg->dr_mode = mode;

	return rc;
}

int smblib_typec_port_type_set(const struct typec_capability *cap,
					enum typec_port_type type)
{
	struct smb_charger *chg = container_of(cap,
					struct smb_charger, typec_caps);
	int rc = 0;

	mutex_lock(&chg->typec_lock);

	if ((chg->pr_swap_in_progress) || (type == TYPEC_PORT_DRP)) {
		smblib_dbg(chg, PR_MISC, "Ignoring port type request type = %d swap_in_progress = %d\n",
				type, chg->pr_swap_in_progress);
		goto unlock;
	}

	chg->pr_swap_in_progress = true;

	rc = smblib_force_dr_mode(chg, type);
	if (rc < 0) {
		chg->pr_swap_in_progress = false;
		smblib_err(chg, "Failed to force mode, rc=%d\n", rc);
		goto unlock;
	}

	smblib_dbg(chg, PR_MISC, "Requested role %s\n",
				type ? "SINK" : "SOURCE");

	/*
	 * As per the hardware requirements,
	 * schedule the work with required delay.
	 */
	if (!(delayed_work_pending(&chg->role_reversal_check))) {
		cancel_delayed_work_sync(&chg->role_reversal_check);
		schedule_delayed_work(&chg->role_reversal_check,
			msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS));
		vote(chg->awake_votable, TYPEC_SWAP_VOTER, true, 0);
	}

unlock:
	mutex_unlock(&chg->typec_lock);
	return rc;
}

static int smblib_role_switch_failure(struct smb_charger *chg, int mode)
{
	int rc = 0;
	union power_supply_propval pval = {0, };

	if (!chg->use_extcon)
		return 0;

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

	/*
	 * When role switch fails notify the
	 * current charger state to usb driver.
	 */
	if (pval.intval && mode == TYPEC_PORT_SRC) {
		smblib_dbg(chg, PR_MISC, " Role reversal failed, notifying device mode to usb driver.\n");
		smblib_notify_device_mode(chg, true);
	}

	return rc;
}

static void smblib_typec_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->typec_lock);

	switch (chg->dr_mode) {
	case TYPEC_PORT_SNK:
		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, TYPEC_PORT_DRP);
			if (rc < 0)
				smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
						rc);
		} else {
			chg->power_role = POWER_SUPPLY_TYPEC_PR_SINK;
			typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
			typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
			smblib_dbg(chg, PR_MISC, "Role changed successfully to SINK");
		}
		break;
	case TYPEC_PORT_SRC:
		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, TYPEC_PORT_DRP);
			if (rc < 0)
				smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
					rc);
			rc = smblib_role_switch_failure(chg, TYPEC_PORT_SRC);
			if (rc < 0)
				smblib_err(chg, "Failed to role switch rc=%d\n",
					rc);
		} else {
			chg->power_role = POWER_SUPPLY_TYPEC_PR_SOURCE;
			typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
			typec_set_data_role(chg->typec_port, TYPEC_HOST);
			smblib_dbg(chg, PR_MISC, "Role changed successfully to SOURCE");
		}
		break;
	default:
		pr_debug("Already in DRP mode\n");
		break;
	}

	chg->pr_swap_in_progress = false;
	vote(chg->awake_votable, TYPEC_SWAP_VOTER, false, 0);
	mutex_unlock(&chg->typec_lock);
}

static void typec_sink_removal(struct smb_charger *chg)
{
	int rc;
@@ -5909,6 +6122,8 @@ static void typec_sink_removal(struct smb_charger *chg)
			smblib_notify_usb_host(chg, false);
		chg->otg_present = false;
	}

	typec_partner_unregister(chg);
}

static void typec_src_removal(struct smb_charger *chg)
@@ -6044,6 +6259,7 @@ static void typec_src_removal(struct smb_charger *chg)
	if (chg->use_extcon)
		smblib_notify_device_mode(chg, false);

	typec_partner_unregister(chg);
	chg->typec_legacy = false;

	del_timer_sync(&chg->apsd_timer);
@@ -6229,6 +6445,10 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
			typec_src_insertion(chg);
		}

		rc = typec_partner_register(chg);
		if (rc < 0)
			smblib_err(chg, "failed to register partner rc =%d\n",
					rc);
	} else {
		switch (chg->sink_src_mode) {
		case SRC_MODE:
@@ -6251,6 +6471,15 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
			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->typec_port && !chg->pr_swap_in_progress)
			smblib_force_dr_mode(chg, TYPEC_PORT_DRP);

		if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
			schedule_delayed_work(&chg->lpd_detach_work,
					msecs_to_jiffies(1000));
@@ -7682,6 +7911,9 @@ int smblib_init(struct smb_charger *chg)
					smblib_pr_lock_clear_work);
	timer_setup(&chg->apsd_timer, apsd_timer_cb, 0);

	INIT_DELAYED_WORK(&chg->role_reversal_check,
					smblib_typec_role_check_work);

	if (chg->wa_flags & CHG_TERMINATION_WA) {
		INIT_WORK(&chg->chg_termination_work,
					smblib_chg_termination_work);
@@ -7726,6 +7958,7 @@ int smblib_init(struct smb_charger *chg)
	chg->thermal_status = TEMP_BELOW_RANGE;
	chg->typec_irq_en = true;
	chg->cp_topo = -EINVAL;
	chg->dr_mode = TYPEC_PORT_DRP;

	switch (chg->mode) {
	case PARALLEL_MASTER:
@@ -7833,6 +8066,7 @@ int smblib_deinit(struct smb_charger *chg)
		cancel_delayed_work_sync(&chg->lpd_detach_work);
		cancel_delayed_work_sync(&chg->thermal_regulation_work);
		cancel_delayed_work_sync(&chg->usbov_dbc_work);
		cancel_delayed_work_sync(&chg->role_reversal_check);
		cancel_delayed_work_sync(&chg->pr_swap_detach_work);
		power_supply_unreg_notifier(&chg->nb);
		smblib_destroy_votables(chg);
+14 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/extcon-provider.h>
#include <linux/usb/typec.h>
#include "storm-watch.h"
#include "battery.h"

@@ -79,6 +80,7 @@ enum print_reason {
#define WLS_PL_CHARGING_VOTER		"WLS_PL_CHARGING_VOTER"
#define ICL_CHANGE_VOTER		"ICL_CHANGE_VOTER"
#define OVERHEAT_LIMIT_VOTER		"OVERHEAT_LIMIT_VOTER"
#define TYPEC_SWAP_VOTER		"TYPEC_SWAP_VOTER"

#define BOOST_BACK_STORM_COUNT	3
#define WEAK_CHG_STORM_COUNT	8
@@ -100,6 +102,7 @@ enum print_reason {
#define DCIN_ICL_MIN_UA			100000
#define DCIN_ICL_MAX_UA			1500000
#define DCIN_ICL_STEP_UA		100000
#define ROLE_REVERSAL_DELAY_MS		500

enum smb_mode {
	PARALLEL_MASTER = 0,
@@ -392,6 +395,7 @@ struct smb_charger {
	spinlock_t		typec_pr_lock;
	struct mutex		adc_lock;
	struct mutex		dpdm_lock;
	struct mutex		typec_lock;

	/* power supplies */
	struct power_supply		*batt_psy;
@@ -419,6 +423,12 @@ struct smb_charger {
	struct smb_regulator	*vconn_vreg;
	struct regulator	*dpdm_reg;

	/* typec */
	struct typec_port	*typec_port;
	struct typec_capability	typec_caps;
	struct typec_partner	*typec_partner;
	struct typec_partner_desc typec_partner_desc;

	/* votables */
	struct votable		*dc_suspend_votable;
	struct votable		*fcc_votable;
@@ -458,6 +468,7 @@ struct smb_charger {
	struct delayed_work	usbov_dbc_work;
	struct delayed_work	pr_swap_detach_work;
	struct delayed_work	pr_lock_clear_work;
	struct delayed_work	role_reversal_check;

	struct alarm		lpd_recheck_timer;
	struct alarm		moisture_protection_alarm;
@@ -510,6 +521,7 @@ struct smb_charger {
	bool			typec_present;
	int			fake_input_current_limited;
	int			typec_mode;
	int			dr_mode;
	int			usb_icl_change_irq_enabled;
	u32			jeita_status;
	u8			float_cfg;
@@ -796,6 +808,8 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
				union power_supply_propval *val);
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
				const union power_supply_propval *val);
int smblib_typec_port_type_set(const struct typec_capability *cap,
					enum typec_port_type type);
int smblib_get_prop_from_bms(struct smb_charger *chg,
				enum power_supply_property psp,
				union power_supply_propval *val);