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

Commit 4fb3309b authored by Kiran Gunda's avatar Kiran Gunda
Browse files

qcom: spmi-wled: Add support for OVP interrupt handling



WLED peripheral has over voltage protection(OVP) circuitry and the OVP
fault is notified through an interrupt. Though this fault condition rising
due to an incorrect hardware configuration is mitigated in the hardware,
it still needs to be detected and handled. Add support for it.

When WLED module is enabled, keep OVP fault interrupt disabled for 10 ms to
account for soft start delay.

Change-Id: I2f331b94f7e7e740cebb2ebc420f90c5a3946df7
Signed-off-by: default avatarKiran Gunda <kgunda@codeaurora.org>
parent 122d5a40
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ platforms. The PMIC is connected to the host processor via SPMI bus.
	Usage:      optional
	Value type: <string>
	Definition: Interrupt names associated with the interrupts.
		    Must be "sc-irq".
		    Currently supported interrupts are "sc-irq" and "ovp-irq".

Example:

@@ -102,8 +102,9 @@ qcom-wled@d800 {
	reg-names = "wled-ctrl-base", "wled-sink-base";
	label = "backlight";

	interrupts = <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>;
	interrupt-names = "sc-irq";
	interrupts = <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>,
			<0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
	interrupt-names = "sc-irq", "ovp-irq";
	qcom,fs-current-limit = <25000>;
	qcom,current-boost-limit = <970>;
	qcom,switching-freq = <800>;
+84 −0
Original line number Diff line number Diff line
@@ -29,8 +29,15 @@
#define WLED_DEFAULT_BRIGHTNESS		2048
#define  WLED_MAX_BRIGHTNESS		4095

#define WLED_SOFT_START_DLY_US		10000

/* WLED control registers */
#define WLED_CTRL_FAULT_STATUS		0x08
#define  WLED_CTRL_ILIM_FAULT_BIT	BIT(0)
#define  WLED_CTRL_OVP_FAULT_BIT	BIT(1)
#define  WLED_CTRL_SC_FAULT_BIT		BIT(2)

#define WLED_CTRL_INT_RT_STS		0x10

#define WLED_CTRL_MOD_ENABLE		0x46
#define  WLED_CTRL_MOD_EN_MASK		BIT(7)
@@ -89,6 +96,7 @@ struct wled_config {
	u32 fs_current;
	u32 string_cfg;
	int sc_irq;
	int ovp_irq;
	bool en_cabc;
	bool ext_pfet_sc_pro_en;
};
@@ -105,6 +113,7 @@ struct wled {
	u32 brightness;
	u32 sc_count;
	bool prev_state;
	bool ovp_irq_disabled;
};

static int wled_module_enable(struct wled *wled, int val)
@@ -114,6 +123,28 @@ static int wled_module_enable(struct wled *wled, int val)
	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
			WLED_CTRL_MOD_ENABLE, WLED_CTRL_MOD_EN_MASK,
			val << WLED_CTRL_MODULE_EN_SHIFT);
	if (rc < 0)
		return rc;
	/*
	 * Wait for at least 10ms before enabling OVP fault interrupt after
	 * enabling the module so that soft start is completed. Keep the OVP
	 * interrupt disabled when the module is disabled.
	 */
	if (val) {
		usleep_range(WLED_SOFT_START_DLY_US,
				WLED_SOFT_START_DLY_US + 1000);

		if (wled->cfg.ovp_irq > 0 && wled->ovp_irq_disabled) {
			enable_irq(wled->cfg.ovp_irq);
			wled->ovp_irq_disabled = false;
		}
	} else {
		if (wled->cfg.ovp_irq > 0 && !wled->ovp_irq_disabled) {
			disable_irq(wled->cfg.ovp_irq);
			wled->ovp_irq_disabled = true;
		}
	}

	return rc;
}

@@ -266,12 +297,42 @@ static irqreturn_t wled_sc_irq_handler(int irq, void *_wled)
	return IRQ_HANDLED;
}

static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
{
	struct wled *wled = _wled;
	int rc;
	u32 int_sts, fault_sts;

	rc = regmap_read(wled->regmap,
			wled->ctrl_addr + WLED_CTRL_INT_RT_STS, &int_sts);
	if (rc < 0) {
		pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
		return IRQ_HANDLED;
	}

	rc = regmap_read(wled->regmap, wled->ctrl_addr +
			WLED_CTRL_FAULT_STATUS, &fault_sts);
	if (rc < 0) {
		pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
		return IRQ_HANDLED;
	}

	if (fault_sts &
		(WLED_CTRL_OVP_FAULT_BIT | WLED_CTRL_ILIM_FAULT_BIT))
		pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
			int_sts, fault_sts);

	return IRQ_HANDLED;
}

static int wled_setup(struct wled *wled)
{
	int rc, temp, i;
	u8 sink_en = 0;
	u32 val;
	u8 string_cfg = wled->cfg.string_cfg;
	int sc_irq = wled->cfg.sc_irq;
	int ovp_irq = wled->cfg.ovp_irq;

	rc = regmap_update_bits(wled->regmap,
			wled->ctrl_addr + WLED_CTRL_OVP,
@@ -369,6 +430,25 @@ static int wled_setup(struct wled *wled)
			return rc;
	}

	if (ovp_irq >= 0) {
		rc = devm_request_threaded_irq(&wled->pdev->dev, ovp_irq,
				NULL, wled_ovp_irq_handler, IRQF_ONESHOT,
				"wled_ovp_irq", wled);
		if (rc < 0) {
			dev_err(&wled->pdev->dev, "Unable to request ovp(%d) IRQ(err:%d)\n",
				ovp_irq, rc);
			return rc;
		}

		rc = regmap_read(wled->regmap, wled->ctrl_addr +
				WLED_CTRL_MOD_ENABLE, &val);
		/* disable the OVP irq only if the module is not enabled */
		if (!rc && !(val & WLED_CTRL_MOD_EN_MASK)) {
			disable_irq(ovp_irq);
			wled->ovp_irq_disabled = true;
		}
	}

	return 0;
}

@@ -541,6 +621,10 @@ static int wled_configure(struct wled *wled, struct device *dev)
	if (wled->cfg.sc_irq < 0)
		dev_dbg(&wled->pdev->dev, "sc irq is not used\n");

	wled->cfg.ovp_irq = platform_get_irq_byname(wled->pdev, "ovp-irq");
	if (wled->cfg.ovp_irq < 0)
		dev_dbg(&wled->pdev->dev, "ovp irq is not used\n");

	return 0;
}