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

Commit c5395e66 authored by Fenglin Wu's avatar Fenglin Wu Committed by Subbaraman Narayanamurthy
Browse files

regulator: qpnp-labibb-regulator: Restart LAB/IBB after SC fault



PBS will be triggered in PMIC hardware to disable LAB/IBB regulators
when a SC(short circuit) error is happened. The regulators won't be
restart in hardware and they will be kept disabled always after that.
Restart LAB/IBB regulator in the software if SC IRQ is detected, but
stop doing this if the SC IRQ had fired frequently.

CRs-Fixed: 2002373
Change-Id: I5db2b70999d043395e070bc9d61015477455cce9
Signed-off-by: default avatarFenglin Wu <fenglinw@codeaurora.org>
parent 3c41228f
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -88,11 +88,11 @@ LAB subnode required properties:
					50, 60, 70 and 80.
- interrupts:				Specify the interrupts as per the interrupt
					encoding.
					Currently "lab-vreg-ok" is required for
					LCD mode in pmi8998. For AMOLED mode,
					"lab-vreg-ok" is required only when SWIRE
					control is enabled and skipping 2nd SWIRE
					pulse is required in pmi8952/8996.
					Currently "lab-vreg-ok" is required and "lab-sc_err"
					is optional for LCD mode in pmi8998.
					For AMOLED mode, "lab-vreg-ok" is required
					only when SWIRE control is enabled and skipping
					2nd SWIRE pulse is required in pmi8952/8996.
- interrupt-names:			Interrupt names to match up 1-to-1 with
					the interrupts specified in 'interrupts'
					property.
@@ -213,6 +213,14 @@ IBB subnode required properties:

IBB subnode optional properties:

- interrupts:				Specify the interrupts as per the interrupt
					encoding.
					Currently "ibb-sc-err" could be used for LCD mode
					in pmi8998 to detect the short circuit fault.
- interrupt-names:			Interrupt names to match up 1-to-1 with
					the interrupts specified in 'interrupts'
					property.

- qcom,qpnp-ibb-discharge-resistor:	The discharge resistor in Kilo Ohms which
					controls the soft start time. Supported values
					are 300, 64, 32 and 16.
+309 −67
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/regmap.h>
#include <linux/module.h>
#include <linux/notifier.h>
@@ -37,6 +38,7 @@

#define REG_REVISION_2			0x01
#define REG_PERPH_TYPE			0x04
#define REG_INT_RT_STS			0x10

#define QPNP_LAB_TYPE			0x24
#define QPNP_IBB_TYPE			0x20
@@ -77,8 +79,8 @@
/* LAB register bits definitions */

/* REG_LAB_STATUS1 */
#define LAB_STATUS1_VREG_OK_MASK	BIT(7)
#define LAB_STATUS1_VREG_OK		BIT(7)
#define LAB_STATUS1_VREG_OK_BIT		BIT(7)
#define LAB_STATUS1_SC_DETECT_BIT	BIT(6)

/* REG_LAB_SWIRE_PGM_CTL */
#define LAB_EN_SWIRE_PGM_VOUT		BIT(7)
@@ -188,8 +190,8 @@
/* IBB register bits definition */

/* REG_IBB_STATUS1 */
#define IBB_STATUS1_VREG_OK_MASK	BIT(7)
#define IBB_STATUS1_VREG_OK		BIT(7)
#define IBB_STATUS1_VREG_OK_BIT		BIT(7)
#define IBB_STATUS1_SC_DETECT_BIT	BIT(6)

