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

Commit 461e9ea4 authored by Umang Agrawal's avatar Umang Agrawal
Browse files

power: smb5: Add moisture detection support for pmi632



Presence of moisture expedites the corrosion of Type-C/uUSB ID pins
due to constant DRP toggling. To reduce the effect of corrosion,
set DRP toggling duty cycle to 1% and suspend usb input on water
detection.

Use property MOISTURE_DETECTED to reflect moisture detection status.

Change-Id: I3bb41564cf67127ae2c0bb27089a1e8b5d722df6
Signed-off-by: default avatarUmang Agrawal <uagrawal@codeaurora.org>
parent bbb93d75
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -220,6 +220,12 @@ Charger specific properties:
		    connector THERM, only valid values are (0/30/100/400).
		    If not specified 100K is used as default pull-up.

- qcom,moisture-protection-enable
	Usage:      optional
	Value type: bool
	Definition: Boolean flag which when present enables mositure protection
		    feature.

=============================================
Second Level Nodes - SMB5 Charger Peripherals
=============================================
+40 −8
Original line number Diff line number Diff line
@@ -280,6 +280,8 @@ static int smb5_chg_config_init(struct smb5 *chip)
	case PMI632_SUBTYPE:
		chip->chg.smb_version = PMI632_SUBTYPE;
		chg->wa_flags |= WEAK_ADAPTER_WA;
		if (pmic_rev_id->rev4 >= 2)
			chg->wa_flags |= MOISTURE_PROTECTION_WA;
		chg->param = smb5_pmi632_params;
		chg->use_extcon = true;
		chg->name = "pmi632_charger";
@@ -484,6 +486,9 @@ static int smb5_parse_dt(struct smb5 *chip)
	of_property_read_u32(node, "qcom,connector-internal-pull-kohm",
					&chg->connector_pull_up);

	chg->moisture_protection_enabled = of_property_read_bool(node,
					"qcom,moisture-protection-enable");

	return 0;
}

@@ -604,6 +609,7 @@ static enum power_supply_property smb5_usb_props[] = {
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED,
	POWER_SUPPLY_PROP_QC_OPTI_DISABLE,
	POWER_SUPPLY_PROP_MOISTURE_DETECTED,
};

static int smb5_usb_get_prop(struct power_supply *psy,
@@ -742,6 +748,9 @@ static int smb5_usb_get_prop(struct power_supply *psy,
		if (chg->hw_connector_mitigation)
			val->intval |= POWER_SUPPLY_QC_CTM_DISABLE;
		break;
	case POWER_SUPPLY_PROP_MOISTURE_DETECTED:
		val->intval = chg->moisture_present;
		break;
	default:
		pr_err("get prop %d is not supported in usb\n", psp);
		rc = -EINVAL;
@@ -1629,14 +1638,6 @@ static int smb5_configure_typec(struct smb_charger *chg)
		return rc;
	}

	rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
				TYPEC_WATER_DETECTION_INT_EN_BIT);
	if (rc < 0) {
		dev_err(chg->dev,
			"Couldn't configure Type-C interrupts rc=%d\n", rc);
		return rc;
	}

	rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
				EN_TRY_SNK_BIT, EN_TRY_SNK_BIT);
	if (rc < 0) {
@@ -1675,6 +1676,37 @@ static int smb5_configure_micro_usb(struct smb_charger *chg)
		return rc;
	}

	if (chg->moisture_protection_enabled &&
				(chg->wa_flags & MOISTURE_PROTECTION_WA)) {
		/* Enable moisture detection interrupt */
		rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
				TYPEC_WATER_DETECTION_INT_EN_BIT,
				TYPEC_WATER_DETECTION_INT_EN_BIT);
		if (rc < 0) {
			dev_err(chg->dev, "Couldn't enable moisture detection interrupt rc=%d\n",
				rc);
			return rc;
		}

		/* Enable uUSB factory mode */
		rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
					EN_MICRO_USB_FACTORY_MODE_BIT,
					EN_MICRO_USB_FACTORY_MODE_BIT);
		if (rc < 0) {
			dev_err(chg->dev, "Couldn't enable uUSB factory mode c=%d\n",
				rc);
			return rc;
		}

		/* Disable periodic monitoring of CC_ID pin */
		rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0);
		if (rc < 0) {
			dev_err(chg->dev, "Couldn't disable periodic monitoring of CC_ID rc=%d\n",
				rc);
			return rc;
		}
	}

	return rc;
}

