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

Commit f95b5190 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ARM: dts: msm: Specify SC interrupts for LAB/IBB in pmi8998"

parents 269da531 1b18856f
Loading
Loading
Loading
Loading
+17 −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.
@@ -153,6 +153,10 @@ LAB subnode optional properties:
					any value in the allowed limit.
- qcom,notify-lab-vreg-ok-sts:		A boolean property which upon set will
					poll and notify the lab_vreg_ok status.
- qcom,qpnp-lab-sc-wait-time-ms:	This property is used to specify the time
					(in ms) to poll for the short circuit
					detection. If not specified the default time
					is 5 sec.

Following properties are available only for PM660A:

@@ -209,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.
+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,11 @@ Required Node Structure
	Value type: <string>
	Definition: should be "qcom,qpnp-oledb-regulator".

- qcom,pmic-revid
	Usage:      required
	Value type: <phandle>
	Definition: Used to identify the PMIC subtype.

- reg
	Usage:      required
	Value type: <prop-encoded-array>
@@ -224,6 +229,7 @@ pm660a_oledb: qpnp-oledb@e000 {
	compatible = "qcom,qpnp-oledb-regulator";
	#address-cells = <1>;
	#size-cells = <1>;
	qcom,pmic-revid = <&pm660l_revid>;
	reg = <0xe000 0x100>;

	label = "oledb";
+8 −1
Original line number Diff line number Diff line
@@ -483,6 +483,10 @@
				regulator-min-microvolt = <4600000>;
				regulator-max-microvolt = <6000000>;

				interrupts = <0x3 0xdc 0x2
						IRQ_TYPE_EDGE_RISING>;
				interrupt-names = "ibb-sc-err";

				qcom,qpnp-ibb-min-voltage = <1400000>;
				qcom,qpnp-ibb-step-size = <100000>;
				qcom,qpnp-ibb-slew-rate = <2000000>;
@@ -516,8 +520,11 @@
				regulator-max-microvolt = <6000000>;

				interrupts = <0x3 0xde 0x0
						IRQ_TYPE_EDGE_RISING>,
					     <0x3 0xde 0x1
						IRQ_TYPE_EDGE_RISING>;
				interrupt-names = "lab-vreg-ok";
				interrupt-names = "lab-vreg-ok", "lab-sc-err";

				qcom,qpnp-lab-min-voltage = <4600000>;
				qcom,qpnp-lab-step-size = <100000>;
				qcom,qpnp-lab-slew-rate = <5000>;
+356 −71
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,12 +559,15 @@ struct lab_regulator {
	struct mutex			lab_mutex;

	int				lab_vreg_ok_irq;
	int				lab_sc_irq;

	int				curr_volt;
	int				min_volt;

	int				step_size;
	int				slew_rate;
	int				soft_start;
	int				sc_wait_time_ms;

	int				vreg_enabled;
};
@@ -572,6 +577,8 @@ struct ibb_regulator {
	struct regulator_dev		*rdev;
	struct mutex			ibb_mutex;

	int				ibb_sc_irq;

	int				curr_volt;
	int				min_volt;

@@ -602,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;
@@ -612,6 +622,8 @@ struct qpnp_labibb {
	bool				skip_2nd_swire_cmd;
	bool				pfm_enable;
	bool				notify_lab_vreg_ok_sts;
	bool				detect_lab_sc;
	bool				sc_detected;
	u32				swire_2nd_cmd_delay;
	u32				swire_ibb_ps_enable_delay;
};
@@ -2178,8 +2190,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
	u8 val;
	struct qpnp_labibb *labibb  = container_of(work, struct qpnp_labibb,
							lab_vreg_ok_work);
	if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL)
		retries = labibb->lab_vreg.sc_wait_time_ms / 5;

	while (retries--) {
	while (retries) {
		rc = qpnp_labibb_read(labibb, labibb->lab_base +
					REG_LAB_STATUS1, &val, 1);
		if (rc < 0) {
@@ -2188,18 +2202,106 @@ 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;
		}

		usleep_range(dly, dly + 100);
		retries--;
	}

	if (!retries)
	if (!retries) {
		if (labibb->detect_lab_sc) {
			pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n");
			/* Disable LAB module */
			val = 0;
			rc = qpnp_labibb_write(labibb, labibb->lab_base +
					REG_LAB_MODULE_RDY, &val, 1);
			if (rc < 0) {
				pr_err("write register %x failed rc = %d\n",
					REG_LAB_MODULE_RDY, rc);
				return;
			}
			raw_notifier_call_chain(&labibb_notifier,
						LAB_VREG_NOT_OK, NULL);
			labibb->sc_detected = true;
			labibb->lab_vreg.vreg_enabled = 0;
			labibb->ibb_vreg.vreg_enabled = 0;
		} else {
			pr_err("LAB_VREG_OK not set, failed to notify\n");
		}
	}
}

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)
{
@@ -2242,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;
	}
@@ -2259,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;
		}
@@ -2330,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;
		}