/* REG_IBB_VOLTAGE */
#define IBB_VOLTAGE_OVERRIDE_EN		BIT(7)
@@ -557,6 +559,8 @@ struct lab_regulator {
	struct mutex			lab_mutex;

	int				lab_vreg_ok_irq;
	int				lab_sc_irq;

	int				curr_volt;
	int				min_volt;

@@ -573,6 +577,8 @@ struct ibb_regulator {
	struct regulator_dev		*rdev;
	struct mutex			ibb_mutex;

	int				ibb_sc_irq;

	int				curr_volt;
	int				min_volt;

@@ -603,6 +609,9 @@ struct qpnp_labibb {
	struct mutex			bus_mutex;
	enum qpnp_labibb_mode		mode;
	struct work_struct		lab_vreg_ok_work;
	struct delayed_work		sc_err_recovery_work;
	struct hrtimer			sc_err_check_timer;
	int				sc_err_count;
	bool				standalone;
	bool				ttw_en;
	bool				in_ttw_mode;
@@ -2193,7 +2202,7 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
			return;
		}

		if (val & LAB_STATUS1_VREG_OK) {
		if (val & LAB_STATUS1_VREG_OK_BIT) {
			raw_notifier_call_chain(&labibb_notifier,
						LAB_VREG_OK, NULL);
			break;
@@ -2226,6 +2235,74 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
	}
}

static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb)
{
	int rc;
	u8 val;

	val = LAB_ENABLE_CTL_EN;
	rc = qpnp_labibb_write(labibb,
		labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
	if (rc < 0) {
		pr_err("Write register %x failed rc = %d\n",
					REG_LAB_ENABLE_CTL, rc);
		return rc;
	}

	udelay(labibb->lab_vreg.soft_start);

	rc = qpnp_labibb_read(labibb, labibb->lab_base +
				REG_LAB_STATUS1, &val, 1);
	if (rc < 0) {
		pr_err("Read register %x failed rc = %d\n",
					REG_LAB_STATUS1, rc);
		return rc;
	}

	if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
		pr_err("Can't enable LAB standalone\n");
		return -EINVAL;
	}

	return 0;
}

static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb)
{
	int rc, delay, retries = 10;
	u8 val;

	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
	if (rc < 0) {
		pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
		return rc;
	}

	delay = labibb->ibb_vreg.soft_start;
	while (retries--) {
		/* Wait for a small period before reading IBB_STATUS1 */
		usleep_range(delay, delay + 100);

		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
				REG_IBB_STATUS1, &val, 1);
		if (rc < 0) {
			pr_err("Read register %x failed rc = %d\n",
				REG_IBB_STATUS1, rc);
			return rc;
		}

		if (val & IBB_STATUS1_VREG_OK_BIT)
			break;
	}

	if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
		pr_err("Can't enable IBB standalone\n");
		return -EINVAL;
	}

	return 0;
}

static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
{
	int rc;
@@ -2267,7 +2344,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
		labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start,
				labibb->ibb_vreg.pwrup_dly, dly);

	if (!(val & LAB_STATUS1_VREG_OK)) {
	if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
		pr_err("failed for LAB %x\n", val);
		goto err_out;
	}
@@ -2284,7 +2361,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
			goto err_out;
		}

		if (val & IBB_STATUS1_VREG_OK) {
		if (val & IBB_STATUS1_VREG_OK_BIT) {
			enabled = true;
			break;
		}
@@ -2355,7 +2432,7 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
			return rc;
		}

		if (!(val & IBB_STATUS1_VREG_OK)) {
		if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
			disabled = true;
			break;
		}
@@ -2384,8 +2461,6 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
{
	int rc;
	u8 val;

	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);

	if (labibb->sc_detected) {
@@ -2402,34 +2477,14 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
	}

	if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) {

		if (!labibb->standalone)
			return qpnp_labibb_regulator_enable(labibb);

		val = LAB_ENABLE_CTL_EN;
		rc = qpnp_labibb_write(labibb,
			labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
		if (rc < 0) {
			pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n",
				REG_LAB_ENABLE_CTL, rc);
			return rc;
		}

		udelay(labibb->lab_vreg.soft_start);

		rc = qpnp_labibb_read(labibb, labibb->lab_base +
					REG_LAB_STATUS1, &val, 1);
		if (rc < 0) {
			pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n",
				REG_LAB_STATUS1, rc);
		rc = qpnp_lab_enable_standalone(labibb);
		if (rc) {
			pr_err("enable lab standalone failed, rc=%d\n", rc);
			return rc;
		}

		if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) {
			pr_err("qpnp_lab_regulator_enable failed\n");
			return -EINVAL;
		}

		labibb->lab_vreg.vreg_enabled = 1;
	}

@@ -2474,6 +2529,163 @@ static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev)
	return labibb->lab_vreg.vreg_enabled;
}

static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb)
{
	int rc;

	if (labibb->skip_2nd_swire_cmd) {
		rc = qpnp_ibb_ps_config(labibb, false);
		if (rc < 0) {
			pr_err("Failed to disable IBB PS rc=%d\n", rc);
			return rc;
		}
	}

	if (!labibb->swire_control) {
		if (!labibb->standalone)
			return qpnp_labibb_regulator_enable(labibb);

		rc = qpnp_ibb_enable_standalone(labibb);
		if (rc < 0) {
			pr_err("enable ibb standalone failed, rc=%d\n", rc);
			return rc;
		}
		labibb->ibb_vreg.vreg_enabled = 1;

		rc = qpnp_lab_enable_standalone(labibb);
		if (rc < 0) {
			pr_err("enable lab standalone failed, rc=%d\n", rc);
			return rc;
		}
		labibb->lab_vreg.vreg_enabled = 1;
	}

	return 0;
}