+226 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/pmic-voter.h>
#include <linux/of_batterydata.h>
#include <linux/alarmtimer.h>
#include "smb5-lib.h"
#include "smb5-reg.h"
#include "battery.h"
@@ -1045,6 +1046,100 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua)
	return 0;
}

/********************
 * Moisture Protection *
 ********************/
#define MICRO_USB_DETECTION_ON_TIME_20_MS 0x08
#define MICRO_USB_DETECTION_PERIOD_X_100 0x03
#define U_USB_STATUS_WATER_PRESENT 0x00
static int smblib_set_moisture_protection(struct smb_charger *chg,
				bool enable)
{
	int rc = 0;

	if (chg->moisture_present == enable) {
		smblib_dbg(chg, PR_MISC, "No change in moisture protection status\n");
		return rc;
	}

	if (enable) {
		chg->moisture_present = true;

		/* Disable uUSB factory mode detection */
		rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
					EN_MICRO_USB_FACTORY_MODE_BIT, 0);
		if (rc < 0) {
			smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n",
				rc);
			return rc;
		}

		/* Disable moisture detection and uUSB state change interrupt */
		rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
					TYPEC_WATER_DETECTION_INT_EN_BIT |
					MICRO_USB_STATE_CHANGE_INT_EN_BIT, 0);
		if (rc < 0) {
			smblib_err(chg, "Couldn't disable moisture detection interrupt rc=%d\n",
			rc);
			return rc;
		}

		/* Set 1% duty cycle on ID detection */
		rc = smblib_masked_write(chg,
					TYPEC_U_USB_WATER_PROTECTION_CFG_REG,
					EN_MICRO_USB_WATER_PROTECTION_BIT |
					MICRO_USB_DETECTION_ON_TIME_CFG_MASK |
					MICRO_USB_DETECTION_PERIOD_CFG_MASK,
					EN_MICRO_USB_WATER_PROTECTION_BIT |
					MICRO_USB_DETECTION_ON_TIME_20_MS |
					MICRO_USB_DETECTION_PERIOD_X_100);
		if (rc < 0) {
			smblib_err(chg, "Couldn't set 1 percent CC_ID duty cycle rc=%d\n",
				rc);
			return rc;
		}

		vote(chg->usb_icl_votable, MOISTURE_VOTER, true, 0);
	} else {
		chg->moisture_present = false;
		vote(chg->usb_icl_votable, MOISTURE_VOTER, false, 0);

		/* Enable moisture detection and uUSB state change interrupt */
		rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
					TYPEC_WATER_DETECTION_INT_EN_BIT |
					MICRO_USB_STATE_CHANGE_INT_EN_BIT,
					TYPEC_WATER_DETECTION_INT_EN_BIT |
					MICRO_USB_STATE_CHANGE_INT_EN_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't enable moisture detection and uUSB state change interrupt rc=%d\n",
				rc);
			return rc;
		}

		/* Disable periodic monitoring of CC_ID pin */
		rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0);
		if (rc < 0) {
			smblib_err(chg, "Couldn't disable 1 percent CC_ID duty cycle rc=%d\n",
				rc);
			return rc;
		}

		/* Enable uUSB factory mode detection */
		rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
					EN_MICRO_USB_FACTORY_MODE_BIT,
					EN_MICRO_USB_FACTORY_MODE_BIT);
		if (rc < 0) {
			smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n",
				rc);
			return rc;
		}
	}

	smblib_dbg(chg, PR_MISC, "Moisture protection %s\n",
			chg->moisture_present ? "enabled" : "disabled");
	return rc;
}

/*********************
 * VOTABLE CALLBACKS *
 *********************/
@@ -3454,11 +3549,28 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data)
	struct smb_charger *chg = irq_data->parent_data;

	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
		if (chg->moisture_protection_enabled &&
				(chg->wa_flags & MOISTURE_PROTECTION_WA)) {
			/*
			 * Adding pm_stay_awake as because pm_relax is called
			 * on exit path from the work routine.
			 */
			pm_stay_awake(chg->dev);
			schedule_work(&chg->moisture_protection_work);
		}

		cancel_delayed_work_sync(&chg->uusb_otg_work);
		/*
		 * Skip OTG enablement if RID interrupt triggers with moisture
		 * protection still enabled.
		 */
		if (!chg->moisture_present) {
			vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0);
			smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n");
			schedule_delayed_work(&chg->uusb_otg_work,
					msecs_to_jiffies(chg->otg_delay_ms));
		}

		return IRQ_HANDLED;
	}

@@ -3826,6 +3938,96 @@ static void smblib_pl_enable_work(struct work_struct *work)
	vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
}

