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

Commit e66d3e6e authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar
Browse files

power: smb135x-charger: workaround for otg enablement



To enable OTG the software has to issue enable, disable and
enable in that order to actually enable the OTG mode. Moreover
each step in this sequence needs to be done withing 10mS of the prior
step.

Update the driver to do so.

Change-Id: Iba619a5400934047b1d874acb2c249a4cb108bdd
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent e5421545
Loading
Loading
Loading
Loading
+84 −3
Original line number Diff line number Diff line
/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -1015,17 +1015,98 @@ static int smb135x_float_voltage_set(struct smb135x_chg *chip, int vfloat_mv)
	return smb135x_write(chip, VFLOAT_REG, temp);
}

static bool elapsed_msec_greater(struct timeval *start_time,
				struct timeval *end_time, int ms)
{
	int msec_elapsed;

	msec_elapsed = (end_time->tv_sec - start_time->tv_sec) * 1000 +
		DIV_ROUND_UP(end_time->tv_usec - start_time->tv_usec, 1000);

	return (msec_elapsed > ms);
}

#define MAX_STEP_MS		10
static int smb135x_chg_otg_regulator_enable(struct regulator_dev *rdev)
{
	int rc = 0;
	struct smb135x_chg *chip = rdev_get_drvdata(rdev);
	int restart_count = 0;
	struct timeval time_a, time_b, time_c, time_d;

	/*
	 * Workaround for a hardware bug where the OTG needs to be enabled
	 * disabled and enabled for it to be actually enabled. The time between
	 * each step should be atmost MAX_STEP_MS
	 *
	 * Note that if enable-disable executes within the timeframe
	 * but the final enable takes more than MAX_STEP_ME, we treat it as
	 * the first enable and try disabling again. We don't want
	 * to issue enable back to back.
	 *
	 * Notice the instances when time is captured and the successive
	 * steps.
	 * timeA-enable-timeC-disable-timeB-enable-timeD.
	 * When
	 * (timeB - timeA) < MAX_STEP_MS AND (timeC - timeD) < MAX_STEP_MS
	 * then it is guaranteed that the successive steps
	 * must have executed within MAX_STEP_MS
	 */
	do_gettimeofday(&time_a);
restart_from_enable:
	/* first step - enable otg */
	rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, OTG_EN);
	if (rc < 0)
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
		return rc;
	}

restart_from_disable:
	/* second step - disable otg */
	do_gettimeofday(&time_c);
	rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, 0);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
		return rc;
	}
	do_gettimeofday(&time_b);

	if (elapsed_msec_greater(&time_a, &time_b, MAX_STEP_MS)) {
		restart_count++;
		if (restart_count > 10) {
			dev_err(chip->dev,
				"Couldn't enable OTG restart_count=%d\n",
				restart_count);
			return -EAGAIN;
		}
		time_a = time_b;
		pr_debug("restarting from first enable\n");
		goto restart_from_enable;
	}

	/* third step (first step in case of a failure) - enable otg */
	time_a = time_b;
	rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, OTG_EN);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
		return rc;
	}
	do_gettimeofday(&time_d);

	if (elapsed_msec_greater(&time_c, &time_d, MAX_STEP_MS)) {
		restart_count++;
		if (restart_count > 10) {
			dev_err(chip->dev,
				"Couldn't enable OTG restart_count=%d\n",
				restart_count);
			return -EAGAIN;
		}
		pr_debug("restarting from disable\n");
		goto restart_from_disable;
	}
	return rc;
}

static int smb135x_chg_otg_regulator_disable(struct regulator_dev *rdev)
{
	int rc = 0;