@@ -2359,10 +2461,13 @@ 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) {
		pr_info("Short circuit detected: disabled LAB/IBB rails\n");
		return 0;
	}

	if (labibb->skip_2nd_swire_cmd) {
		rc = qpnp_ibb_ps_config(labibb, false);
		if (rc < 0) {
@@ -2372,38 +2477,18 @@ 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;
	}

	if (labibb->notify_lab_vreg_ok_sts)
	if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc)
		schedule_work(&labibb->lab_vreg_ok_work);

	return 0;
@@ -2444,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)
{
@@ -2505,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 */
@@ -2661,6 +2903,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
	labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node,
					"qcom,notify-lab-vreg-ok-sts");

	labibb->lab_vreg.sc_wait_time_ms = -EINVAL;
	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
					labibb->detect_lab_sc)
		of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms",
					&labibb->lab_vreg.sc_wait_time_ms);

	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
					&(labibb->lab_vreg.soft_start));
	if (!rc) {
@@ -2833,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) {
@@ -3302,45 +3562,26 @@ 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->ibb_vreg.vreg_enabled && !labibb->swire_control) {
	if (labibb->sc_detected) {
		pr_info("Short circuit detected: disabled LAB/IBB rails\n");
		return 0;
	}

	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);
		rc = qpnp_ibb_enable_standalone(labibb);
		if (rc < 0) {
			pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
			pr_err("enable ibb standalone failed, 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("qpnp_ibb_regulator_enable read register %x failed rc = %d\n",
					REG_IBB_STATUS1, 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;
}

@@ -3389,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);
@@ -3611,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) {
@@ -3684,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;
}

@@ -3788,6 +4065,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)

	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
		labibb->mode = QPNP_LABIBB_AMOLED_MODE;
		/* Enable polling for LAB short circuit detection for PM660A */
		labibb->detect_lab_sc = true;
	} else {
		rc = of_property_read_string(labibb->dev->of_node,
				"qcom,qpnp-labibb-mode", &mode_name);
@@ -3896,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;
@@ -3919,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,
+46 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/qpnp-labibb-regulator.h>
#include <linux/qpnp/qpnp-pbs.h>
#include <linux/qpnp/qpnp-revid.h>

#define QPNP_OLEDB_REGULATOR_DRIVER_NAME	"qcom,qpnp-oledb-regulator"
#define OLEDB_VOUT_STEP_MV				100
@@ -162,6 +163,7 @@ struct qpnp_oledb {
	struct notifier_block			oledb_nb;
	struct mutex				bus_lock;
	struct device_node			*pbs_dev_node;
	struct pmic_revid_data			*pmic_rev_id;

	u32					base;
	u8					mod_enable;
@@ -181,6 +183,8 @@ struct qpnp_oledb {
	bool					dynamic_ext_pinctl_config;
	bool					pbs_control;
	bool					force_pd_control;
	bool					handle_lab_sc_notification;
	bool					lab_sc_detected;
};

static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)

	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);

	if (oledb->lab_sc_detected == true) {
		pr_info("Short circuit detected: Disabled OLEDB rail\n");
		return 0;
	}

	if (oledb->ext_pin_control) {
		rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
								 &val, 1);
@@ -1085,8 +1094,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb)
static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
{
	int rc = 0;
	struct device_node *revid_dev_node;
	struct device_node *of_node = oledb->dev->of_node;

	revid_dev_node = of_parse_phandle(oledb->dev->of_node,
					"qcom,pmic-revid", 0);
	if (!revid_dev_node) {
		pr_err("Missing qcom,pmic-revid property - driver failed\n");
		return -EINVAL;
	}

	oledb->pmic_rev_id = get_revid_data(revid_dev_node);
	if (IS_ERR(oledb->pmic_rev_id)) {
		pr_debug("Unable to get revid data\n");
		return -EPROBE_DEFER;
	}

	oledb->swire_control =
			of_property_read_bool(of_node, "qcom,swire-control");

@@ -1227,15 +1250,32 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb,
					unsigned long action, void *data)
{
	int rc = 0;
	u8 val;
	struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
								oledb_nb);

	if (action == LAB_VREG_NOT_OK) {
		/* short circuit detected. Disable OLEDB module */
		val = 0;
		rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY,
					&val, 1);
		if (rc < 0) {
			pr_err("Failed to write MODULE_RDY rc=%d\n", rc);
			return NOTIFY_STOP;
		}
		oledb->lab_sc_detected = true;
		oledb->mod_enable = false;
		pr_crit("LAB SC detected, disabling OLEDB forever!\n");
	}

	if (action == LAB_VREG_OK) {
		/* Disable SWIRE pull down control and enable via spmi mode */
		rc = qpnp_oledb_force_pulldown_config(oledb);
		if (rc < 0)
		if (rc < 0) {
			pr_err("Failed to config force pull down\n");
			return NOTIFY_STOP;
		}
	}

	return NOTIFY_OK;
}
@@ -1281,7 +1321,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
		return rc;
	}

	if (oledb->force_pd_control) {
	/* Enable LAB short circuit notification support */
	if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
		oledb->handle_lab_sc_notification = true;

	if (oledb->force_pd_control || oledb->handle_lab_sc_notification) {
		oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
		rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
		if (rc < 0) {
Loading