#define MOISTURE_PROTECTION_CHECK_DELAY_MS 300000		/* 5 mins */
static void smblib_moisture_protection_work(struct work_struct *work)
{
	struct smb_charger *chg = container_of(work, struct smb_charger,
						moisture_protection_work);
	int rc;
	bool usb_plugged_in;
	u8 stat;

	/*
	 * Disable 1% duty cycle on CC_ID pin and enable uUSB factory mode
	 * detection to track any change on RID, as interrupts are disable.
	 */
	rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0);
	if (rc < 0) {
		smblib_err(chg, "Couldn't disable periodic monitoring of CC_ID rc=%d\n",
			rc);
		goto out;
	}

	rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
					EN_MICRO_USB_FACTORY_MODE_BIT,
					EN_MICRO_USB_FACTORY_MODE_BIT);
	if (rc < 0) {
		smblib_err(chg, "Couldn't enable uUSB factory mode detection rc=%d\n",
			rc);
		goto out;
	}

	/*
	 * Add a delay of 100ms to allow change in rid to reflect on
	 * status registers.
	 */
	msleep(100);

	rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
		goto out;
	}
	usb_plugged_in = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);

	/* Check uUSB status for moisture presence */
	rc = smblib_read(chg, TYPEC_U_USB_STATUS_REG, &stat);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read TYPE_C_U_USB_STATUS_REG rc=%d\n",
				rc);
		goto out;
	}

	/*
	 * Factory mode detection happens in case of USB plugged-in by using
	 * a different current source of 2uA which can hamper moisture
	 * detection. Since factory mode is not supported in kernel, factory
	 * mode detection can be considered as equivalent to presence of
	 * moisture.
	 */
	if (stat == U_USB_STATUS_WATER_PRESENT || stat == U_USB_FMB1_BIT ||
			stat == U_USB_FMB2_BIT || (usb_plugged_in &&
			stat == U_USB_FLOAT1_BIT)) {
		smblib_set_moisture_protection(chg, true);
		alarm_start_relative(&chg->moisture_protection_alarm,
			ms_to_ktime(MOISTURE_PROTECTION_CHECK_DELAY_MS));
	} else {
		smblib_set_moisture_protection(chg, false);
		rc = alarm_cancel(&chg->moisture_protection_alarm);
		if (rc < 0)
			smblib_err(chg, "Couldn't cancel moisture protection alarm\n");
	}

out:
	pm_relax(chg->dev);
}

static enum alarmtimer_restart moisture_protection_alarm_cb(struct alarm *alarm,
							ktime_t now)
{
	struct smb_charger *chg = container_of(alarm, struct smb_charger,
					moisture_protection_alarm);

	smblib_dbg(chg, PR_MISC, "moisture Protection Alarm Triggered %lld\n",
			ktime_to_ms(now));

	/* Atomic context, cannot use voter */
	pm_stay_awake(chg->dev);
	schedule_work(&chg->moisture_protection_work);

	return ALARMTIMER_NORESTART;
}

#define JEITA_SOFT			0
#define JEITA_HARD			1
static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds,
@@ -4028,6 +4230,21 @@ int smblib_init(struct smb_charger *chg)
	INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
	INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
	INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);

	if (chg->moisture_protection_enabled &&
				(chg->wa_flags & MOISTURE_PROTECTION_WA)) {
		INIT_WORK(&chg->moisture_protection_work,
					smblib_moisture_protection_work);

		if (alarmtimer_get_rtcdev()) {
			alarm_init(&chg->moisture_protection_alarm,
				ALARM_BOOTTIME, moisture_protection_alarm_cb);
		} else {
			smblib_err(chg, "Failed to initialize moisture protection alarm\n");
			return -ENODEV;
		}
	}

	chg->fake_capacity = -EINVAL;
	chg->fake_input_current_limited = -EINVAL;
	chg->fake_batt_status = -EINVAL;