#define SC_ERR_RECOVERY_DELAY_MS	250
#define SC_ERR_COUNT_INTERVAL_SEC	1
#define POLLING_SCP_DONE_COUNT		2
#define POLLING_SCP_DONE_INTERVAL_MS	5
static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb)
{
	int rc;
	u16 reg;
	u8 sc_err_mask, val;
	char *str;
	struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb;
	bool in_sc_err, lab_en, ibb_en, scp_done = false;
	int count;

	if (irq == labibb->lab_vreg.lab_sc_irq) {
		reg = labibb->lab_base + REG_LAB_STATUS1;
		sc_err_mask = LAB_STATUS1_SC_DETECT_BIT;
		str = "LAB";
	} else if (irq == labibb->ibb_vreg.ibb_sc_irq) {
		reg = labibb->ibb_base + REG_IBB_STATUS1;
		sc_err_mask = IBB_STATUS1_SC_DETECT_BIT;
		str = "IBB";
	} else {
		return IRQ_HANDLED;
	}

	rc = qpnp_labibb_read(labibb, reg, &val, 1);
	if (rc < 0) {
		pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
		return IRQ_HANDLED;
	}
	pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val);

	in_sc_err = !!(val & sc_err_mask);

	/*
	 * The SC fault would trigger PBS to disable regulators
	 * for protection. This would cause the SC_DETECT status being
	 * cleared so that it's not able to get the SC fault status.
	 * Check if LAB/IBB regulators are enabled in the driver but
	 * disabled in hardware, this means a SC fault had happened
	 * and SCP handling is completed by PBS.
	 */
	if (!in_sc_err) {
		count = POLLING_SCP_DONE_COUNT;
		do {
			reg = labibb->lab_base + REG_LAB_ENABLE_CTL;
			rc = qpnp_labibb_read(labibb, reg, &val, 1);
			if (rc < 0) {
				pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
				return IRQ_HANDLED;
			}
			lab_en = !!(val & LAB_ENABLE_CTL_EN);

			reg = labibb->ibb_base + REG_IBB_ENABLE_CTL;
			rc = qpnp_labibb_read(labibb, reg, &val, 1);
			if (rc < 0) {
				pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
				return IRQ_HANDLED;
			}
			ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN);
			if (lab_en || ibb_en)
				msleep(POLLING_SCP_DONE_INTERVAL_MS);
			else
				break;
		} while ((lab_en || ibb_en) && count--);

		if (labibb->lab_vreg.vreg_enabled
				&& labibb->ibb_vreg.vreg_enabled
				&& !lab_en && !ibb_en) {
			pr_debug("LAB/IBB has been disabled by SCP\n");
			scp_done = true;
		}
	}

	if (in_sc_err || scp_done) {
		if (hrtimer_active(&labibb->sc_err_check_timer) ||
			hrtimer_callback_running(&labibb->sc_err_check_timer)) {
			labibb->sc_err_count++;
		} else {
			labibb->sc_err_count = 1;
			hrtimer_start(&labibb->sc_err_check_timer,
					ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0),
					HRTIMER_MODE_REL);
		}
		schedule_delayed_work(&labibb->sc_err_recovery_work,
				msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS));
	}

	return IRQ_HANDLED;
}

#define SC_FAULT_COUNT_MAX		4
static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer)
{
	struct qpnp_labibb *labibb = container_of(timer,
			struct qpnp_labibb, sc_err_check_timer);
	/*
	 * if SC fault triggers more than 4 times in 1 second,
	 * then disable the IRQs and leave as it.
	 */
	if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) {
		disable_irq(labibb->lab_vreg.lab_sc_irq);
		disable_irq(labibb->ibb_vreg.ibb_sc_irq);
	}

	return HRTIMER_NORESTART;
}

static void labibb_sc_err_recovery_work(struct work_struct *work)
{
	struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
					sc_err_recovery_work.work);
	int rc;

	labibb->ibb_vreg.vreg_enabled = 0;
	labibb->lab_vreg.vreg_enabled = 0;
	rc = qpnp_labibb_force_enable(labibb);
	if (rc < 0)
		pr_err("force enable labibb failed, rc=%d\n", rc);

}

static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev,
				int min_uV, int max_uV, unsigned int *selector)
{
@@ -2535,7 +2747,7 @@ static int qpnp_skip_swire_command(struct qpnp_labibb *labibb)
			pr_err("Failed to read ibb_status1 reg rc=%d\n", rc);
			return rc;
		}
		if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK)
		if (reg & IBB_STATUS1_VREG_OK_BIT)
			break;

		/* poll delay */