@@ -4089,6 +4306,11 @@ int smblib_deinit(struct smb_charger *chg)
{
	switch (chg->mode) {
	case PARALLEL_MASTER:
		if (chg->moisture_protection_enabled &&
				(chg->wa_flags & MOISTURE_PROTECTION_WA)) {
			alarm_cancel(&chg->moisture_protection_alarm);
			cancel_work_sync(&chg->moisture_protection_work);
		}
		cancel_work_sync(&chg->bms_update_work);
		cancel_work_sync(&chg->jeita_update_work);
		cancel_work_sync(&chg->pl_update_work);
+9 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/extcon.h>
#include <linux/alarmtimer.h>
#include "storm-watch.h"

enum print_reason {
@@ -68,6 +69,7 @@ enum print_reason {
#define HW_LIMIT_VOTER			"HW_LIMIT_VOTER"
#define FORCE_RECHARGE_VOTER		"FORCE_RECHARGE_VOTER"
#define AICL_THRESHOLD_VOTER		"AICL_THRESHOLD_VOTER"
#define MOISTURE_VOTER			"MOISTURE_VOTER"

#define BOOST_BACK_STORM_COUNT	3
#define WEAK_CHG_STORM_COUNT	8
@@ -98,6 +100,7 @@ enum sink_src_mode {
enum {
	BOOST_BACK_WA			= BIT(0),
	WEAK_ADAPTER_WA			= BIT(1),
	MOISTURE_PROTECTION_WA		= BIT(2),
};

enum {
@@ -331,6 +334,7 @@ struct smb_charger {
	struct work_struct	bms_update_work;
	struct work_struct	pl_update_work;
	struct work_struct	jeita_update_work;
	struct work_struct	moisture_protection_work;
	struct delayed_work	ps_change_timeout_work;
	struct delayed_work	clear_hdc_work;
	struct delayed_work	icl_change_work;
@@ -338,6 +342,9 @@ struct smb_charger {
	struct delayed_work	uusb_otg_work;
	struct delayed_work	bb_removal_work;

	/* alarm */
	struct alarm		moisture_protection_alarm;

	/* pd */
	int			voltage_min_uv;
	int			voltage_max_uv;
@@ -386,6 +393,8 @@ struct smb_charger {
	int			aicl_cont_threshold_mv;
	int			default_aicl_cont_threshold_mv;
	bool			aicl_max_reached;
	bool			moisture_present;
	bool			moisture_protection_enabled;

	/* workaround flag */
	u32			wa_flags;
+12 −0
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ enum {
#define USBIN_SUSPEND_STS_BIT			BIT(6)
#define USE_USBIN_BIT				BIT(4)
#define USE_DCIN_BIT				BIT(3)
#define POWER_PATH_MASK				GENMASK(2, 1)
#define VALID_INPUT_POWER_SOURCE_STS_BIT	BIT(0)

#define DCDC_CMD_OTG_REG			(DCDC_BASE + 0x40)
@@ -284,6 +285,7 @@ enum {
#define TYPEC_ATTACH_DETACH_STATE_BIT		BIT(5)

#define TYPE_C_MISC_STATUS_REG			(TYPEC_BASE + 0x0B)
#define TYPEC_WATER_DETECTION_STATUS_BIT	BIT(7)
#define SNK_SRC_MODE_BIT			BIT(6)
#define TYPEC_VBUS_ERROR_STATUS_BIT		BIT(4)
#define CC_ORIENTATION_BIT			BIT(1)
@@ -296,6 +298,10 @@ enum {
#define TYPEC_U_USB_STATUS_REG			(TYPEC_BASE + 0x0F)
#define U_USB_GROUND_NOVBUS_BIT			BIT(6)
#define U_USB_GROUND_BIT			BIT(4)
#define U_USB_FMB1_BIT				BIT(3)
#define U_USB_FLOAT1_BIT			BIT(2)
#define U_USB_FMB2_BIT				BIT(1)
#define U_USB_FLOAT2_BIT			BIT(0)

#define TYPE_C_MODE_CFG_REG			(TYPEC_BASE + 0x44)
#define TYPEC_POWER_ROLE_CMD_MASK		GENMASK(2, 1)
@@ -341,8 +347,14 @@ enum {
#define REDUCE_TCCDEBOUNCE_TO_2MS_BIT		BIT(2)

#define TYPEC_U_USB_CFG_REG			(TYPEC_BASE + 0x70)
#define EN_MICRO_USB_FACTORY_MODE_BIT		BIT(1)
#define EN_MICRO_USB_MODE_BIT			BIT(0)

#define TYPEC_U_USB_WATER_PROTECTION_CFG_REG	(TYPEC_BASE + 0x72)
#define EN_MICRO_USB_WATER_PROTECTION_BIT	BIT(4)
#define MICRO_USB_DETECTION_ON_TIME_CFG_MASK	GENMASK(3, 2)
#define MICRO_USB_DETECTION_PERIOD_CFG_MASK	GENMASK(1, 0)

#define TYPEC_MICRO_USB_MODE_REG		(TYPEC_BASE + 0x73)
#define MICRO_USB_MODE_ONLY_BIT			BIT(0)
/********************************