@@ -2869,6 +3081,18 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
		}
	}

	if (labibb->lab_vreg.lab_sc_irq != -EINVAL) {
		rc = devm_request_threaded_irq(labibb->dev,
				labibb->lab_vreg.lab_sc_irq, NULL,
				labibb_sc_err_handler,
				IRQF_ONESHOT | IRQF_TRIGGER_RISING,
				"lab-sc-err", labibb);
		if (rc) {
			pr_err("Failed to register 'lab-sc-err' irq rc=%d\n",
						rc);
			return rc;
		}
	}
	rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY,
				&val, 1);
	if (rc < 0) {
@@ -3338,8 +3562,7 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb,

static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
{
	int rc, delay, retries = 10;
	u8 val;
	int rc = 0;
	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);

	if (labibb->sc_detected) {
@@ -3348,40 +3571,17 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
	}

	if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {

		if (!labibb->standalone)
			return qpnp_labibb_regulator_enable(labibb);

		rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
		if (rc < 0) {
			pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
			return rc;
		}

		delay = labibb->ibb_vreg.soft_start;
		while (retries--) {
			/* Wait for a small period before reading IBB_STATUS1 */
			usleep_range(delay, delay + 100);

			rc = qpnp_labibb_read(labibb, labibb->ibb_base +
					REG_IBB_STATUS1, &val, 1);
		rc = qpnp_ibb_enable_standalone(labibb);
		if (rc < 0) {
				pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n",
					REG_IBB_STATUS1, rc);
			pr_err("enable ibb standalone failed, rc=%d\n", rc);
			return rc;
		}

			if (val & IBB_STATUS1_VREG_OK)
				break;
		}

		if (!(val & IBB_STATUS1_VREG_OK)) {
			pr_err("qpnp_ibb_regulator_enable failed\n");
			return -EINVAL;
		}

		labibb->ibb_vreg.vreg_enabled = 1;
	}

	return 0;
}

@@ -3430,7 +3630,6 @@ static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev,
	return rc;
}


static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev)
{
	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
@@ -3652,6 +3851,19 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
		labibb->ibb_vreg.pwrdn_dly = 0;
	}

	if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) {
		rc = devm_request_threaded_irq(labibb->dev,
				labibb->ibb_vreg.ibb_sc_irq, NULL,
				labibb_sc_err_handler,
				IRQF_ONESHOT | IRQF_TRIGGER_RISING,
				"ibb-sc-err", labibb);
		if (rc) {
			pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n",
						rc);
			return rc;
		}
	}

	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY,
				&val, 1);
	if (rc < 0) {
@@ -3725,15 +3937,39 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
static int qpnp_lab_register_irq(struct device_node *child,
				struct qpnp_labibb *labibb)
{
	int rc = 0;

	if (is_lab_vreg_ok_irq_available(labibb)) {
		labibb->lab_vreg.lab_vreg_ok_irq =
					of_irq_get_byname(child, "lab-vreg-ok");
		if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
		rc = of_irq_get_byname(child, "lab-vreg-ok");
		if (rc < 0) {
			pr_err("Invalid lab-vreg-ok irq\n");
			return -EINVAL;
			return rc;
		}
		labibb->lab_vreg.lab_vreg_ok_irq = rc;
	}

	labibb->lab_vreg.lab_sc_irq = -EINVAL;
	rc = of_irq_get_byname(child, "lab-sc-err");
	if (rc < 0)
		pr_debug("Unable to get lab-sc-err, rc = %d\n", rc);
	else
		labibb->lab_vreg.lab_sc_irq = rc;

	return 0;
}

static int qpnp_ibb_register_irq(struct device_node *child,
				struct qpnp_labibb *labibb)
{
	int rc;

	labibb->ibb_vreg.ibb_sc_irq = -EINVAL;
	rc = of_irq_get_byname(child, "ibb-sc-err");
	if (rc < 0)
		pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc);
	else
		labibb->ibb_vreg.ibb_sc_irq = rc;

	return 0;
}

@@ -3939,6 +4175,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
		case QPNP_IBB_TYPE:
			labibb->ibb_base = base;
			labibb->ibb_dig_major = revision;
			qpnp_ibb_register_irq(child, labibb);
			rc = register_qpnp_ibb_regulator(labibb, child);
			if (rc < 0)
				goto fail_registration;
@@ -3962,6 +4199,11 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
	}

	INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work);
	INIT_DELAYED_WORK(&labibb->sc_err_recovery_work,
			labibb_sc_err_recovery_work);
	hrtimer_init(&labibb->sc_err_check_timer,
			CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	labibb->sc_err_check_timer.function = labibb_check_sc_err_count;
	dev_set_drvdata(&pdev->dev, labibb);
	pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n",
						labibb->lab_vreg.vreg_enabled,